Created
May 17, 2022 22:30
-
-
Save DasBrain/914b4e00a9b714ca1ac4bb12ec176db5 to your computer and use it in GitHub Desktop.
This file contains hidden or 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 pw.dasbrain.methodhandle; | |
import java.io.IOException; | |
import java.lang.constant.MethodTypeDesc; | |
import java.lang.invoke.MethodHandle; | |
import java.lang.invoke.MethodHandles; | |
import java.lang.invoke.MethodHandles.Lookup; | |
import java.lang.invoke.MethodType; | |
import java.util.ArrayDeque; | |
import org.objectweb.asm.ClassReader; | |
import org.objectweb.asm.ClassVisitor; | |
import org.objectweb.asm.MethodVisitor; | |
import org.objectweb.asm.commons.AnalyzerAdapter; | |
import static org.objectweb.asm.Opcodes.*; | |
public class ParseMethod { | |
public static MethodHandle parseMethod(Lookup l, String name) throws IOException { | |
ClassReader cr = new ClassReader(l.lookupClass() | |
.getResourceAsStream(l.lookupClass().getSimpleName().replace('.', '/') + ".class")); | |
CV cv = new CV(l, name); | |
cr.accept(cv, 0); | |
return cv.result; | |
} | |
private static class CV extends ClassVisitor { | |
private final String name; | |
private final Lookup lookup; | |
private MethodHandle result; | |
CV(Lookup lookup, String name) { | |
super(ASM9, null); | |
this.name = name; | |
this.lookup = lookup; | |
} | |
@Override | |
public MethodVisitor visitMethod(int access, String name, String descriptor, | |
String signature, String[] exceptions) { | |
if (name.equals(this.name)) { | |
return new MV(this, lookup, | |
new AnalyzerAdapter(signature, access, name, descriptor, null)); | |
} | |
return null; | |
} | |
} | |
private static class MV extends MethodVisitor { | |
private final ArrayDeque<MethodHandle> stack = new ArrayDeque<>(); | |
private final Lookup lookup; | |
private final CV cv; | |
private final AnalyzerAdapter adapter; | |
private MV(CV cv, Lookup lookup, AnalyzerAdapter adapter) { | |
super(ASM9, adapter); | |
this.lookup = lookup; | |
this.cv = cv; | |
this.adapter = adapter; | |
} | |
@Override | |
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, | |
boolean isInterface) { | |
try { | |
Class<?> c = lookup.findClass(owner.replace('/', '.')); | |
MethodType mt = (MethodType) MethodTypeDesc.ofDescriptor(descriptor) | |
.resolveConstantDesc(lookup); | |
MethodHandle mh = switch (opcode) { | |
case INVOKESTATIC -> lookup.findStatic(c, name, mt); | |
default -> throw new IllegalStateException("Unknown opcode " + opcode); | |
}; | |
int count = mh.type().parameterCount(); | |
for (int i = count - 1; i >= 0; i--) { | |
mh = MethodHandles.collectArguments(mh, i, stack.pop()); | |
} | |
stack.push(mh); | |
} catch (ReflectiveOperationException roe) { | |
throw new RuntimeException(roe); | |
} | |
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); | |
} | |
@Override | |
public void visitInsn(int opcode) { | |
switch (opcode) { | |
case ARETURN -> { | |
cv.result = stack.pop(); | |
} | |
default -> throw new IllegalStateException("Not implemented: " + opcode); | |
} | |
super.visitInsn(opcode); | |
} | |
@Override | |
public void visitVarInsn(int opcode, int var) { | |
int count = 0, varIdx = -1; | |
for (int i = 0; i < adapter.locals.size(); i++) { | |
Object t = adapter.locals.get(i); | |
count++; | |
if (i == var) { | |
varIdx = count; | |
} | |
if (t == DOUBLE | t == LONG) { | |
i++; | |
} | |
} | |
int[] permArray = {varIdx}; | |
MethodHandle mh = MethodHandles.identity(Object.class); | |
MethodHandles.permuteArguments(mh, null, permArray); | |
super.visitVarInsn(opcode, var); | |
} | |
} | |
} |
This file contains hidden or 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 pw.dasbrain.methodhandle; | |
import java.lang.invoke.MethodHandle; | |
import java.lang.invoke.MethodHandles; | |
public class Test { | |
public static void main(String[] args) throws Throwable { | |
System.out.println(a()); | |
MethodHandle mh = ParseMethod.parseMethod(MethodHandles.lookup(), "a"); | |
System.out.println(mh); | |
System.out.println(mh.invokeExact()); | |
} | |
private static Object a() { | |
return b(c(d(), d()), c(d(), d())); | |
} | |
private static Object b(Object o1, Object o2) { | |
return "b(" + o1 + ", " + o2 + ")"; | |
} | |
private static Object c(Object o1, Object o2) { | |
return "c(" + o1 + ", " + o2 + ")"; | |
} | |
private static Object d() { | |
return "d()"; | |
} | |
private static Object e(Object o) { | |
return o; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment