gpt4 book ai didi

java - 如何使用ASM重写visitMethod将两个类合并为一个类

转载 作者:行者123 更新时间:2023-12-01 11:38:44 24 4
gpt4 key购买 nike

我正在尝试通过 ASM API 在运行时将类 Callee 合并到类 Caller 。下面的部分代码是从 http://asm.ow2.org/current/asm-transformations.pdf 中的 3.1.5(将两个类合并为一个) 复制的。 。我修改了示例代码,因为我使用ASM 5.0版本。

public class Caller {
public static void main(String[] args) {
// TODO Auto-generated method stub
new Caller().test("xu", "shijie");
}

public void test(String a, String b){
Callee obj = new Callee(a,b);
System.out.println(obj.calculate(10));

System.out.println("1..........");
}
}
public class Callee {

final String concat;
public Callee(String a, String b){
concat = a+b;
}

public String calculate(int t){
return concat+t;
}

}

class MergeAdapter extends ClassVisitor {
private ClassNode cn;
private String cname;

public MergeAdapter(ClassVisitor cv, ClassNode cn) {
super(Opcodes.ASM5, cv);
this.cn = cn;
}

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

public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
return super.visitMethod(access, name, desc, signature, exceptions);
}

public void visitEnd() {
for (Iterator<FieldNode> it = cn.fields.iterator(); it.hasNext();) {
((FieldNode) it.next()).accept(this);
}
for (Iterator it = cn.methods.iterator(); it.hasNext();) {
MethodNode mn = (MethodNode) it.next();
String[] exceptions = new String[mn.exceptions.size()];
mn.exceptions.toArray(exceptions);
MethodVisitor mv = cv.visitMethod(mn.access, mn.name, mn.desc,
mn.signature, exceptions);
mn.instructions.resetLabels();
mn.accept(new RemappingMethodAdapter(mn.access, mn.desc, mv,
new SimpleRemapper(cn.name, cname)));
}
super.visitEnd();
}
}

public class Main 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 {
ClassReader cr = new ClassReader(is);
ClassNode classNode = new ClassNode();
cr.accept(classNode, 0);

resource = caller.replace('.', '/')+".class";
is = getResourceAsStream(resource);
cr = new ClassReader(is);
ClassWriter cw = new ClassWriter(0);
ClassVisitor visitor = new MergeAdapter(cw, classNode);
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.adapted");
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 Main();
Class<?> c = loader.loadClass(args[0]);
Method m = c.getMethod("main", new Class<?>[] { String[].class });
String[] applicationArgs = new String[args.length - 1];
System.arraycopy(args, 1, applicationArgs, 0, applicationArgs.length);
m.invoke(null, new Object[] { applicationArgs });
}
}

上面的主要问题是新创建的Callee对象

            **Callee obj = new Callee(a,b);**

Caller::test(String a, String b)的主体中仍然存在,并且为测试生成的字节码是:

public void test(java.lang.String, java.lang.String);
flags: ACC_PUBLIC
Code:
stack=4, locals=4, args_size=3
0: new #26 // class code/sxu/asm/example/Callee
3: dup
4: aload_1
5: aload_2
6: invokespecial #28 // Method code/sxu/asm/example/Callee."<init>":(Ljava/lang/String;Ljava/lang/String;)V
9: astore_3
10: getstatic #34 // Field java/lang/System.out:Ljava/io/
PrintStream;

这是不正确的。因此,当 m.invoke() (重新加载)在 main 方法末尾时,这将导致“尝试重复名称的类定义:”异常。

我的想法是,Caller 类中的所有所有者:code/sxu/asm/example/Callee 也应该映射到 code/sxu/asm/example/Caller。因此我重写了

public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
return super.visitMethod(access, name, desc, signature, exceptions);
}

但我不知道如何在访问方法的主体中实现这里。谁能给点建议吗?

最佳答案

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

MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
return new RemappingMethodAdapter(access, desc, mv,
new SimpleRemapper(cn.name, cname));
}

关于java - 如何使用ASM重写visitMethod将两个类合并为一个类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29733578/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com