在内联方法体中invokevirtual的意外指令和参数

我遵循http://asm.ow2.org/current/asm-transformations.pdf中的“3.2.6 Inline Method”中的示例代码,将MethodNode内联到一个调用站点。

我的问题是内联后生成的字节码中出现了一些意外的指令(这些字节码与我的代码不一致),并且问题仅在ifeq在内联方法体之后存在并且xLoad加载堆栈上的variables时才存在。

我还没有find问题的根源。 现在我开始删除所有不明智的代码,旨在用最less的代码重现它。 任何人有好的build议,欢迎。

下面是我现有的创始人之一:问题是不相关的框架,因为问题仍然存在当configurationClassRewiter是COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS和configurationClassReader ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES

为了简化问题,被调用者的身体是:

 public invokeExact(Ljava/lang/String;)Z      ICONST_0          IRETURN 

来电者是:

 public String invokeExact(String a, String b){       boolean flag = _guard.invokeExact(a);       if(flag)       {         return a;       }       return b;     } 

。 MethodWriter调用者的相应的字节码操作跟踪是:

 public java.lang.String invokeExact(java.lang.String, java.lang.String)      ....        4: aload_1             5: astore_3            6: astore       4        8: iconst_0            visitJumpInsn goto   L1029004533         //visitmax() empty implementation.         //visitEnd() Empty implementation.         visitlabel   L1029004533  // This label is newly created once inlining starts, but is visited until the end of inlining as the target of all xReturn instructions in the Callee's method body.      visitVarInsn istore 5      visitVarInsn iload 5      visitJumpInsn ifeq L980604133      visitVarInsn  aload 1      visitInsn       areturn      visitLabel     L980604133      visitVarInsn  aload 2      visitInsn       areturn 

最后,生成的类文件是:

 public java.lang.String invokeExact(java.lang.String, java.lang.String);   stack=2, locals=6, args_size=3        0: aload_0             1: getfield     #17                // Field _guard:Ltest/code/jit/asm/simple/MHGuard;        4: aload_1             5: astore_3            6: astore       4        8: iconst_0            **9: goto         9       12: fconst_0           13: iconst_2**           14: iload        5       16: ifeq         21       19: aload_1            20: areturn            21: aload_2            22: areturn          StackMapTable: number_of_entries = 2          frame_type = 255 /* full_frame */         offset_delta = 12         locals = [ class test/code/jit/asm/simple/GWTSample, class java/lang/String, class java/lang/String, class java/lang/String, class test/code/jit/asm/simple/MHGuard ]         stack = [ int ]          frame_type = 252 /* append */            offset_delta = 8       locals = [ int ] 

#9,#12和#13是错误的。


我的部分代码是(我将在周末继续简化我的代码):

 public class MethodCallInliner extends LocalVariablesSorter { protected MethodContext _context; private IPlugin _plugin; public MethodCallInliner(int access, String desc, MethodContext context){ // context.getRawMV() return a Class MethodWriter. super(Opcodes.ASM5, access, desc, context.getRawMV()); _context = context; //_fieldVisitor = new FieldManipulationVisitor(mv, context); _plugin = NameMappingService.get().getPlugin(); //removed some unncessary codes.. } @Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { if(opcode != Opcodes.INVOKEVIRTUAL){ mv.visitMethodInsn(opcode, owner, name, desc, itf); return; } MethodNode mn = _plugin.map(owner, name, desc, _context, this); if(mn == null){ mv.visitMethodInsn(opcode, owner, name, desc, itf); return; } //ASMUtil.debug(mn); //to double confirm the mn content is correct. performInline(ASMUtil.isStaticMethod(mn)?Opcodes.INVOKESTATIC:Opcodes.INVOKEVIRTUAL, owner, desc, mn); _plugin.postProcess(mn, this, _context); } protected void performInline(int opcode, String owner, String desc, MethodNode mn){ Remapper remapper = Mapper.getMapper(_context, _context.getReceiverFieldName()); mn.instructions.resetLabels(); Label end = new Label(); System.out.println("++"+end.toString()); _context.beginInline(); mn.accept(new InliningAdapter(this, opcode == Opcodes.INVOKESTATIC ? Opcodes.ACC_STATIC : 0, desc, remapper, end, _context)); _context.endInline(); super.visitLabel(end); } public void visitJumpInsn(int opcode, Label label) { super.visitJumpInsn(opcode, label); } @Override public void visitVarInsn(final int opcode, final int var){ super.visitVarInsn(opcode, var);; } ... } 

[新发现]

我觉得我现在更接近这个问题。

  • 内联访问者MethodCallInliner应该是正确的,因为对同一个类成功访问该访问者的另一个独立testing。
  • 问题在于如何构buildMethodVisitor链。 a)我只需要一次访问方法说明。 2) MethodCallInliner被安排在链的末尾。 在此之前,一些更多的访问者被插入到推理types信息中,这可能在MethodCallInliner方法内联期间使用。

我的连锁店是:

 @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { ..... MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions); return new TransformationChain(Opcodes.ASM5, access, name, desc, signature, mv, _context); //return new MethodCallInliner(access, desc, context); //This is OK. } public class TransformationChain extends BaseMethodTransform { public TransformationChain(int api, int access, String name, String desc, String signature, MethodVisitor mv, ClassContext classContext) { super(api, mv, classContext.getClassName(), name, desc); .... ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS); _visitors.add(new AnalyzerAdapter(Opcodes.ASM5, owner, access, name,desc, cw.visitMethod(access, name, desc, owner, null)){ @Override public void visitJumpInsn(final int opcode, final Label label){ super.visitJumpInsn(opcode, label); } }); MethodNode node = new MethodNode(access, name, desc, signature, null); _visitors.add(node); //cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS); //MethodNode node = context.getClassContext().getMethodNode(name, desc); //_visitors.add(new TypeInferencer(Opcodes.ASM5, cw.visitMethod(access, name, desc, null, null), node, context)); _visitors.add(name.equals(Constants.CONSTRUCTOR)?new ConstructorMerge(access, desc, context): new MethodCallInliner(access, desc, context)); } } abstract class BaseMethodTransform extends MethodVisitor { protected final List<MethodVisitor> _visitors = new LinkedList<MethodVisitor>(); public BaseMethodTransform(int api, MethodVisitor mv, String className, String methodName, String methodDesc) { super(api, mv); } @Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { for (MethodVisitor mv : _visitors) { mv.visitMethodInsn(opcode, owner, name, desc, itf); } } @Override public void visitIntInsn(int opcode, int operand) { for (MethodVisitor mv : _visitors) { mv.visitIntInsn(opcode, operand); } } @Override public void visitMaxs(int maxStack, int maxLocals) { for (MethodVisitor mv : _visitors) { if (mv!= _visitors.get(_visitors.size()-1) || mv instanceof TraceMethodVisitor) { continue; } mv.visitMaxs(maxStack, maxLocals); } } @Override public void visitJumpInsn(final int opcode, final Label label) { for (MethodVisitor mv : _visitors) { mv.visitJumpInsn(opcode, label); } } ...... } 

我在这里find的是,如果我注释掉_visitors.add(new AnalyzerAdapter..);生成的类是正确的_visitors.add(new AnalyzerAdapter..);TransformationChain ,这里新创build了MethodVisitor。 看来方法的某些元素有状态,可能会被MethodWriters修改(即使它们都是独立的),以前的修改会影响到后来的访问者

我也注意到这是标签:

 /** * Informations about forward references. Each forward reference is * described by two consecutive integers in this array: the first one is the * position of the first byte of the bytecode instruction that contains the * forward reference, while the second is the position of the first byte of * the forward reference itself. In fact the sign of the first integer * indicates if this reference uses 2 or 4 bytes, and its absolute value * gives the position of the bytecode instruction. This array is also used * as a bitset to store the subroutines to which a basic block belongs. This * information is needed in {@linked MethodWriter#visitMaxs}, after all * forward references have been resolved. Hence the same array can be used * for both purposes without problems. */ private int[] srcAndRefPositions; 

当AnalyzerAdapter :: visitJmpAdadpter首次访问它时,在数组的开头插入两个整数,例如10和11。 然后在下一次迭代`MethodCallInliner :: visitJmpInsn`,另外两个新的整数被添加到位置2和3.现在数组的内容是:

[10,11,16,17,0,0],其中对(10,11)用于AnalyzerAdapter,对(16,17)用于Method MethodCallInliner

但是我在这里困惑的是:当生成bytcode类(或块,堆栈框架计算什么)时,ASM应该能够为正确的MethodVisitor区分不同的对?

代码可以通过https://github.com/xushijie/InlineMethod/tree/typeinference进行访问

问题是由MethodVisitorpipe道访问标签(类读取器从类文件中读取)时MethodVisitor 。 标签有一个字段int [] srcAndRefPositions 。 一旦MethodVisitor访问了标签,它的两个连续位置(比如我的原始文章的末尾)就会更新。 就我而言, ifeq label中的ifeq label有2个MethodVisitors。 在生成类文件时(使用最后一个MethodVisitor),似乎srcAndRefPositions位置不正确。

我没有调查根本原因。 相反,我的解决scheme是克隆标签,然后在MethodVisitor访问时使用新标签。