Skip to content

Instantly share code, notes, and snippets.

@forax
Last active November 22, 2019 21:20
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 forax/d0f0034190bc479b86ce977fb94ca176 to your computer and use it in GitHub Desktop.
Save forax/d0f0034190bc479b86ce977fb94ca176 to your computer and use it in GitHub Desktop.
import static java.lang.invoke.MethodHandles.lookup;
import java.lang.reflect.InvocationTargetException;
import fr.umlv.nreflect.ReflectMirror;
public class Main {
private void foo(String s) {
System.out.println("hello " + s);
}
private static void bar(String s) {
System.out.println("hello " + s);
}
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException {
var foo = Main.class.getDeclaredMethod("foo", String.class);
var fooMirror = ReflectMirror.mirror(lookup(), foo);
System.out.println(fooMirror);
var main = new Main();
fooMirror.invoke(main, "bob");
var bar = Main.class.getDeclaredMethod("bar", String.class);
var barMirror = ReflectMirror.mirror(lookup(), bar);
System.out.println(barMirror);
barMirror.invoke(main, "anna");
}
}
package fr.umlv.nreflect;
import static java.lang.invoke.MethodHandles.dropArguments;
import static java.lang.invoke.MethodHandles.lookup;
import static java.lang.invoke.MethodType.genericMethodType;
import static java.lang.invoke.MethodType.methodType;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Base64;
public abstract class ReflectMirror {
private static final byte[] REFLECT_MIRROR_IMPL;
static {
//var text = "yv66vgAAADkAOAEAImZyL3VtbHYvbnJlZmxlY3QvUmVmbGVjdE1pcnJvckltcGwHAAEBAB5mci91bWx2L25yZWZsZWN0L1JlZmxlY3RNaXJyb3IHAAMBABZSZWZsZWN0TWlycm9ySW1wbC5qYXZhAQAGPGluaXQ+AQADKClWDAAGAAcKAAQACAEABHRoaXMBACRMZnIvdW1sdi9ucmVmbGVjdC9SZWZsZWN0TWlycm9ySW1wbDsBAAZpbnZva2UBADkoTGphdmEvbGFuZy9PYmplY3Q7W0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsBACtqYXZhL2xhbmcvcmVmbGVjdC9JbnZvY2F0aW9uVGFyZ2V0RXhjZXB0aW9uBwAOAQAaamF2YS9sYW5nL1J1bnRpbWVFeGNlcHRpb24HABABAA9qYXZhL2xhbmcvRXJyb3IHABIBABNqYXZhL2xhbmcvVGhyb3dhYmxlBwAUAQADYnNtAQBrKExqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZXMkTG9va3VwO0xqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvQ2xhc3M7KUxqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZTsMABYAFwoABAAYDwYAGQEAAm1oAQAfTGphdmEvbGFuZy9pbnZva2UvTWV0aG9kSGFuZGxlOwwAGwAcEQAAAB0BAB1qYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZQcAHwEAC2ludm9rZUV4YWN0DAAhAA0KACAAIgEAEGphdmEvbGFuZy9PYmplY3QHACQBABNbTGphdmEvbGFuZy9PYmplY3Q7BwAmAQAYKExqYXZhL2xhbmcvVGhyb3dhYmxlOylWDAAGACgKAA8AKQEACHJlY2VpdmVyAQASTGphdmEvbGFuZy9PYmplY3Q7AQAEYXJncwEAAWUBABVMamF2YS9sYW5nL1Rocm93YWJsZTsBAAVjYXVzZQEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAA1TdGFja01hcFRhYmxlAQAKRXhjZXB0aW9ucwEAClNvdXJjZUZpbGUBABBCb290c3RyYXBNZXRob2RzACEAAgAEAAAAAAACAAEABgAHAAEAMQAAAC8AAQABAAAABSq3AAmxAAAAAgAyAAAABgABAAAABgAzAAAADAABAAAABQAKAAsAAAABAAwADQACADEAAADFAAMABQAAABsSHk4tKyy2ACOwOgQZBL86BLsAD1kZBLcAKr8AAwADAAkACgARAAMACQAKABMAAwAJAA8AFQADADQAAAAcAAL/AAoABAcAAgcAJQcAJwcAIAABBwAVRAcAFQAyAAAAGgAGAAAACQADAAsACgAMAAwADQAPAA4AEQAPADMAAAA+AAYAAAAbAAoACwAAAAAAGwArACwAAQAAABsALQAmAAIAAwAYABsAHAADAAwAAwAuAC8ABAARAAoAMAAvAAQANQAAAAQAAQAPAAIANgAAAAIABQA3AAAABgABABoAAA==";
var text = "yv66vgAAADkAOgEAImZyL3VtbHYvbnJlZmxlY3QvUmVmbGVjdE1pcnJvckltcGwHAAEBAB5mci91bWx2L25yZWZsZWN0L1JlZmxlY3RNaXJyb3IHAAMBABZSZWZsZWN0TWlycm9ySW1wbC5qYXZhAQAGPGluaXQ+AQADKClWDAAGAAcKAAQACAEABHRoaXMBACRMZnIvdW1sdi9ucmVmbGVjdC9SZWZsZWN0TWlycm9ySW1wbDsBAAZpbnZva2UBADkoTGphdmEvbGFuZy9PYmplY3Q7W0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsBACtqYXZhL2xhbmcvcmVmbGVjdC9JbnZvY2F0aW9uVGFyZ2V0RXhjZXB0aW9uBwAOAQAaamF2YS9sYW5nL1J1bnRpbWVFeGNlcHRpb24HABABAA9qYXZhL2xhbmcvRXJyb3IHABIBABNqYXZhL2xhbmcvVGhyb3dhYmxlBwAUAQAeamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzBwAWAQAJY2xhc3NEYXRhAQBeKExqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZXMkTG9va3VwO0xqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvQ2xhc3M7KUxqYXZhL2xhbmcvT2JqZWN0OwwAGAAZCgAXABoPBgAbAQACbWgBAB9MamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGU7DAAdAB4RAAAAHwEAHWphdmEvbGFuZy9pbnZva2UvTWV0aG9kSGFuZGxlBwAhAQALaW52b2tlRXhhY3QMACMADQoAIgAkAQAQamF2YS9sYW5nL09iamVjdAcAJgEAE1tMamF2YS9sYW5nL09iamVjdDsHACgBABgoTGphdmEvbGFuZy9UaHJvd2FibGU7KVYMAAYAKgoADwArAQAIcmVjZWl2ZXIBABJMamF2YS9sYW5nL09iamVjdDsBAARhcmdzAQABZQEAFUxqYXZhL2xhbmcvVGhyb3dhYmxlOwEABWNhdXNlAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEADVN0YWNrTWFwVGFibGUBAApFeGNlcHRpb25zAQAKU291cmNlRmlsZQEAEEJvb3RzdHJhcE1ldGhvZHMAIQACAAQAAAAAAAIAAQAGAAcAAQAzAAAALwABAAEAAAAFKrcACbEAAAACADQAAAAGAAEAAAAGADUAAAAMAAEAAAAFAAoACwAAAAEADAANAAIAMwAAAMUAAwAFAAAAGxIgTi0rLLYAJbA6BBkEvzoEuwAPWRkEtwAsvwADAAMACQAKABEAAwAJAAoAEwADAAkADwAVAAMANgAAABwAAv8ACgAEBwACBwAnBwApBwAiAAEHABVEBwAVADQAAAAaAAYAAAAJAAMACwAKAAwADAANAA8ADgARAA8ANQAAAD4ABgAAABsACgALAAAAAAAbAC0ALgABAAAAGwAvACgAAgADABgAHQAeAAMADAADADAAMQAEABEACgAyADEABAA3AAAABAABAA8AAgA4AAAAAgAFADkAAAAGAAEAHAAA";
REFLECT_MIRROR_IMPL = Base64.getDecoder().decode(text);
}
public abstract Object invoke(Object receiver, Object... args) throws InvocationTargetException;
public static ReflectMirror mirror(Lookup lookup, Method method) {
MethodHandle target;
try {
target = lookup.unreflect(method);
} catch (IllegalAccessException e) {
throw (IllegalAccessError)new IllegalAccessError().initCause(e);
}
var parameterCount = target.type().parameterCount();
target = target.asType(genericMethodType(parameterCount));
if (Modifier.isStatic(method.getModifiers())) {
target = dropArguments(target.asSpreader(Object[].class, parameterCount), 0, Object.class);
} else {
target = target.asSpreader(Object[].class, parameterCount - 1);
}
MethodHandle constructor;
try {
var hiddenClassLookup = lookup().defineHiddenClassWithClassData(REFLECT_MIRROR_IMPL, target, true /*false*/ /*, Lookup.ClassOption.NESTMATE*/);
constructor = hiddenClassLookup.findConstructor(hiddenClassLookup.lookupClass(), methodType(void.class)).asType(methodType(ReflectMirror.class));
} catch (NoSuchMethodException | IllegalAccessException e) {
throw (IllegalAccessError)new IllegalAccessError().initCause(e);
}
try {
return (ReflectMirror)constructor.invokeExact();
} catch (RuntimeException | Error e) {
throw e;
} catch(Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
// private static MethodHandle bsm(Lookup lookup, String name, Class<?> type) throws IllegalAccessException {
// //System.out.println("bsm " + name);
// return (MethodHandle)MethodHandles.classData(lookup, name, type);
// }
}
package fr.umlv.nreflect.build;
import java.util.Base64;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.ConstantDynamic;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public class ReflectMirrorImplDump implements Opcodes {
public static byte[] dump() throws Exception {
ClassWriter classWriter = new ClassWriter(0);
MethodVisitor mv;
classWriter.visit(V13, ACC_PUBLIC | ACC_SUPER, "fr/umlv/nreflect/ReflectMirrorImpl", null,
"fr/umlv/nreflect/ReflectMirror", null);
classWriter.visitSource("ReflectMirrorImpl.java", null);
{
mv = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
Label label0 = new Label();
mv.visitLabel(label0);
mv.visitLineNumber(6, label0);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "fr/umlv/nreflect/ReflectMirror", "<init>", "()V", false);
mv.visitInsn(RETURN);
Label label1 = new Label();
mv.visitLabel(label1);
mv.visitLocalVariable("this", "Lfr/umlv/nreflect/ReflectMirrorImpl;", null, label0, label1, 0);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{
mv = classWriter.visitMethod(ACC_PUBLIC, "invoke",
"(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", null,
new String[] { "java/lang/reflect/InvocationTargetException" });
mv.visitCode();
Label label0 = new Label();
Label label1 = new Label();
Label label2 = new Label();
mv.visitTryCatchBlock(label0, label1, label2, "java/lang/RuntimeException");
mv.visitTryCatchBlock(label0, label1, label2, "java/lang/Error");
Label label3 = new Label();
mv.visitTryCatchBlock(label0, label1, label3, "java/lang/Throwable");
Label label4 = new Label();
mv.visitLabel(label4);
mv.visitLineNumber(9, label4);
//var bsm = new Handle(Opcodes.H_INVOKESTATIC, "fr/umlv/nreflect/ReflectMirror", "bsm", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;", false);
//mv.visitLdcInsn(new ConstantDynamic("mh", "Ljava/lang/invoke/MethodHandle;", bsm));
var bsm = new Handle(Opcodes.H_INVOKESTATIC, "java/lang/invoke/MethodHandles", "classData", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;", false);
mv.visitLdcInsn(new ConstantDynamic("mh", "Ljava/lang/invoke/MethodHandle;", bsm));
mv.visitVarInsn(ASTORE, 3);
mv.visitLabel(label0);
mv.visitLineNumber(11, label0);
mv.visitVarInsn(ALOAD, 3);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invokeExact",
"(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", false);
mv.visitLabel(label1);
mv.visitInsn(ARETURN);
mv.visitLabel(label2);
mv.visitLineNumber(12, label2);
mv.visitFrame(Opcodes.F_FULL, 4, new Object[] { "fr/umlv/nreflect/ReflectMirrorImpl",
"java/lang/Object", "[Ljava/lang/Object;", "java/lang/invoke/MethodHandle" }, 1,
new Object[] { "java/lang/Throwable" });
mv.visitVarInsn(ASTORE, 4);
Label label5 = new Label();
mv.visitLabel(label5);
mv.visitLineNumber(13, label5);
mv.visitVarInsn(ALOAD, 4);
mv.visitInsn(ATHROW);
mv.visitLabel(label3);
mv.visitLineNumber(14, label3);
mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] { "java/lang/Throwable" });
mv.visitVarInsn(ASTORE, 4);
Label label6 = new Label();
mv.visitLabel(label6);
mv.visitLineNumber(15, label6);
mv.visitTypeInsn(NEW, "java/lang/reflect/InvocationTargetException");
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, 4);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/reflect/InvocationTargetException", "<init>",
"(Ljava/lang/Throwable;)V", false);
mv.visitInsn(ATHROW);
Label label7 = new Label();
mv.visitLabel(label7);
mv.visitLocalVariable("this", "Lfr/umlv/nreflect/ReflectMirrorImpl;", null, label4, label7, 0);
mv.visitLocalVariable("receiver", "Ljava/lang/Object;", null, label4, label7, 1);
mv.visitLocalVariable("args", "[Ljava/lang/Object;", null, label4, label7, 2);
mv.visitLocalVariable("mh", "Ljava/lang/invoke/MethodHandle;", null, label0, label7, 3);
mv.visitLocalVariable("e", "Ljava/lang/Throwable;", null, label5, label3, 4);
mv.visitLocalVariable("cause", "Ljava/lang/Throwable;", null, label6, label7, 4);
mv.visitMaxs(3, 5);
mv.visitEnd();
}
classWriter.visitEnd();
return classWriter.toByteArray();
}
public static void main(String[] args) throws Exception {
var bytes = dump();
var text = Base64.getEncoder().encodeToString(bytes);
System.err.println(text);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment