Fixed issue 8, support for inner classes.

This commit is contained in:
Nathan Sweet 2012-06-14 05:20:00 +00:00
parent 4049c757c4
commit 45eff6c4f4
2 changed files with 98 additions and 61 deletions

View File

@ -9,21 +9,24 @@ import org.objectweb.asm.MethodVisitor;
import static org.objectweb.asm.Opcodes.*;
public abstract class ConstructorAccess<T> {
static public <T> ConstructorAccess<T> get (Class<T> 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 <T> ConstructorAccess<T> get (Class<T> 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<T> {
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, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "com/esotericsoftware/reflectasm/ConstructorAccess", "<init>", "()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, "<init>", "()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<T> access = (ConstructorAccess<T>)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, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "com/esotericsoftware/reflectasm/ConstructorAccess", "<init>", "()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, "<init>", "()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, "<init>", "(L" + enclosingClassNameInternal + ";)V");
mv.visitInsn(ARETURN);
mv.visitMaxs(4, 2);
} else {
mv.visitTypeInsn(NEW, classNameInternal);
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, classNameInternal, "<init>", "()V");
mv.visitInsn(ARETURN);
mv.visitMaxs(2, 2);
}
mv.visitEnd();
}
}

View File

@ -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;
}
}