diff --git a/src/main/java/com/github/shautvast/reflective/InvokerFactory.java b/src/main/java/com/github/shautvast/reflective/InvokerFactory.java index 1301ef9..2cc2d5f 100644 --- a/src/main/java/com/github/shautvast/reflective/InvokerFactory.java +++ b/src/main/java/com/github/shautvast/reflective/InvokerFactory.java @@ -21,13 +21,27 @@ public class InvokerFactory { public static final String SUPER = Java.internalName(AbstractInvoker.class); public static Result of(MetaMethod m) { - ClassNode classNode = ASM.createDefaultClassNode("Invoker" + UUID.randomUUID(), SUPER); + ClassNode classNode = ASM.createDefaultClassNode("Invoker" + m.getName() + m.getDescriptor().replaceAll("[()/;\\[]", ""), SUPER); MethodNode invokerMethod = new MethodNode(ACC_PUBLIC, "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", null, null); invokerMethod.instructions.add(new VarInsnNode(ALOAD, 1)); invokerMethod.instructions.add(new TypeInsnNode(CHECKCAST, m.getMetaClass().getRawName())); + for (int i = 0; i < m.getParameters().size(); i++) { + invokerMethod.instructions.add(new VarInsnNode(ALOAD, 2)); + if (i < 6) { + invokerMethod.instructions.add(new InsnNode(3 + i)); //ICONST_X + } else { + invokerMethod.instructions.add(new LdcInsnNode(i)); + } + invokerMethod.instructions.add(new InsnNode(AALOAD)); + invokerMethod.instructions.add(new TypeInsnNode(CHECKCAST, m.getParameters().get(i).getDescriptor())); + } + ; invokerMethod.instructions.add(new MethodInsnNode(INVOKEVIRTUAL, m.getMetaClass().getRawName(), m.getName(), m.getDescriptor())); + + convertReturnTypeForPrimitives(m, invokerMethod); + invokerMethod.instructions.add(new InsnNode(ARETURN)); classNode.methods.add(invokerMethod); @@ -35,11 +49,11 @@ public class InvokerFactory { classNode.accept(classWriter); byte[] byteArray = classWriter.toByteArray(); - try (FileOutputStream out = new FileOutputStream("C.class")) { - out.write(byteArray); - } catch (IOException e) { - e.printStackTrace(); - } +// try (FileOutputStream out = new FileOutputStream("C.class")) { +// out.write(byteArray); +// } catch (IOException e) { +// e.printStackTrace(); +// } ByteClassLoader.INSTANCE.addClass(classNode.name, byteArray); try { @@ -50,5 +64,37 @@ public class InvokerFactory { } } + private static void convertReturnTypeForPrimitives(MetaMethod m, MethodNode invokerMethod) { + switch (m.getReturnParameter().getDescriptor()) { + case "V": + invokerMethod.instructions.add(new InsnNode(ACONST_NULL)); + break; + case "Z": + invokerMethod.instructions.add(new MethodInsnNode(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;")); + break; + case "I": + invokerMethod.instructions.add(new MethodInsnNode(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;")); + break; + case "J": + invokerMethod.instructions.add(new MethodInsnNode(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;")); + break; + case "F": + invokerMethod.instructions.add(new MethodInsnNode(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;")); + break; + case "D": + invokerMethod.instructions.add(new MethodInsnNode(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;")); + break; + case "C": + invokerMethod.instructions.add(new MethodInsnNode(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;")); + break; + case "S": + invokerMethod.instructions.add(new MethodInsnNode(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;")); + break; + case "B": + invokerMethod.instructions.add(new MethodInsnNode(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;")); + break; + } + } + } diff --git a/src/main/java/com/github/shautvast/reflective/MetaMethod.java b/src/main/java/com/github/shautvast/reflective/MetaMethod.java index f217a4f..b3d7f9c 100644 --- a/src/main/java/com/github/shautvast/reflective/MetaMethod.java +++ b/src/main/java/com/github/shautvast/reflective/MetaMethod.java @@ -3,8 +3,9 @@ package com.github.shautvast.reflective; import com.github.shautvast.reflective.java.Java; import com.github.shautvast.rusty.Result; +import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedList; import java.util.List; import static com.github.shautvast.rusty.Result.err; @@ -15,16 +16,21 @@ public class MetaMethod { private final String name; private final int modifiers; private final String descriptor; - private List> parameters = new LinkedList<>(); - private Class returnParameter; - private final AbstractInvoker invoker = InvokerFactory.of(this).unwrapOr(() -> null); + private final List> parameters; + private Parameter returnParameter; + private final AbstractInvoker invoker; public MetaMethod(MetaClass metaClass, String methodname, int modifiers, String descriptor) { this.metaClass = metaClass; this.name = methodname; this.modifiers = modifiers; this.descriptor = descriptor; - getParameters(descriptor); + this.parameters = createParameters(descriptor); + if (!Modifier.isPrivate(modifiers)) { + invoker = InvokerFactory.of(this).unwrap();//TODO + } else { + invoker = null; + } } public String getName() { @@ -43,11 +49,11 @@ public class MetaMethod { return descriptor; } - public List> getParameters() { + public List> getParameters() { return parameters; } - public Class getReturnParameter() { + public Parameter getReturnParameter() { return returnParameter; } @@ -65,24 +71,39 @@ public class MetaMethod { } } - private void getParameters(String descriptor) { + private List> createParameters(String descriptor) { + List> mutableParams = new ArrayList<>(); String[] split = descriptor.split("[()]"); String parms = split[1]; StringBuilder buf = new StringBuilder(); for (int i = 0; i < parms.length(); i++) { String t = Character.toString(parms.charAt(i)); if (!"L".equals(t) && buf.length() == 0) { - this.parameters.add(Java.getClassFromDescriptor(t)); + mutableParams.add(new Parameter<>(Java.getClassFromDescriptor(t), t)); } if (";".equals(t)) { buf.append(t); - this.parameters.add(Java.getClassFromDescriptor(buf.toString())); + String desc = buf.toString(); + mutableParams.add(new Parameter<>(Java.getClassFromDescriptor(desc), correct(desc))); buf = new StringBuilder(); } else { buf.append(t); } } - this.parameters = Collections.unmodifiableList(this.parameters); //effectively final - this.returnParameter = Java.getClassFromDescriptor(split[2]); + + String returnDesc = split[2]; + this.returnParameter = new Parameter<>( + Java.getClassFromDescriptor(returnDesc), + correct(returnDesc)); + + return Collections.unmodifiableList(mutableParams); + } + + private static String correct(String returnDesc) { + if (returnDesc.startsWith("L")) { + return returnDesc.substring(1, returnDesc.length() - 1); + } else { + return returnDesc; + } } } diff --git a/src/main/java/com/github/shautvast/reflective/Parameter.java b/src/main/java/com/github/shautvast/reflective/Parameter.java new file mode 100644 index 0000000..85408f7 --- /dev/null +++ b/src/main/java/com/github/shautvast/reflective/Parameter.java @@ -0,0 +1,27 @@ +package com.github.shautvast.reflective; + +public class Parameter { + private final Class type; + private final String descriptor; + + public Parameter(Class type, String descriptor) { + this.type = type; + this.descriptor = descriptor; + } + + public Class getType() { + return type; + } + + public boolean isVoid(){ + return type == Void.class; + } + + public boolean isPrimitive(){ + return !descriptor.startsWith("L"); + } + + public String getDescriptor() { + return descriptor; + } +} diff --git a/src/main/java/com/github/shautvast/reflective/java/Java.java b/src/main/java/com/github/shautvast/reflective/java/Java.java index 8338c8e..53109d6 100644 --- a/src/main/java/com/github/shautvast/reflective/java/Java.java +++ b/src/main/java/com/github/shautvast/reflective/java/Java.java @@ -136,6 +136,8 @@ public class Java { return isArray ? newInstance(short.class, new int[arrayDims]).getClass() : short.class; case 'Z': return isArray ? newInstance(boolean.class, new int[arrayDims]).getClass() : boolean.class; + case 'V': + return Void.class; default: throw new RuntimeException(new ClassNotFoundException("unknown descriptor: " + descriptor)); //must not happen } diff --git a/src/test/java/com/github/shautvast/reflective/DummyInvoker.java b/src/test/java/com/github/shautvast/reflective/DummyInvoker.java index fa13fe6..fcaa43e 100644 --- a/src/test/java/com/github/shautvast/reflective/DummyInvoker.java +++ b/src/test/java/com/github/shautvast/reflective/DummyInvoker.java @@ -3,6 +3,7 @@ package com.github.shautvast.reflective; public class DummyInvoker extends AbstractInvoker { public Object invoke(Object instance, Object... arguments) { - return ((ReflectiveTest.Dummy) instance).getName(); + ((ReflectiveTest.Dummy) instance).setName((String)arguments[0]); + return null; } } diff --git a/src/test/java/com/github/shautvast/reflective/ReflectiveTest.java b/src/test/java/com/github/shautvast/reflective/ReflectiveTest.java index ebf7ac3..8abff2d 100644 --- a/src/test/java/com/github/shautvast/reflective/ReflectiveTest.java +++ b/src/test/java/com/github/shautvast/reflective/ReflectiveTest.java @@ -50,7 +50,7 @@ public class ReflectiveTest { @Test - void testInvocation() { + void testInvokeGetter() { Dummy dummy = new Dummy("bar"); MetaMethod getName = Reflective.getMetaForClass(dummy.getClass()).getMethod("getName").orElseGet(Assertions::fail); @@ -58,9 +58,20 @@ public class ReflectiveTest { assertEquals("bar", getName.invoke(dummy).unwrap()); } + @Test + void testInvokeSetter() { + Dummy dummy = new Dummy("bar"); + MetaClass metaForClass = Reflective.getMetaForClass(dummy.getClass()); + MetaMethod setName = metaForClass.getMethod("setName").orElseGet(Assertions::fail); + + assertEquals("bar", dummy.getName()); // before invoke + setName.invoke(dummy, "foo"); + assertEquals("foo", dummy.getName()); // after invoke + } + public static class Dummy { - private final String name; + private String name; public Dummy(String name) { this.name = name; @@ -70,6 +81,10 @@ public class ReflectiveTest { return privateMethod()[0]; } + public void setName(String name) { + this.name = name; + } + @Override public boolean equals(Object obj) { return obj instanceof Dummy;