mirror of
https://github.com/EsotericSoftware/reflectasm.git
synced 2025-03-09 16:50:16 +08:00
Fixed issue 8, support for inner classes.
This commit is contained in:
parent
4049c757c4
commit
45eff6c4f4
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user