内联方法体中调用虚拟的意外指令和参数
我按照 http://asm.ow2.org/current/asm-transformations.pdf 中“3.2.6 内联方法”中的示例代码,将 MethodNode 内联到调用站点。
我的问题是,内联后生成的字节码中显示了一些意想不到的指令(这些字节码与我的代码不一致),并且仅当内联方法体之后并且堆栈上的变量由xLoad加载时,问题才存在。ifeq
我仍然没有找到问题的根本原因。现在我开始删除所有不必要的代码,旨在用最少的代码重现它。欢迎任何有好建议的人。
这是我现有的一个基础:问题与Frame无关,因为当ClassRewiter的配置和ClassReader的配置时,问题仍然存在。COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS
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;
}
.方法编写器上调用方的相应字节码操作跟踪为:
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
- 问题在于如何构建方法访问器链。a) 我只想要一张通行证,按照方法说明进行访问。2)布置在链的末端。在此之前,还会插入更多的访问者来推断类型信息,这些信息可能会在 .
MethodCallInliner
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);
}
}
......
}
我在这里的发现是,如果我在 中注释掉生成的类,则生成的类是正确的,其方法访问器是在此处新创建的。似乎方法的某些元素具有状态,这些状态可能会被MethodWriters修改(即使它们都是独立的),并且先前的修改对以后的访问者有影响。_visitors.add(new AnalyzerAdapter..);
TransformationChain
我还注意到它是标签:
/**
* 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) 用于方法 。
MethodCallInliner
但让我感到困惑的是:ASM应该能够在生成bytcode类(或块,堆栈帧计算等)时为正确的方法访问器区分不同的对?
该代码可以通过 https://github.com/xushijie/InlineMethod/tree/typeinference