From 45eff6c4f45b4d9801cf29090aa8a509b81c19fd Mon Sep 17 00:00:00 2001 From: Nathan Sweet Date: Thu, 14 Jun 2012 05:20:00 +0000 Subject: [PATCH] Fixed issue 8, support for inner classes. --- .../reflectasm/ConstructorAccess.java | 97 +++++++++++++------ .../reflectasm/MethodAccess.java | 62 ++++++------ 2 files changed, 98 insertions(+), 61 deletions(-) diff --git a/src/com/esotericsoftware/reflectasm/ConstructorAccess.java b/src/com/esotericsoftware/reflectasm/ConstructorAccess.java index bec4cdc..e334f28 100644 --- a/src/com/esotericsoftware/reflectasm/ConstructorAccess.java +++ b/src/com/esotericsoftware/reflectasm/ConstructorAccess.java @@ -9,21 +9,24 @@ import org.objectweb.asm.MethodVisitor; import static org.objectweb.asm.Opcodes.*; public abstract class ConstructorAccess { - static public ConstructorAccess get (Class type) { - try { - type.getConstructor((Class[])null); - } catch (Exception ex) { - if (type.isMemberClass() && !Modifier.isStatic(type.getModifiers())) - throw new RuntimeException("Class cannot be created (non-static member class): " + type.getName()); - else - throw new RuntimeException("Class cannot be created (missing no-arg constructor): " + type.getName()); - } + boolean isNonStaticMemberClass; + public boolean isNonStaticMemberClass () { + return isNonStaticMemberClass; + } + + abstract public T newInstance (); + + abstract public T newInstance (Object enclosingInstance); + + static public ConstructorAccess get (Class type) { AccessClassLoader loader = AccessClassLoader.get(type); String className = type.getName(); String accessClassName = className + "ConstructorAccess"; if (accessClassName.startsWith("java.")) accessClassName = "reflectasm." + accessClassName; + Class enclosingType = type.getEnclosingClass(); + boolean isNonStaticMemberClass = enclosingType != null && type.isMemberClass() && !Modifier.isStatic(type.getModifiers()); Class accessClass = null; try { accessClass = loader.loadClass(accessClassName); @@ -32,40 +35,74 @@ public abstract class ConstructorAccess { if (accessClass == null) { String accessClassNameInternal = accessClassName.replace('.', '/'); String classNameInternal = className.replace('.', '/'); + String enclosingClassNameInternal = isNonStaticMemberClass ? enclosingType.getName().replace('.', '/') : null; ClassWriter cw = new ClassWriter(0); cw.visit(V1_1, ACC_PUBLIC + ACC_SUPER, accessClassNameInternal, null, "com/esotericsoftware/reflectasm/ConstructorAccess", null); MethodVisitor mv; - { - mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); - mv.visitCode(); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, "com/esotericsoftware/reflectasm/ConstructorAccess", "", "()V"); - mv.visitInsn(RETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - { - mv = cw.visitMethod(ACC_PUBLIC, "newInstance", "()Ljava/lang/Object;", null, null); - mv.visitCode(); - mv.visitTypeInsn(NEW, classNameInternal); - mv.visitInsn(DUP); - mv.visitMethodInsn(INVOKESPECIAL, classNameInternal, "", "()V"); - mv.visitInsn(ARETURN); - mv.visitMaxs(2, 1); - mv.visitEnd(); - } + + insertConstructor(cw); + insertNewInstance(cw, classNameInternal); + insertNewInstanceInner(cw, classNameInternal, enclosingClassNameInternal); + cw.visitEnd(); byte[] data = cw.toByteArray(); accessClass = loader.defineClass(accessClassName, data); } try { - return (ConstructorAccess)accessClass.newInstance(); + ConstructorAccess access = (ConstructorAccess)accessClass.newInstance(); + access.isNonStaticMemberClass = isNonStaticMemberClass; + return access; } catch (Exception ex) { throw new RuntimeException("Error constructing constructor access class: " + accessClassName, ex); } } - abstract public T newInstance (); + static private void insertConstructor (ClassWriter cw) { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "com/esotericsoftware/reflectasm/ConstructorAccess", "", "()V"); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + + static void insertNewInstance (ClassWriter cw, String classNameInternal) { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "newInstance", "()Ljava/lang/Object;", null, null); + mv.visitCode(); + mv.visitTypeInsn(NEW, classNameInternal); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, classNameInternal, "", "()V"); + mv.visitInsn(ARETURN); + mv.visitMaxs(2, 1); + mv.visitEnd(); + } + + static void insertNewInstanceInner (ClassWriter cw, String classNameInternal, String enclosingClassNameInternal) { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "newInstance", "(Ljava/lang/Object;)Ljava/lang/Object;", null, null); + mv.visitCode(); + + if (enclosingClassNameInternal != null) { + mv.visitTypeInsn(NEW, classNameInternal); + mv.visitInsn(DUP); + mv.visitVarInsn(ALOAD, 1); + mv.visitTypeInsn(CHECKCAST, enclosingClassNameInternal); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;"); + mv.visitInsn(POP); + mv.visitMethodInsn(INVOKESPECIAL, classNameInternal, "", "(L" + enclosingClassNameInternal + ";)V"); + mv.visitInsn(ARETURN); + mv.visitMaxs(4, 2); + } else { + mv.visitTypeInsn(NEW, classNameInternal); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, classNameInternal, "", "()V"); + mv.visitInsn(ARETURN); + mv.visitMaxs(2, 2); + } + + mv.visitEnd(); + } } diff --git a/src/com/esotericsoftware/reflectasm/MethodAccess.java b/src/com/esotericsoftware/reflectasm/MethodAccess.java index ee77e74..da47420 100644 --- a/src/com/esotericsoftware/reflectasm/MethodAccess.java +++ b/src/com/esotericsoftware/reflectasm/MethodAccess.java @@ -15,6 +15,37 @@ import org.objectweb.asm.Type; import static org.objectweb.asm.Opcodes.*; public abstract class MethodAccess { + private String[] methodNames; + private Class[][] parameterTypes; + + abstract public Object invoke (Object object, int methodIndex, Object... args); + + /** Invokes the first method with the specified name. */ + public Object invoke (Object object, String methodName, Object... args) { + return invoke(object, getIndex(methodName), args); + } + + /** Returns the index of the first method with the specified name. */ + public int getIndex (String methodName) { + for (int i = 0, n = methodNames.length; i < n; i++) + if (methodNames[i].equals(methodName)) return i; + throw new IllegalArgumentException("Unable to find public method: " + methodName); + } + + public int getIndex (String methodName, Class... paramTypes) { + for (int i = 0, n = methodNames.length; i < n; i++) + if (methodNames[i].equals(methodName) && Arrays.equals(paramTypes, parameterTypes[i])) return i; + throw new IllegalArgumentException("Unable to find public method: " + methodName + " " + Arrays.toString(parameterTypes)); + } + + public String[] getMethodNames () { + return methodNames; + } + + public Class[][] getParameterTypes () { + return parameterTypes; + } + static public MethodAccess get (Class type) { AccessClassLoader loader = AccessClassLoader.get(type); @@ -211,35 +242,4 @@ public abstract class MethodAccess { throw new RuntimeException("Error constructing method access class: " + accessClassName, ex); } } - - private String[] methodNames; - private Class[][] parameterTypes; - - abstract public Object invoke (Object object, int methodIndex, Object... args); - - /** Invokes the first method with the specified name. */ - public Object invoke (Object object, String methodName, Object... args) { - return invoke(object, getIndex(methodName), args); - } - - /** Returns the index of the first method with the specified name. */ - public int getIndex (String methodName) { - for (int i = 0, n = methodNames.length; i < n; i++) - if (methodNames[i].equals(methodName)) return i; - throw new IllegalArgumentException("Unable to find public method: " + methodName); - } - - public int getIndex (String methodName, Class... paramTypes) { - for (int i = 0, n = methodNames.length; i < n; i++) - if (methodNames[i].equals(methodName) && Arrays.equals(paramTypes, parameterTypes[i])) return i; - throw new IllegalArgumentException("Unable to find public method: " + methodName + " " + Arrays.toString(parameterTypes)); - } - - public String[] getMethodNames () { - return methodNames; - } - - public Class[][] getParameterTypes () { - return parameterTypes; - } }