Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/347.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java ASM在字节码方法内联期间重新映射变量_Java_Inline_Bytecode_Java Bytecode Asm_Bytecode Manipulation - Fatal编程技术网

Java ASM在字节码方法内联期间重新映射变量

Java ASM在字节码方法内联期间重新映射变量,java,inline,bytecode,java-bytecode-asm,bytecode-manipulation,Java,Inline,Bytecode,Java Bytecode Asm,Bytecode Manipulation,我正在使用ASM进行在线字节码方法内联优化。我的更改基于示例3.2.6内联方法()。测试示例(调用方::test处内联被调用方的计算(int,int)是: 基于ASM 5.0版本,我的代码是: //MainInliner.java public class MainInliner extends ClassLoader{ public byte[] generator(String caller, String callee) throws ClassNotFoundException

我正在使用ASM进行在线字节码方法内联优化。我的更改基于示例
3.2.6内联方法
()。测试示例(调用方::test处内联被调用方的计算(int,int)是:

基于ASM 5.0版本,我的代码是:

//MainInliner.java
public class MainInliner extends ClassLoader{

    public byte[] generator(String caller, String callee) throws ClassNotFoundException{
        String resource = callee.replace('.', '/') + ".class";
        InputStream is =  getResourceAsStream(resource);
        byte[] buffer;
        // adapts the class on the fly
        try {
            resource = caller.replace('.', '/')+".class";
            is = getResourceAsStream(resource);
            ClassReader cr = new ClassReader(is);
            ClassWriter cw = new ClassWriter(0);

            ClassVisitor visitor = new BCMerge(Opcodes.ASM5, cw, callee);
            cr.accept(visitor, 0);

            buffer= cw.toByteArray();

        } catch (Exception e) {
            throw new ClassNotFoundException(caller, e);
        }

        // optional: stores the adapted class on disk
        try {
            FileOutputStream fos = new FileOutputStream("/tmp/data.class");
            fos.write(buffer);
            fos.close();
        } catch (IOException e) {}
        return buffer;
     }


      @Override
      protected synchronized Class<?> loadClass(final String name,
                final boolean resolve) throws ClassNotFoundException {
            if (name.startsWith("java.")) {
                System.err.println("Adapt: loading class '" + name
                        + "' without on the fly adaptation");
                return super.loadClass(name, resolve);
            } else {
                System.err.println("Adapt: loading class '" + name
                        + "' with on the fly adaptation");
            }
            String caller = "code.sxu.asm.example.Caller";
            String callee = "code.sxu.asm.example.Callee";
            byte[] b = generator(caller, callee);
            // returns the adapted class
            return defineClass(caller, b, 0, b.length);
        }

        public static void main(final String args[]) throws Exception {
            // loads the application class (in args[0]) with an Adapt class loader
            ClassLoader loader = new MainInliner();
            Class<?> c = loader.loadClass(args[0]);
            Method m = c.getMethod("main", new Class<?>[] { String[].class });

        }
}


class BCMerge extends ClassVisitor{

    String _callee;
    String _caller;

    public BCMerge(int api, ClassVisitor cv, String callee) {
        super(api, cv);
        // TODO Auto-generated constructor stub
        _callee = callee.replace('.', '/');
    }

    public void visit(int version, int access, String name, String signature,
            String superName, String[] interfaces) {
        super.visit(version, access, name, signature, superName, interfaces);
        this._caller = name;
    } 

    public MethodVisitor visitMethod(int access, String name, String desc,
            String signature, String[] exceptions) {

        if(!name.equals("test")){
            return super.visitMethod(access, name, desc, signature, exceptions);
        }
        MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);

        ClassReader cr = null;
        try {
            cr = new ClassReader(this.getClass().getClassLoader().getResourceAsStream(_callee.replace('.', '/')+".class"));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

         ClassNode classNode = new ClassNode();
         cr.accept(classNode, 0);

         MethodNode inlinedMethod = null;
         for(MethodNode node: classNode.methods){
            if(node.name.equals("calculate")){
                inlinedMethod = node;
                break;
            }
        }

        return new MethodCallInliner(access, desc, mv, inlinedMethod, _callee, _caller  );
    }
}

//MethodCallInliner.java
public class MethodCallInliner extends LocalVariablesSorter {

    private final String oldClass;
    private final String newClass;
    private final MethodNode mn;  //Method Visitor wrappers the mv.
    private List blocks = new ArrayList();
    private boolean inlining;

    public MethodCallInliner(int access, String desc, MethodVisitor mv, MethodNode mn, 
            String oldClass, String newClass){
        super(Opcodes.ASM5, access, desc, mv);
        this.oldClass = oldClass;
        this.newClass =  newClass;
        this.mn = mn;
        inlining = false;
    }

    public void visitMethodInsn(int opcode, String owner, String name,
            String desc, boolean itf) {

        System.out.println("opcode:" + opcode + " owner:" + owner + " name:"
                + name + " desc:" + desc);
        if (!canBeInlined(owner, name, desc)) {
            mv.visitMethodInsn(opcode, owner, name, desc, itf);
            return;
        }
        //if it is INVOKEVIRTUAL  ../Callee::calculate(II), then..
        Remapper remapper = new SimpleRemapper(oldClass, newClass);
        Label end = new Label();
        inlining = true;
        mn.instructions.resetLabels();
        mn.accept(new InliningAdapter(this,opcode == Opcodes.INVOKESTATIC ? Opcodes.ACC_STATIC : 0, desc,remapper, end));
        inlining = false;
        super.visitLabel(end);
    }

    private boolean canBeInlined(String owner, String name, String decs){
            if(name.equals("calculate") && owner.equals("code/sxu/asm/example/Callee")){
            return true;
        }
        return false;
    }
}

//InliningAdapter.java
public class InliningAdapter extends RemappingMethodAdapter {

    private final LocalVariablesSorter lvs;
    private final Label end;

    public InliningAdapter(LocalVariablesSorter mv,
            int acc, String desc,Remapper remapper, Label end) {
        super(acc, desc, mv, remapper);
        this.lvs = mv;
        this.end = end;

//      int offset = (acc & Opcodes.ACC_STATIC)!=0 ?0 : 1;
//      Type[] args = Type.getArgumentTypes(desc);
//      for (int i = args.length-1; i >= 0; i--) {
//          super.visitVarInsn(args[i].getOpcode(
//                  Opcodes.ISTORE), i + offset);
//      }
//      if(offset>0) {
//          super.visitVarInsn(Opcodes.ASTORE, 0);
//      }
    }

    public void visitInsn(int opcode) {
        if(opcode==Opcodes.RETURN || opcode == Opcodes.IRETURN) {
            super.visitJumpInsn(Opcodes.GOTO, end);
        } else {
            super.visitInsn(opcode);
        }
    }

    public void visitMaxs(int stack, int locals) {
        System.out.println("visit maxs: "+stack+"  "+locals);
    }

    protected int newLocalMapping(Type type) {
        return lvs.newLocal(type);
    }
}
data.class上的javap结果显示Callee::calculate的主体已插入到正确的位置(Caller::test line::15)。然而,有两个主要问题:

  • invokevirtual之前的前三个堆栈对象

    被调用方::计算(第15行) 9:aload_0
    10:getfield#14//Field(被调用方):Lcode/sxu/asm/example/callee; 13:iload_1
    14:iload_2

    内联后不应在堆栈上

  • 复制的正文(Callee::calculate())中的变量编号0应映射到正确的编号

  • 变量编号不正确。首先,data.class(从第15行到第42行)中复制的Callee::calculate主体的变量号应该从5开始(而不是0)。其次,Callee::calculate()之后的变量号应按照以下规则重新编号:a)如果介于(0,4)之间,则不更改;b)如果与Callee::calculate()复制的正文中的数字冲突,则重新编号

我检查了基类
LocalVariablesSorter
的实现。问题似乎在于其结构:

protected LocalVariablesSorter(final int api, final int access,
            final String desc, final MethodVisitor mv) {
        super(api, mv);
        Type[] args = Type.getArgumentTypes(desc);
        nextLocal = (Opcodes.ACC_STATIC & access) == 0 ? 1 : 0;
        for (int i = 0; i < args.length; i++) {
            nextLocal += args[i].getSize();
        }
        firstLocal = nextLocal; 
    }
    private int[] mapping = new int[40];

Update1:取消InliningAdapter的构造函数注释后的data.class:

     0: iload_1       
     1: istore_3      
     2: iload_2       
     3: istore        4
     5: iload_3       
     6: iload         4
     8: iadd          
     9: aload_0       
    10: getfield      #14                 // Field _callee:Lcode/sxu/asm/example/Callee;
    13: iload_1       
    14: iload_2       
    15: istore_2        //should be istore 5
    16: istore_1        //should be istore 6
    17: astore_0        //should be astore 7
    18: aload_0        
    19: getfield      #40                 // Field _a:Ljava/lang/String;
    22: invokevirtual #46                 // Method java/lang/String.length:()I
    25: aload_0       
    26: getfield      #49                 // Field _b:Ljava/lang/String;
    29: invokevirtual #46                 // Method java/lang/String.length:()I
    32: iadd          
    33: istore        6
    35: iload         6
    37: iload_1       
    38: iload_2       
    39: iadd          
    40: iadd          
    41: istore        6
    43: iload         6
    45: goto          48
    48: isub          
    49: istore        6
    51: iload_3       
    52: iload         4
    54: isub          
    55: istore        7
    57: getstatic     #59 
新存储的三个变量(15,16,17)应编号为5,6,7,而不是2,1,0,内联代码中
*store/*load
中的映射应如下

       0 => 7
       1 => 6 
       2 => 5
       3 => 8
       4 => 9  ...
这些映射应该位于数组中:
LocalVariablesSorter::mapping
,该数组由
LocalVariablesSorter::remap()方法更新。但是,我似乎不可能将它们插入到
mapping
数组中

应进行两种类型的重新映射:

  • 重新映射内联代码(从第18行到第45行)和变量的内部 索引从5开始。最大索引是k
  • 内联代码后重新映射(从第46行到末尾),如果原始索引大于5,则应重新映射任何变量索引(新索引从k+1开始)

正如@Holger已经建议的那样,首先取消对IningAdapter
行的注释

  • 为了解决您列出的主要问题:
    LocalVariablesSorter
    (由
    InliningAdapter
    扩展)认为参数已经存储在固定位置的局部变量中-这确实是输入方法时的正常情况。所以它根本不映射这些(请参见
    LocalVariablesSorter.remap()中的第一行-
    firstLocal
    在构造函数中计算)。然而,在这种情况下,我们得到的却是堆栈上的参数,需要手动分配局部变量。解决方案是告诉
    LocalVariablesSorter
    本地变量中没有存储参数(使
    firstLocal=0
    )。然后,它会将对它们的任何引用视为新变量,并为它们分配新的局部变量。这可以通过愚弄
    LocalVariablesSorter
    来实现,认为没有参数,方法是静态的(即使它实际上不是)。因此,我们将InliningAdapter中的第一行从

        super(acc, desc, mv, remapper);
    

    现在变量0,1,2,。。。重新映射到5,6,7,。。。或者类似的(不管它们是什么,调用方的
    LocalVariablesSorter
    (即
    MethodCallInliner
    实例)负责分配它们)

  • 还有一个问题是,通过使用
    InlineAdapter
    扩展
    RemappingMethodAdaptor
    ,将
    被调用方
    类映射到
    调用方
    ——但是我想您希望将
    a
    \u b
    变量存储在
    被调用方
    实例中

    • 如果我的猜测是正确的,那么您实际上不应该将引用从
      被调用方
      重新映射到
      调用方
      。您只需将
      InliningAdapter
      extend
      localvariablessorter
      改为并去掉重新映射即可
    • 如果我的猜测不正确,那么我猜您可能需要将
      Callee
      的变量也嵌入
      Caller
      中,在这种情况下,您应该保留现有的
      RemappingMethodAdaptor
  • 调试内联代码时,
    被调用方
    的行号没有意义,因为代码内联到
    调用方
    类中。因此,
    Caller
    中的所有行号可能都应该替换为发生内联调用的
    Caller
    中的行号。不幸的是,在Java中,您不能逐行指定不同的源代码文件(例如,您可以在C中这样做)。因此,您可以使用类似这样的方法覆盖
    InliningAdapter
    中的
    visitLineNumber()
    InliningAdapter
    将被传递给
    InliningAdapter
    的构造函数):

    。。或者干脆跳过超级电话,我不是100%确定


  • 您的代码缺少您注释掉的部分。这是从堆栈中弹出参数并将它们放入与参数和内联代码的接收者匹配的局部变量的部分。如果不这样做,堆栈上将出现悬空值,并且
    this
    的局部变量与内联方法的参数不匹配,正如您所描述的。感谢@Holger,您是正确的,这些代码在开始时由于变量索引错误而被注释掉。有关问题解释,请参阅本文末尾的update1:无法在
    LocalVariablesSorter::rmapping
    arrayAct上保留映射
         0: iload_1       
         1: istore_3      
         2: iload_2       
         3: istore        4
         5: iload_3       
         6: iload         4
         8: iadd          
         9: aload_0       
        10: getfield      #14                 // Field _callee:Lcode/sxu/asm/example/Callee;
        13: iload_1       
        14: iload_2       
        15: istore_2        //should be istore 5
        16: istore_1        //should be istore 6
        17: astore_0        //should be astore 7
        18: aload_0        
        19: getfield      #40                 // Field _a:Ljava/lang/String;
        22: invokevirtual #46                 // Method java/lang/String.length:()I
        25: aload_0       
        26: getfield      #49                 // Field _b:Ljava/lang/String;
        29: invokevirtual #46                 // Method java/lang/String.length:()I
        32: iadd          
        33: istore        6
        35: iload         6
        37: iload_1       
        38: iload_2       
        39: iadd          
        40: iadd          
        41: istore        6
        43: iload         6
        45: goto          48
        48: isub          
        49: istore        6
        51: iload_3       
        52: iload         4
        54: isub          
        55: istore        7
        57: getstatic     #59 
    
           0 => 7
           1 => 6 
           2 => 5
           3 => 8
           4 => 9  ...
    
        super(acc, desc, mv, remapper);
    
        super(acc | Opcodes.ACC_STATIC, "()V", mv, remapper);
    
    @Override
    public void visitLineNumber(int line, Label start) {
        super.visitLineNumber(inlinedLine, start);
    }