Last active
November 22, 2019 21:20
-
-
Save forax/d0f0034190bc479b86ce977fb94ca176 to your computer and use it in GitHub Desktop.
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 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"); | |
} | |
} |
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
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); | |
// } | |
} |
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
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