ch.eiafr.cojac.FloatReplaceClassVisitor.java Source code

Java tutorial

Introduction

Here is the source code for ch.eiafr.cojac.FloatReplaceClassVisitor.java

Source

/*
 * *
 *    Copyright 2011-2014 Baptiste Wicht, Frdric Bapst & Romain Monnard
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 *
 */

package ch.eiafr.cojac;

import static ch.eiafr.cojac.models.FloatReplacerClasses.*;
import static ch.eiafr.cojac.instrumenters.InvokableMethod.replaceFloatMethodDescription;

import ch.eiafr.cojac.instrumenters.FloatProxyMethod;
import ch.eiafr.cojac.instrumenters.ReplaceFloatsMethods;

import java.io.PrintWriter;
import java.util.ArrayList;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.AnalyzerAdapter;
import org.objectweb.asm.commons.LocalVariablesSorter;
import org.objectweb.asm.util.Printer;
import org.objectweb.asm.util.ASMifier;
import org.objectweb.asm.util.TraceMethodVisitor;

public final class FloatReplaceClassVisitor extends CojacClassVisitor {
    private static final String DUMP_CLASS = null; // "" or "Dump"
    private static final String DUMP_METHOD = "instanceMethod"; // "Dump"

    private final ArrayList<String> proxyMethods;
    private FloatProxyMethod fpm;

    FloatReplaceClassVisitor(ClassVisitor cv, CojacReferences references, CojacAnnotationVisitor cav) {
        super(cv, references, cav);
        proxyMethods = new ArrayList<>();
    }

    @Override
    public void visit(int version, int access, String name, String signature, String supername,
            String[] interfaces) {
        super.visit(version, access, name, signature, supername, interfaces);
        fpm = new FloatProxyMethod(this, crtClassName);
    }

    public boolean isProxyMethod(String name, String desc) {
        return proxyMethods.contains(name + "_" + desc);
    }

    public MethodVisitor addProxyMethod(int access, String name, String desc, String signature,
            String[] exceptions) {
        proxyMethods.add(name + "_" + desc);
        return cv.visitMethod(access, name, desc, signature, exceptions);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        String oldDesc = desc;
        desc = replaceFloatMethodDescription(desc);
        boolean isNative = (access & Opcodes.ACC_NATIVE) > 0;
        //boolean isAbstrac = (access & Opcodes.ACC_ABSTRACT) > 0;
        //boolean isInterface = (access & Opcodes.ACC_INTERFACE) > 0;   
        if (isNative && desc.equals(oldDesc) == false) {
            /*  If the native method has not the same descriptor, create a 
                 method to transform types and call the good native method. */
            cv.visitMethod(access, name, oldDesc, signature, exceptions);
            fpm.nativeCall(null, access, crtClassName, name, oldDesc);
            return null;
        }
        MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
        String currentMethodID = crtClassName + '/' + name;
        if (cav.isClassAnnotated() || cav.isMethodAnnotated(currentMethodID)) {
            return mv;
        }
        return instrumentMethod(mv, access, desc, oldDesc, name);
    }

    private MethodVisitor instrumentMethod(MethodVisitor parentMv, int access, String desc, String oldDesc,
            String name) {
        MethodVisitor mv;
        // Create the MethodVisitor delegation chain: 
        // MyLocalAdder                    adds two local variables
        //  -> FloatReplacerMethodVisitor  main bytecode transformation (with helper class FloatProxyMethod)
        //   -> FloatVariableSorter        remaps parameters index (due to the replacement of double (2 slots) as objects (1 slot))
        //   -> AnalyzerAdapter            keeps track of effective stack, so that we know the type of the top
        //    -> parentMv                  typically the final Writer
        // the last one (parentMv) is typically a MethodWriter
        // With maybe some intermediate TraceMethodVisitor to help debugging...

        if (wantsToDump(crtClassName, name)) { // name.contains("dumpMethod")) { // "dumpMethod"
            parentMv = new TraceMethodVisitor(parentMv, newPrinter("FINAL  " + name + desc));
        }
        AnalyzerAdapter aa = new AnalyzerAdapter(crtClassName, access, name, desc, parentMv);
        FloatVariablesSorter lvs = new FloatVariablesSorter(access, oldDesc, aa);
        MethodVisitor aux = lvs;
        if (wantsToDump(crtClassName, name)) {
            aux = new TraceMethodVisitor(lvs, newPrinter("JUST BEFORE LVS " + name + desc));
        }
        ReplaceFloatsMethods rfm = new ReplaceFloatsMethods(fpm, crtClassName, references);
        FloatReplacerMethodVisitor frmv = new FloatReplacerMethodVisitor(aa, aux, rfm, stats, factory, references);
        MyLocalAdder mla = new MyLocalAdder(access, oldDesc, frmv);
        fpm.setUsefulPartners(aa, mla);
        mv = mla;
        if (wantsToDump(crtClassName, name))
            mv = new TraceMethodVisitor(mv, newPrinter("BEFORE " + name + desc));
        return mv;
    }

    private boolean wantsToDump(String className, String methName) {
        if (DUMP_CLASS == null || DUMP_METHOD == null)
            return false;
        return className.contains(DUMP_CLASS) && methName.contains(DUMP_METHOD);
    }

    private Printer newPrinter(final String s) {
        Printer printer = new ASMifier(Opcodes.ASM5, "mv", 0) { // or Textifier(ASM5)
            @Override
            public void visitMethodEnd() {
                System.out.println("======================== " + s + " ==================");
                PrintWriter p = new PrintWriter(System.out);
                print(p);
                p.flush(); // don't forget that!
            }
        };
        return printer;
    }

    @Override
    public FieldVisitor visitField(int accessFlags, String fieldName, String fieldType, String genericSignature,
            Object initValStatic) {
        if (fieldType.equals("F")) {
            return super.visitField(accessFlags, fieldName, COJAC_FLOAT_WRAPPER_TYPE_DESCR, genericSignature, null);
        } else if (fieldType.equals("D")) {
            return super.visitField(accessFlags, fieldName, COJAC_DOUBLE_WRAPPER_TYPE_DESCR, genericSignature,
                    null);
        } else if (fieldType.equals(Type.getType(Float.class).getDescriptor())) {
            return super.visitField(accessFlags, fieldName, COJAC_FLOAT_WRAPPER_TYPE_DESCR, genericSignature, null);
        } else if (fieldType.equals(Type.getType(Double.class).getDescriptor())) {
            return super.visitField(accessFlags, fieldName, COJAC_DOUBLE_WRAPPER_TYPE_DESCR, genericSignature,
                    null);
        }

        Type type = Type.getType(fieldType);
        if (type.getSort() == Type.ARRAY) {
            if (type.getElementType().equals(Type.FLOAT_TYPE)) {
                String desc = arrayDescriptor(type.getDimensions(), COJAC_FLOAT_WRAPPER_TYPE_DESCR);
                return super.visitField(accessFlags, fieldName, desc, genericSignature, null);
            } else if (type.getElementType().equals(Type.DOUBLE_TYPE)) {
                String desc = arrayDescriptor(type.getDimensions(), COJAC_DOUBLE_WRAPPER_TYPE_DESCR);
                return super.visitField(accessFlags, fieldName, desc, genericSignature, null);
            }
        }
        return super.visitField(accessFlags, fieldName, fieldType, genericSignature, initValStatic);
    }

    static String arrayDescriptor(int dimensions, String eltType) {
        String desc = "";
        for (int i = 0; i < dimensions; i++) {
            desc += "[";
        }
        desc += eltType;
        return desc;
    }

    //    private FieldVisitor instrumentField(FieldVisitor parentFv, int arg0, String arg1, String arg2, String arg3, Object arg4) {
    //        FieldVisitor fv = new CojacFieldVisitor(parentFv, arg0, arg1, arg2, arg3, arg4);
    //        fv.visitEnd();
    //        return fv;
    //    }

    //========================================================================
    public static class MyLocalAdder extends LocalVariablesSorter {
        private static final Type OBJ_ARRAY_TYPE = Type.getType("[Ljava/lang/Object;");
        private static final Type OBJ_TYPE = Type.getType(Object.class);

        private int paramArrayVar = -1;
        private int targetVar = -1;

        protected MyLocalAdder(int access, String desc, MethodVisitor mv) {
            super(Opcodes.ASM5, access, desc, mv);
        }

        public int paramArrayVar() {
            if (paramArrayVar < 0)
                paramArrayVar = newLocal(OBJ_ARRAY_TYPE);
            return paramArrayVar;
        }

        public int targetVar() {
            if (targetVar < 0)
                targetVar = newLocal(OBJ_TYPE);
            return targetVar;
        }
    }
    //========================================================================

}