Created
June 14, 2017 07:11
-
-
Save raphw/2b749d8d10875d6fab6a90c2ee851104 to your computer and use it in GitHub Desktop.
Inlining attempt with Byte Buddy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import net.bytebuddy.description.method.MethodDescription; | |
import net.bytebuddy.description.type.TypeDescription; | |
import net.bytebuddy.dynamic.DynamicType; | |
import net.bytebuddy.dynamic.scaffold.InstrumentedType; | |
import net.bytebuddy.implementation.Implementation; | |
import net.bytebuddy.implementation.bytecode.ByteCodeAppender; | |
import net.bytebuddy.pool.TypePool; | |
import org.objectweb.asm.*; | |
import org.objectweb.asm.commons.ClassRemapper; | |
import org.objectweb.asm.commons.SimpleRemapper; | |
import org.objectweb.asm.util.TraceClassVisitor; | |
import java.io.IOException; | |
import java.io.OutputStreamWriter; | |
import java.io.PrintWriter; | |
import java.lang.reflect.Constructor; | |
import java.lang.reflect.Field; | |
import java.lang.reflect.Method; | |
import java.util.ArrayList; | |
import java.util.List; | |
import static net.bytebuddy.matcher.ElementMatchers.*; | |
public class Experiment { | |
public static void main(String[] args) throws Exception { | |
DynamicType.Builder<?> builder = new ByteBuddy().rebase(Foo.class); | |
ClassReader classReader = new ClassReader(FooImpl.class.getName()); | |
TypePool typePool = TypePool.ClassLoading.of(FooImpl.class.getClassLoader()); | |
Implementation i = new Implementation() { | |
@Override | |
public ByteCodeAppender appender(Target implementationTarget) { | |
return (methodVisitor, implementationContext, instrumentedMethod) -> { | |
int[] maxs = new int[]{-1, -1}; | |
classReader.accept(new ClassRemapper(new ClassVisitor(Opcodes.ASM5) { | |
@Override | |
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { | |
if (instrumentedMethod.getInternalName().equals(name) && instrumentedMethod.getDescriptor().equals(desc)) { | |
return new MethodVisitor(Opcodes.ASM5, methodVisitor) { | |
@Override | |
public void visitCode() { | |
} | |
@Override | |
public void visitMaxs(int maxStack, int maxLocals) { | |
maxs[0] = maxStack; | |
maxs[1] = maxLocals; | |
} | |
@Override | |
public void visitEnd() { | |
} | |
@Override | |
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { | |
if (opcode == Opcodes.INVOKESPECIAL && Foo.class.getName().equals(owner.replace('/', '.'))) { | |
List<TypeDescription> arguments = new ArrayList<>(); | |
for (Type t : Type.getArgumentTypes(desc)) { | |
arguments.add(typePool.describe(t.getClassName()).resolve()); | |
} | |
MethodDescription.SignatureToken token = new MethodDescription.SignatureToken( | |
name, | |
typePool.describe(Type.getReturnType(desc).getClassName()).resolve(), | |
arguments | |
); | |
if (itf) { | |
implementationTarget | |
.invokeDefault(token, typePool.describe(name.replace('/', '.')).resolve()) | |
.apply(mv, implementationContext); | |
} else { | |
implementationTarget | |
.invokeSuper(token) | |
.apply(mv, implementationContext); | |
} | |
} else { | |
super.visitMethodInsn(opcode, owner, name, desc, itf); | |
} | |
} | |
}; | |
} else { | |
return null; | |
} | |
} | |
}, new SimpleRemapper(Foo.class.getName(), FooImpl.class.getName())), 0); | |
if (maxs[0] == -1 || maxs[1] == -1) { | |
throw new IllegalStateException("Could not find: " + instrumentedMethod); | |
} | |
return new ByteCodeAppender.Size(maxs[0], maxs[1]); | |
}; | |
} | |
@Override | |
public InstrumentedType prepare(InstrumentedType instrumentedType) { | |
return instrumentedType; | |
} | |
}; | |
for (Method m : FooImpl.class.getDeclaredMethods()) { | |
try { | |
Foo.class.getDeclaredMethod(m.getName(), m.getParameterTypes()); | |
builder = builder.method(named(m.getName()).and(takesArguments(m.getParameterTypes()))) | |
.intercept(i); | |
} catch (NoSuchMethodException e) { | |
builder = builder.defineMethod(m.getName(), m.getGenericReturnType(), m.getModifiers()) | |
.withParameters(m.getParameterTypes()) | |
.intercept(i); | |
} | |
} | |
for (Constructor<?> c : FooImpl.class.getDeclaredConstructors()) { | |
try { | |
Foo.class.getDeclaredConstructor(c.getParameterTypes()); | |
builder = builder.method(isConstructor().and(takesArguments(c.getParameterTypes()))) | |
.intercept(i); | |
} catch (NoSuchMethodException e) { | |
builder = builder.defineConstructor(c.getModifiers()) | |
.withParameters(c.getParameterTypes()) | |
.intercept(i); | |
} | |
} | |
for (Field f : FooImpl.class.getDeclaredFields()) { | |
try { | |
Foo.class.getDeclaredField(f.getName()); | |
throw new IllegalStateException("Cannot copy duplicate field: " + f); | |
} catch (NoSuchFieldException e) { | |
builder = builder.defineField(f.getName(), f.getType(), f.getModifiers()); | |
} | |
} | |
DynamicType.Unloaded<?> dynamicType = builder.make(); | |
new ClassReader(dynamicType.getBytes()).accept(new TraceClassVisitor(new PrintWriter(new OutputStreamWriter(System.out))), 0); | |
Class<?> transformed = dynamicType.load(null).getLoaded(); | |
System.out.println(transformed.getName()); | |
Object instance = transformed.newInstance(); | |
System.out.println(transformed.getMethod("b").invoke(instance)); | |
} | |
public static class Foo { | |
private String x; | |
public String a() { | |
return "a"; | |
} | |
public String b() { | |
return "b"; | |
} | |
} | |
static class FooImpl extends Foo { | |
private String y; | |
@Override | |
public String b() { | |
return super.b() + "b"; | |
} | |
public String c() { | |
return "c"; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment