Skip to content

Instantly share code, notes, and snippets.

@DasBrain
Created May 17, 2022 22:30
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 DasBrain/914b4e00a9b714ca1ac4bb12ec176db5 to your computer and use it in GitHub Desktop.
Save DasBrain/914b4e00a9b714ca1ac4bb12ec176db5 to your computer and use it in GitHub Desktop.
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);
}
}
}
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