我试图从premain方法添加到用户字节码,该方法在遇到新的行号节点时将String
形式的某些信息添加到列表中,尽管当我运行代理和应用程序jar时会发生以下异常:
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.VerifyError: Expecting a stackmap frame at branch target
134
Exception Details:
Location:
sketches/UserCode.main([Ljava/lang/String;)V @24: if_icmpge
Reason:
Expected stackmap frame at this location.
Bytecode:
0x0000000: b200 12b8 0018 b600 1cba 0037 0000 b900
0x0000010: 2e02 0057 033c 1b07 a200 6eb2 0012 b800
0x0000020: 18b6 001c ba00 3a00 00b9 002e 0200 57bb
0x0000030: 0014 59ba 004a 0000 1bb2 0012 b800 18b6
0x0000040: 001c ba00 4d00 00b9 002e 0200 57b8 0053
0x0000050: b700 564d b200 12b8 0018 b600 1cba 0059
0x0000060: 0000 b900 2e02 0057 2cb6 005c b200 12b8
0x0000070: 0018 b600 1cba 0037 0000 b900 2e02 0057
0x0000080: 8401 01a7 ff93 b200 12b8 0018 b600 1cba
0x0000090: 005f 0000 b900 2e02 0057 b1
Stackmap Table:
append_frame(@22,Integer)
chop_frame(@154,1)
以下是premain的转换方法:
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
// bootstrap loader is not what we're looking for and will be signified by null
if (loader == null) {
return null;
}
ClassNode cn = new ClassNode(ASM9);
ClassReader cr1 = new ClassReader(classfileBuffer);
cr1.accept(cn, 0);
...
// make the synchronized list field
cn.fields.add(new FieldNode(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC + Opcodes.ACC_VOLATILE,
listName, "Ljava/util/List;", null, null));
// go through all MethodNodes and all instruction lists in methods and add synchronized list insert op
// also, add initialisation for synchronized list (may include creating a static initialiser)
for (MethodNode mn : cn.methods) {
InsnList insns = mn.instructions;
if (insns.size() == 0) {
continue;
}
for (AbstractInsnNode node : insns) {
if (node instanceof LineNumberNode) {
InsnList addedInsns = new InsnList();
//TODO: fix this
addedInsns.add(new FieldInsnNode(GETSTATIC, cn.name, "list",
"Ljava/util/List;"));
addedInsns.add(new MethodInsnNode(INVOKESTATIC, "java/lang/Thread",
"currentThread", "()Ljava/lang/Thread;", false));
addedInsns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/Thread",
"getId", "()J", false));
addedInsns.add(new InvokeDynamicInsnNode("makeConcatWithConstants",
"(J)Ljava/lang/String;",
new Handle(H_INVOKESTATIC,
"java/lang/invoke/StringConcatFactory",
"makeConcatWithConstants",
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;",
false),
"\u0001|" + ((LineNumberNode) node).line + "|" + className));
addedInsns.add(new MethodInsnNode(INVOKEINTERFACE, "java/util/List",
"add", "(Ljava/lang/Object;)Z", true));
addedInsns.add(new InsnNode(POP));
insns.insert(node.getPrevious(), addedInsns);
}
}
}
ClassWriter cw1 = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cn.accept(cw1);
return cw1.toByteArray();
}
我已经看到了对以前问题的回答,其中异常消息或多或少相似,但是,至少对我来说,这些情况下的错误似乎更明显(即没有相应STORE的LOAD指令)。我还查看了ASM4用户指南,尽管我认为我不太理解关于堆栈映射帧的讨论…
任何关于如何解决这个问题的建议都将不胜感激——解释得越彻底越好;我希望能够理解和应用我将来可能遇到的任何此类问题的建议。
这不是最漂亮的,但我认为这是实现霍尔格建议的一种方式:
for (MethodNode mn : cn.methods) {
InsnList insns = mn.instructions;
if (insns.size() == 0) {
continue;
}
int lineNum = -1;
int l1 = -1;
int l2 = -1;
AbstractInsnNode node;
int numAdded;
for (int i = 0; i < insns.size(); i++) {
node = insns.get(i);
if (node instanceof LineNumberNode) {
lineNum = ((LineNumberNode) node).line;
} else if (node instanceof LabelNode) {
if (l1 == -1) {
l1 = i;
} else {
l2 = i;
}
} else if (node instanceof FrameNode) {
l1 = i;
}
if (lineNum > -1 && l1 < l2) {
InsnList addedInsns = new InsnList();
// bytecode insertion code
numAdded = addedInsns.size(); // get this before inserting into insns as the insert operation empties addedInsns
insns.insert(insns.get(l1), addedInsns);
lineNum = -1;
i += numAdded - 1; // -1 to counteract i incrementing with next iteration
l1 = -1;
l1 = -1;
}
}
}