字节伙伴 AsmVisitorWrapper
Bytebuddy AsmVisitorWrapper
我用 asm 检测了一个已经加载的 class(名为 test 的方法)(有效):
public class Test {
public void test() {
System.out.println("Can I call test2 private void ?");
test2();
}
private void test2() {
System.out.println("test2 called");
}
public static class TestAgent {
public static void agentmain(String arg, Instrumentation instrumentation) {
System.out.println(TestAgent.class.getName() + " loaded");
instrumentation.addTransformer(new TestAgentClassFileTransformer());
try {
instrumentation.redefineClasses(new ClassDefinition(Test.class, Tools.getBytesFromClass(Test.class)));
} catch (ClassNotFoundException | UnmodifiableClassException | IOException e) {
e.printStackTrace();
}
}
}
public static class TestAgentMethodVisitor extends MethodVisitor {
public TestAgentMethodVisitor(int api, MethodVisitor mv) {
super(api, mv);
}
@Override
public void visitCode() {
Label l0 = new Label();
super.visitLabel(l0);
super.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
super.visitLdcInsn("yes");
super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
Label l1 = new Label();
super.visitLabel(l1);
super.visitVarInsn(Opcodes.ALOAD, 0);
super.visitMethodInsn(Opcodes.INVOKESPECIAL, "test/Test", "test2", "()V", false);
Label l2 = new Label();
super.visitLabel(l2);
super.visitInsn(Opcodes.RETURN);
Label l3 = new Label();
super.visitLabel(l3);
super.visitLocalVariable("this", "Ltest/Test;", null, l0, l3, 0);
super.visitMaxs(2, 1);
super.visitEnd();
}
}
public static class TestAgentClassVisitor extends ClassVisitor {
public TestAgentClassVisitor(int api, ClassVisitor cv) {
super(api, cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (name.equals("test")) {
return new TestAgentMethodVisitor(Opcodes.ASM5, super.visitMethod(access, name, desc, signature, exceptions));
}
return super.visitMethod(access, name, desc, signature, exceptions);
}
}
public static class TestAgentClassFileTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
if (className.equals("test/Test")) {
ClassReader classReader = new ClassReader(classfileBuffer);
ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_FRAMES);
classReader.accept(new TestAgentClassVisitor(Opcodes.ASM5, classWriter), ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG);
return classWriter.toByteArray();
}
return classfileBuffer;
}
}
}
输出:
Before agent
Can I call test2 private void ?
test2 called
test.Test$TestAgent loaded
After agent
yes
test2 called
而且我想用byte-buddy来使用ResettableClassFileTransformer。所以我尝试转换代码。我使用 MethodDelagation.to 来修改我的方法。但是我不知道如何在 class TestAgentMethodInterceptor 中使用 field/private Test 的私有 field/private 方法(这可能吗?),所以我使用了 AsmVisitorWrapper(见后文)。有效
public class Test {
public void test() {
System.out.println("Can I call test2 private void ?");
test2();
}
private void test2() {
System.out.println("test2 called");
}
public static class TestAgent {
private static Instrumentation inst;
private static ResettableClassFileTransformer resettableClassFileTransformer;
public static void agentmain(String arg, Instrumentation instrumentation) {
System.out.println(TestAgent.class.getName() + " loaded");
inst = instrumentation;
AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() {
@Override
public Builder<?> transform(Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader) {
return builder.method(ElementMatchers.named("test")).intercept(MethodDelegation.to(TestAgentMethodInterceptor.class));
}
};
AgentBuilder builder = new AgentBuilder.Default()
.with(RedefinitionStrategy.RETRANSFORMATION)
.disableClassFormatChanges();
Narrowable narrowable = builder.type(ElementMatchers.named(Test.class.getName()));
Extendable extendable = narrowable.transform(transformer);
resettableClassFileTransformer = extendable.installOn(instrumentation);
}
public static boolean reset() {
if (inst != null && resettableClassFileTransformer != null) {
return resettableClassFileTransformer.reset(inst, RedefinitionStrategy.RETRANSFORMATION);
}
return true;
}
}
public static class TestAgentMethodInterceptor {
public static void intercept() {
System.out.println("I don't know how to call private void test2");
}
}
}
输出:
Before agent
Can I call test2 private void ?
test2 called
test.Test$TestAgent loaded
After agent
I don't know how to call private void test2
所以我尝试使用 AsmVisitorWrapper 来使用我的代码,但它什么也没做。
public class Test {
public void test() {
System.out.println("Can I call test2 private void ?");
test2();
}
private void test2() {
System.out.println("test2 called");
}
public static class TestAgent {
private static Instrumentation inst;
private static ResettableClassFileTransformer resettableClassFileTransformer;
public static void agentmain(String arg, Instrumentation instrumentation) {
System.out.println(TestAgent.class.getName() + " loaded");
inst = instrumentation;
AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() {
@Override
public Builder<?> transform(Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader) {
return builder.visit(new AsmVisitorWrapper() {
@Override
public ClassVisitor wrap(TypeDescription instrumentedType, ClassVisitor classVisitor, Implementation.Context implementationContext, TypePool typePool, FieldList<FieldDescription.InDefinedShape> fields, MethodList<?> methods, int writerFlags, int readerFlags) {
return new TestAgentClassVisitor(Opcodes.ASM5, classVisitor);
}
@Override
public int mergeWriter(int flags) {
return flags;
}
@Override
public int mergeReader(int flags) {
return flags;
}
});
}
};
AgentBuilder builder = new AgentBuilder.Default()
.with(RedefinitionStrategy.RETRANSFORMATION)
.disableClassFormatChanges();
Narrowable narrowable = builder.type(ElementMatchers.named(Test.class.getName()));
Extendable extendable = narrowable.transform(transformer);
resettableClassFileTransformer = extendable.installOn(instrumentation);
}
public static boolean reset() {
if (inst != null && resettableClassFileTransformer != null) {
return resettableClassFileTransformer.reset(inst, RedefinitionStrategy.RETRANSFORMATION);
}
return true;
}
}
public static class TestAgentMethodVisitor extends MethodVisitor {
public TestAgentMethodVisitor(int api, MethodVisitor mv) {
super(api, mv);
}
@Override
public void visitCode() {
Label l0 = new Label();
super.visitLabel(l0);
super.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
super.visitLdcInsn("yes");
super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
Label l1 = new Label();
super.visitLabel(l1);
super.visitVarInsn(Opcodes.ALOAD, 0);
super.visitMethodInsn(Opcodes.INVOKESPECIAL, "test/Test", "test2", "()V", false);
Label l2 = new Label();
super.visitLabel(l2);
super.visitInsn(Opcodes.RETURN);
Label l3 = new Label();
super.visitLabel(l3);
super.visitLocalVariable("this", "Ltest/Test;", null, l0, l3, 0);
super.visitMaxs(2, 1);
super.visitEnd();
}
}
public static class TestAgentClassVisitor extends ClassVisitor {
public TestAgentClassVisitor(int api, ClassVisitor cv) {
super(api, cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (name.equals("test")) {
return new TestAgentMethodVisitor(Opcodes.ASM5, super.visitMethod(access, name, desc, signature, exceptions));
}
return super.visitMethod(access, name, desc, signature, exceptions);
}
}
}
输出:
Before agent
Can I call test2 private void ?
test2 called
test.Test$TestAgent loaded
After agent
Can I call test2 private void ?
test2 called
那么如何使用 AsmVisitorWrapper 呢?
您目前只能使用反射或实现您自己的 ParameterBinder
并使用 MethodDelegation
注册自定义代理。我希望在某个时候添加标准组件这样的东西。此问题由以下人员跟踪:https://github.com/raphw/byte-buddy/issues/252
我用 asm 检测了一个已经加载的 class(名为 test 的方法)(有效):
public class Test {
public void test() {
System.out.println("Can I call test2 private void ?");
test2();
}
private void test2() {
System.out.println("test2 called");
}
public static class TestAgent {
public static void agentmain(String arg, Instrumentation instrumentation) {
System.out.println(TestAgent.class.getName() + " loaded");
instrumentation.addTransformer(new TestAgentClassFileTransformer());
try {
instrumentation.redefineClasses(new ClassDefinition(Test.class, Tools.getBytesFromClass(Test.class)));
} catch (ClassNotFoundException | UnmodifiableClassException | IOException e) {
e.printStackTrace();
}
}
}
public static class TestAgentMethodVisitor extends MethodVisitor {
public TestAgentMethodVisitor(int api, MethodVisitor mv) {
super(api, mv);
}
@Override
public void visitCode() {
Label l0 = new Label();
super.visitLabel(l0);
super.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
super.visitLdcInsn("yes");
super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
Label l1 = new Label();
super.visitLabel(l1);
super.visitVarInsn(Opcodes.ALOAD, 0);
super.visitMethodInsn(Opcodes.INVOKESPECIAL, "test/Test", "test2", "()V", false);
Label l2 = new Label();
super.visitLabel(l2);
super.visitInsn(Opcodes.RETURN);
Label l3 = new Label();
super.visitLabel(l3);
super.visitLocalVariable("this", "Ltest/Test;", null, l0, l3, 0);
super.visitMaxs(2, 1);
super.visitEnd();
}
}
public static class TestAgentClassVisitor extends ClassVisitor {
public TestAgentClassVisitor(int api, ClassVisitor cv) {
super(api, cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (name.equals("test")) {
return new TestAgentMethodVisitor(Opcodes.ASM5, super.visitMethod(access, name, desc, signature, exceptions));
}
return super.visitMethod(access, name, desc, signature, exceptions);
}
}
public static class TestAgentClassFileTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
if (className.equals("test/Test")) {
ClassReader classReader = new ClassReader(classfileBuffer);
ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_FRAMES);
classReader.accept(new TestAgentClassVisitor(Opcodes.ASM5, classWriter), ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG);
return classWriter.toByteArray();
}
return classfileBuffer;
}
}
}
输出:
Before agent
Can I call test2 private void ?
test2 called
test.Test$TestAgent loaded
After agent
yes
test2 called
而且我想用byte-buddy来使用ResettableClassFileTransformer。所以我尝试转换代码。我使用 MethodDelagation.to 来修改我的方法。但是我不知道如何在 class TestAgentMethodInterceptor 中使用 field/private Test 的私有 field/private 方法(这可能吗?),所以我使用了 AsmVisitorWrapper(见后文)。有效
public class Test {
public void test() {
System.out.println("Can I call test2 private void ?");
test2();
}
private void test2() {
System.out.println("test2 called");
}
public static class TestAgent {
private static Instrumentation inst;
private static ResettableClassFileTransformer resettableClassFileTransformer;
public static void agentmain(String arg, Instrumentation instrumentation) {
System.out.println(TestAgent.class.getName() + " loaded");
inst = instrumentation;
AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() {
@Override
public Builder<?> transform(Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader) {
return builder.method(ElementMatchers.named("test")).intercept(MethodDelegation.to(TestAgentMethodInterceptor.class));
}
};
AgentBuilder builder = new AgentBuilder.Default()
.with(RedefinitionStrategy.RETRANSFORMATION)
.disableClassFormatChanges();
Narrowable narrowable = builder.type(ElementMatchers.named(Test.class.getName()));
Extendable extendable = narrowable.transform(transformer);
resettableClassFileTransformer = extendable.installOn(instrumentation);
}
public static boolean reset() {
if (inst != null && resettableClassFileTransformer != null) {
return resettableClassFileTransformer.reset(inst, RedefinitionStrategy.RETRANSFORMATION);
}
return true;
}
}
public static class TestAgentMethodInterceptor {
public static void intercept() {
System.out.println("I don't know how to call private void test2");
}
}
}
输出:
Before agent
Can I call test2 private void ?
test2 called
test.Test$TestAgent loaded
After agent
I don't know how to call private void test2
所以我尝试使用 AsmVisitorWrapper 来使用我的代码,但它什么也没做。
public class Test {
public void test() {
System.out.println("Can I call test2 private void ?");
test2();
}
private void test2() {
System.out.println("test2 called");
}
public static class TestAgent {
private static Instrumentation inst;
private static ResettableClassFileTransformer resettableClassFileTransformer;
public static void agentmain(String arg, Instrumentation instrumentation) {
System.out.println(TestAgent.class.getName() + " loaded");
inst = instrumentation;
AgentBuilder.Transformer transformer = new AgentBuilder.Transformer() {
@Override
public Builder<?> transform(Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader) {
return builder.visit(new AsmVisitorWrapper() {
@Override
public ClassVisitor wrap(TypeDescription instrumentedType, ClassVisitor classVisitor, Implementation.Context implementationContext, TypePool typePool, FieldList<FieldDescription.InDefinedShape> fields, MethodList<?> methods, int writerFlags, int readerFlags) {
return new TestAgentClassVisitor(Opcodes.ASM5, classVisitor);
}
@Override
public int mergeWriter(int flags) {
return flags;
}
@Override
public int mergeReader(int flags) {
return flags;
}
});
}
};
AgentBuilder builder = new AgentBuilder.Default()
.with(RedefinitionStrategy.RETRANSFORMATION)
.disableClassFormatChanges();
Narrowable narrowable = builder.type(ElementMatchers.named(Test.class.getName()));
Extendable extendable = narrowable.transform(transformer);
resettableClassFileTransformer = extendable.installOn(instrumentation);
}
public static boolean reset() {
if (inst != null && resettableClassFileTransformer != null) {
return resettableClassFileTransformer.reset(inst, RedefinitionStrategy.RETRANSFORMATION);
}
return true;
}
}
public static class TestAgentMethodVisitor extends MethodVisitor {
public TestAgentMethodVisitor(int api, MethodVisitor mv) {
super(api, mv);
}
@Override
public void visitCode() {
Label l0 = new Label();
super.visitLabel(l0);
super.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
super.visitLdcInsn("yes");
super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
Label l1 = new Label();
super.visitLabel(l1);
super.visitVarInsn(Opcodes.ALOAD, 0);
super.visitMethodInsn(Opcodes.INVOKESPECIAL, "test/Test", "test2", "()V", false);
Label l2 = new Label();
super.visitLabel(l2);
super.visitInsn(Opcodes.RETURN);
Label l3 = new Label();
super.visitLabel(l3);
super.visitLocalVariable("this", "Ltest/Test;", null, l0, l3, 0);
super.visitMaxs(2, 1);
super.visitEnd();
}
}
public static class TestAgentClassVisitor extends ClassVisitor {
public TestAgentClassVisitor(int api, ClassVisitor cv) {
super(api, cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (name.equals("test")) {
return new TestAgentMethodVisitor(Opcodes.ASM5, super.visitMethod(access, name, desc, signature, exceptions));
}
return super.visitMethod(access, name, desc, signature, exceptions);
}
}
}
输出:
Before agent
Can I call test2 private void ?
test2 called
test.Test$TestAgent loaded
After agent
Can I call test2 private void ?
test2 called
那么如何使用 AsmVisitorWrapper 呢?
您目前只能使用反射或实现您自己的 ParameterBinder
并使用 MethodDelegation
注册自定义代理。我希望在某个时候添加标准组件这样的东西。此问题由以下人员跟踪:https://github.com/raphw/byte-buddy/issues/252