diff --git a/pom.xml b/pom.xml
index eea9ba3..2f4d736 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
com.github.shautvast
reflective
- 1.0.0
+ 1.1.0
reflective
Reflective utils that don't use java.lang.reflect
@@ -29,6 +29,12 @@
jol-core
0.17
+
+ org.projectlombok
+ lombok
+ 1.18.26
+ test
+
diff --git a/src/main/java/com/github/shautvast/reflective/InvokerFactory.java b/src/main/java/com/github/shautvast/reflective/InvokerFactory.java
index bb08a32..c120c9f 100644
--- a/src/main/java/com/github/shautvast/reflective/InvokerFactory.java
+++ b/src/main/java/com/github/shautvast/reflective/InvokerFactory.java
@@ -9,11 +9,11 @@ import org.objectweb.asm.tree.*;
import java.lang.reflect.InvocationTargetException;
-import static com.github.shautvast.reflective.java.Java.*;
import static com.github.shautvast.reflective.java.Java.DOUBLE;
import static com.github.shautvast.reflective.java.Java.FLOAT;
import static com.github.shautvast.reflective.java.Java.INTEGER;
import static com.github.shautvast.reflective.java.Java.LONG;
+import static com.github.shautvast.reflective.java.Java.*;
import static com.github.shautvast.rusty.Result.err;
import static com.github.shautvast.rusty.Result.ok;
import static org.objectweb.asm.Opcodes.*;
@@ -58,12 +58,13 @@ public class InvokerFactory {
}
// put argument on the stack
insns.add(new InsnNode(AALOAD));
- insns.add(new TypeInsnNode(CHECKCAST, method.getParameters().get(i).getDescriptor()));
+ insns.add(new TypeInsnNode(CHECKCAST, Java.internalName(mapPrimitiveToWrapper(method.getParameters().get(i).getType()).getName())));
+ unwrapPrimitive(method.getParameters().get(i), insns);
}
// call the method
insns.add(new MethodInsnNode(INVOKEVIRTUAL, method.getMetaClass().getRawName(), method.getName(), method.getDescriptor()));
// if the returned argument is primitive, wrap it as an Object
- wrapAnyPrimitivesForReturnObject(method, invokerMethod);
+ wrapPrimitive(method.getReturnParameter(), invokerMethod.instructions);
// return the object on the stack
insns.add(new InsnNode(ARETURN));
@@ -75,7 +76,7 @@ public class InvokerFactory {
classNode.accept(classWriter);
byte[] byteArray = classWriter.toByteArray();
-// try (FileOutputStream out = new FileOutputStream("C.class")) {
+// try (FileOutputStream out = new FileOutputStream("C"+method.getName() + ".class")) {
// out.write(byteArray);
// } catch (IOException e) {
// e.printStackTrace();
@@ -95,12 +96,39 @@ public class InvokerFactory {
}
}
+ private static void unwrapPrimitive(Parameter> parameter, InsnList insns) {
+ switch (parameter.getDescriptor()) {
+ case "B":
+ insns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B"));
+ break;
+ case "S":
+ insns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S"));
+ break;
+ case "I":
+ insns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I"));
+ break;
+ case "Z":
+ insns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z"));
+ break;
+ case "J":
+ insns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J"));
+ break;
+ case "F":
+ insns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F"));
+ break;
+ case "D":
+ insns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D"));
+ break;
+ case "C":
+ insns.add(new MethodInsnNode(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C"));
+ }
+ }
+
/*
* wrap primitives in their wrapper
*/
- private static void wrapAnyPrimitivesForReturnObject(MetaMethod m, MethodNode invokerMethod) {
- InsnList insns = invokerMethod.instructions;
- switch (m.getReturnParameter().getDescriptor()) {
+ private static void wrapPrimitive(Parameter> parameter, InsnList insns) {
+ switch (parameter.getDescriptor()) {
case "V":
insns.add(new InsnNode(ACONST_NULL));
break;
@@ -126,10 +154,32 @@ public class InvokerFactory {
insns.add(new MethodInsnNode(INVOKESTATIC, SHORT, VALUEOF, "(S)Ljava/lang/Short;"));
break;
case "B":
- insns.add(new MethodInsnNode(INVOKESTATIC, "java/lang/Byte", VALUEOF, "(B)Ljava/lang/Byte;"));
+ insns.add(new MethodInsnNode(INVOKESTATIC, BYTE, VALUEOF, "(B)Ljava/lang/Byte;"));
break;
}
}
+ private static Class> mapPrimitiveToWrapper(Class> type) {
+ if (type == int.class) {
+ return Integer.class;
+ } else if (type == byte.class) {
+ return Byte.class;
+ } else if (type == short.class) {
+ return Short.class;
+ } else if (type == long.class) {
+ return Long.class;
+ } else if (type == float.class) {
+ return Float.class;
+ } else if (type == double.class) {
+ return Double.class;
+ } else if (type == char.class) {
+ return Character.class;
+ } else if (type == boolean.class) {
+ return Boolean.class;
+ } else {
+ return type;
+ }
+ }
+
}
diff --git a/src/main/java/com/github/shautvast/reflective/MetaClass.java b/src/main/java/com/github/shautvast/reflective/MetaClass.java
index 4a25aeb..0dfd467 100644
--- a/src/main/java/com/github/shautvast/reflective/MetaClass.java
+++ b/src/main/java/com/github/shautvast/reflective/MetaClass.java
@@ -34,6 +34,7 @@ public class MetaClass {
return Set.copyOf(methods.values());
}
+ // should account for overloading!
public Optional getMethod(String name) {
return Optional.ofNullable(methods.get(name));
}
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 16c9047..d440b41 100644
--- a/src/main/java/com/github/shautvast/reflective/java/Java.java
+++ b/src/main/java/com/github/shautvast/reflective/java/Java.java
@@ -22,6 +22,7 @@ public class Java {
public static final String DOUBLE = "java/lang/Double";
public static final String CHAR = "java/lang/Character";
public static final String SHORT = "java/lang/Short";
+ public static final String BYTE = "java/lang/Byte";
public static String internalName(String className) {
return className.replaceAll("\\.", "/");
diff --git a/src/test/java/com/github/shautvast/reflective/DummyInvoker.java b/src/test/java/com/github/shautvast/reflective/DummyInvoker.java
index fcaa43e..072d33d 100644
--- a/src/test/java/com/github/shautvast/reflective/DummyInvoker.java
+++ b/src/test/java/com/github/shautvast/reflective/DummyInvoker.java
@@ -1,9 +1,10 @@
package com.github.shautvast.reflective;
+@SuppressWarnings("ALL")
public class DummyInvoker extends AbstractInvoker {
public Object invoke(Object instance, Object... arguments) {
- ((ReflectiveTest.Dummy) instance).setName((String)arguments[0]);
+ ((ReflectiveTest.Dummy) instance).setIntValue((int)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 0291d99..5a32924 100644
--- a/src/test/java/com/github/shautvast/reflective/ReflectiveTest.java
+++ b/src/test/java/com/github/shautvast/reflective/ReflectiveTest.java
@@ -2,11 +2,13 @@ package com.github.shautvast.reflective;
import com.github.shautvast.rusty.Panic;
import com.github.shautvast.rusty.Result;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Modifier;
-import java.util.Iterator;
import java.util.List;
import java.util.Set;
@@ -16,17 +18,13 @@ public class ReflectiveTest {
@Test
void testMethods() {
- Dummy dummy = new Dummy("bar");
+ Dummy dummy = new Dummy((byte) 42, (short) 43, 44, 45, 46.0F, 47.0, 'D', true, "don't panic!");
MetaClass metaDummy = Reflective.getMetaClass(dummy.getClass());
assertEquals("com.github.shautvast.reflective.ReflectiveTest$Dummy", metaDummy.getName());
- Iterator fields = metaDummy.getFields().iterator();
- assertTrue(fields.hasNext());
- assertEquals("name", fields.next().getName());
-
Set methods = metaDummy.getMethods();
assertFalse(methods.isEmpty());
- assertEquals(6, methods.size());
+ assertEquals(22, methods.size());
MetaMethod equals = metaDummy.getMethod("equals").orElseGet(Assertions::fail);
assertEquals(boolean.class, equals.getReturnParameter().getType());
@@ -37,10 +35,10 @@ public class ReflectiveTest {
assertEquals(int.class, hashCode.getReturnParameter().getType());
assertTrue(Modifier.isPublic(hashCode.getModifiers()));
- MetaMethod getName = metaDummy.getMethod("getName").orElseGet(Assertions::fail);
- assertEquals(List.of(), getName.getParameters());
- assertEquals(String.class, getName.getReturnParameter().getType());
- assertTrue(Modifier.isPublic(getName.getModifiers()));
+ MetaMethod getStringValue = metaDummy.getMethod("getStringValue").orElseGet(Assertions::fail);
+ assertEquals(List.of(), getStringValue.getParameters());
+ assertEquals(String.class, getStringValue.getReturnParameter().getType());
+ assertTrue(Modifier.isPublic(getStringValue.getModifiers()));
MetaMethod privateMethod = metaDummy.getMethod("privateMethod").orElseGet(Assertions::fail);
assertEquals(List.of(), privateMethod.getParameters());
@@ -51,29 +49,60 @@ public class ReflectiveTest {
@Test
void testInvokeGetter() {
- Dummy dummy = new Dummy("bar");
- MetaMethod getName = Reflective.getMetaClass(dummy.getClass()).getMethod("getName").orElseGet(Assertions::fail);
+ Dummy dummy = new Dummy((byte) 42, (short) 43, 44, 45, 46.0F, 47.0, 'D', true, "don't panic!");
+ MetaMethod getStringValue = Reflective.getMetaClass(dummy.getClass()).getMethod("getStringValue").orElseGet(Assertions::fail);
// passing "foo" as the instance is not allowed
- assertThrows(Panic.class, () -> getName.invoke("foo").unwrap());
+ assertThrows(Panic.class, () -> getStringValue.invoke("foo").unwrap());
// we should pass a valid dummy instance
- assertEquals("bar", getName.invoke(dummy).unwrap());
+ assertEquals("don't panic!", getStringValue.invoke(dummy).unwrap());
}
@Test
- void testInvokeSetter() {
- Dummy dummy = new Dummy("bar");
+ void testInvokeSetters() {
+ Dummy dummy = new Dummy((byte) 42, (short) 43, 44, 45, 46.0F, 47.0, 'D', true, "don't panic!");
MetaClass metaForClass = Reflective.getMetaClass(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
+ MetaMethod setByte = metaForClass.getMethod("setByteValue").orElseGet(Assertions::fail);
+ setByte.invoke(dummy, (byte) -42).unwrap();
+ assertEquals((byte) -42, dummy.getByteValue());
+
+ MetaMethod setShort = metaForClass.getMethod("setShortValue").orElseGet(Assertions::fail);
+ setShort.invoke(dummy, (short) -43).unwrap();
+ assertEquals((short) -43, dummy.getShortValue());
+
+ MetaMethod setInt = metaForClass.getMethod("setIntValue").orElseGet(Assertions::fail);
+ setInt.invoke(dummy, -44).unwrap();
+ assertEquals(-44, dummy.getIntValue());
+
+ MetaMethod setLongValue = metaForClass.getMethod("setLongValue").orElseGet(Assertions::fail);
+ setLongValue.invoke(dummy, -45L).unwrap();
+ assertEquals(-45L, dummy.getLongValue());
+
+ MetaMethod setFloat = metaForClass.getMethod("setFloatValue").orElseGet(Assertions::fail);
+ setFloat.invoke(dummy, -46.0F).unwrap();
+ assertEquals(-46.0F, dummy.getFloatValue());
+
+ MetaMethod setDouble = metaForClass.getMethod("setDoubleValue").orElseGet(Assertions::fail);
+ setDouble.invoke(dummy, -47.0).unwrap();
+ assertEquals(-47.0, dummy.getDoubleValue());
+
+ MetaMethod setChar = metaForClass.getMethod("setCharValue").orElseGet(Assertions::fail);
+ setChar.invoke(dummy, '-').unwrap();
+ assertEquals('-', dummy.getCharValue());
+
+ MetaMethod setBoolean = metaForClass.getMethod("setBooleanValue").orElseGet(Assertions::fail);
+ setBoolean.invoke(dummy, false).unwrap();
+ assertFalse(dummy.isBooleanValue());
+
+ MetaMethod setString = metaForClass.getMethod("setStringValue").orElseGet(Assertions::fail);
+ setString.invoke(dummy, "panic!").unwrap();
+ assertEquals("panic!", dummy.getStringValue());
}
@Test
void testInvocationExceptionHappened() {
- Dummy dummy = new Dummy("bar");
+ Dummy dummy = new Dummy((byte) 42, (short) 43, 44, 45, 46.0F, 47.0, 'D', true, "don't panic!");
MetaClass metaForClass = Reflective.getMetaClass(dummy.getClass());
MetaMethod throwEx = metaForClass.getMethod("throwEx").orElseGet(Assertions::fail);
@@ -81,25 +110,19 @@ public class ReflectiveTest {
assertFalse(result.isOk());
}
-
+ @Getter
+ @Setter
+ @AllArgsConstructor
public static class Dummy {
- private String name;
-
- public Dummy(String name) {
- this.name = name;
- }
-
- public String getName() {
- return privateMethod()[0];
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public void throwEx() throws Exception {
- throw new Exception("something must have gone wrong");
- }
+ private byte byteValue;
+ private short shortValue;
+ private int intValue;
+ private long longValue;
+ private float floatValue;
+ private double doubleValue;
+ private char charValue;
+ private boolean booleanValue;
+ private String stringValue;
@Override
public boolean equals(Object obj) {
@@ -111,8 +134,15 @@ public class ReflectiveTest {
return 6;
}
- private String[] privateMethod() {
- return new String[]{name, "bar"};
+ @SuppressWarnings("unused")
+ private String[] privateMethod(){
+ return new String[1];
}
+
+ @SuppressWarnings("unused")
+ public void throwEx() {
+ throw new RuntimeException("ex");
+ }
+
}
}