Skip to content

Instantly share code, notes, and snippets.

@raphw
Created June 14, 2017 07:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save raphw/2b749d8d10875d6fab6a90c2ee851104 to your computer and use it in GitHub Desktop.
Save raphw/2b749d8d10875d6fab6a90c2ee851104 to your computer and use it in GitHub Desktop.
Inlining attempt with Byte Buddy
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