diff --git a/pom.xml b/pom.xml index 658ddbe..eea9ba3 100644 --- a/pom.xml +++ b/pom.xml @@ -24,6 +24,11 @@ asm-tree 9.4 + + org.openjdk.jol + jol-core + 0.17 + diff --git a/src/main/java/com/github/shautvast/reflective/array/ArrayCreator.java b/src/main/java/com/github/shautvast/reflective/array/ArrayCreator.java new file mode 100644 index 0000000..54d62b6 --- /dev/null +++ b/src/main/java/com/github/shautvast/reflective/array/ArrayCreator.java @@ -0,0 +1,6 @@ +package com.github.shautvast.reflective.array; + +public abstract class ArrayCreator { + + public abstract Object newInstance(); +} diff --git a/src/main/java/com/github/shautvast/reflective/array/ArrayFactory.java b/src/main/java/com/github/shautvast/reflective/array/ArrayFactory.java new file mode 100644 index 0000000..0e4fb46 --- /dev/null +++ b/src/main/java/com/github/shautvast/reflective/array/ArrayFactory.java @@ -0,0 +1,75 @@ +package com.github.shautvast.reflective.array; + +import com.github.shautvast.reflective.java.ASM; +import com.github.shautvast.reflective.java.ByteClassLoader; +import com.github.shautvast.reflective.java.Java; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.tree.*; + +import java.io.FileOutputStream; +import java.io.IOException; + +import static org.objectweb.asm.Opcodes.*; + +public class ArrayFactory { + + /* + should become + public static T newArray(Class componentType) + or + public static T newArray(Class componentType, int... dimensions) + */ + public static Object newArray(Class componentType, int... dimensions) { + return newArray(componentType.getName(), dimensions); + } + + public static Object newArray(String componentType, int... dimensions) { + ClassNode classNode = ASM.createDefaultClassNode( + "ReflectiveCreator" + dimensions.length, + Java.internalName(ArrayCreator.class)); + MethodNode methodNode = new MethodNode(ACC_PUBLIC, + "newInstance", "()Ljava/lang/Object;", null, null); + classNode.methods.add(methodNode); + InsnList insns = methodNode.instructions; + + int localVarIndex = 0; + StringBuilder arrayType = new StringBuilder(dimensions.length); + String post = ""; + String pre = ""; + insns.add(new LdcInsnNode(1)); + insns.add(new LdcInsnNode(1)); + insns.add(new LdcInsnNode(1)); + insns.add(new MultiANewArrayInsnNode("[[[Ljava/lang/String;",3)); + +// for (int dim : dimensions) { +// insns.add(new LdcInsnNode(dim)); +// String type = arrayType + pre + Java.internalName(componentType) + post; +// insns.add(new TypeInsnNode(ANEWARRAY, type)); +// insns.add(new VarInsnNode(ASTORE, ++localVarIndex)); +// arrayType.append("["); +// pre = "L"; +// post = ";"; +// +// } + +// insns.add(new VarInsnNode(ALOAD, localVarIndex)); + insns.add(new InsnNode(ARETURN)); + ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + classNode.accept(classWriter); + byte[] byteArray = classWriter.toByteArray(); + + try (FileOutputStream out = new FileOutputStream("A.class")) { + out.write(byteArray); + } catch (IOException e) { + e.printStackTrace(); + } + + // load it into the JVM + ByteClassLoader.INSTANCE.addClass(classNode.name, byteArray); + try { + return ((ArrayCreator) ByteClassLoader.INSTANCE.loadClass(classNode.name).getConstructor().newInstance()).newInstance(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/com/github/shautvast/reflective/array/Arrays.java b/src/main/java/com/github/shautvast/reflective/array/Arrays.java new file mode 100644 index 0000000..6060468 --- /dev/null +++ b/src/main/java/com/github/shautvast/reflective/array/Arrays.java @@ -0,0 +1,17 @@ +package com.github.shautvast.reflective.array; + +public class Arrays { + private Arrays() { + } + + public static Object newInstance(Class componentType, int... dimensions) + throws IllegalArgumentException, NegativeArraySizeException { + return new String[dimensions[1]][2][3]; + } + + public static void main(String[] args) { + String[][][] a = (String[][][])newInstance(String.class, 1, 2, 3); + a[0]=new String[1][2]; + + } +} diff --git a/src/test/java/com/github/shautvast/reflective/ReflectiveTest.java b/src/test/java/com/github/shautvast/reflective/ReflectiveTest.java index 534de92..0291d99 100644 --- a/src/test/java/com/github/shautvast/reflective/ReflectiveTest.java +++ b/src/test/java/com/github/shautvast/reflective/ReflectiveTest.java @@ -54,7 +54,9 @@ public class ReflectiveTest { Dummy dummy = new Dummy("bar"); MetaMethod getName = Reflective.getMetaClass(dummy.getClass()).getMethod("getName").orElseGet(Assertions::fail); + // passing "foo" as the instance is not allowed assertThrows(Panic.class, () -> getName.invoke("foo").unwrap()); + // we should pass a valid dummy instance assertEquals("bar", getName.invoke(dummy).unwrap()); } diff --git a/src/test/java/com/github/shautvast/reflective/array/ArraysTest.java b/src/test/java/com/github/shautvast/reflective/array/ArraysTest.java new file mode 100644 index 0000000..f9d8a03 --- /dev/null +++ b/src/test/java/com/github/shautvast/reflective/array/ArraysTest.java @@ -0,0 +1,51 @@ +package com.github.shautvast.reflective.array; + +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Array; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ArraysTest { + + + @Test + void test1Dim() { + Object o = ArrayFactory.newArray(String.class, 1); + assertTrue(o instanceof String[]); + } + + @Test + void test2Dims() { + Object o = ArrayFactory.newArray(String.class, 1, 2); + assertTrue(o instanceof String[][]); + } + + @Test + void test3Dims() { + String[][][] array = (String[][][]) ArrayFactory.newArray(String[][][].class, 1, 2, 3); + assertEquals(1, array.length); + array[0] = new String[2][1]; + array[0][1] = new String[1]; + } + + @Test + void test3DimsT() { + String[][][] array = new String[1][1][1]; + assertEquals(1, array.length); + array[0] = new String[2][1]; + + array[0][0] = new String[1]; + array[0][1] = new String[1]; + array[0][0][0]="0,0,0"; + array[0][1][0]="0,1,0"; // WTF? + System.out.println(array[0][1][0]); + } + + @Test + void forName() throws ClassNotFoundException { + Class.forName("[Ljava.lang.String;"); + } + +}