Skip to content

Instantly share code, notes, and snippets.

@melix
Created February 1, 2012 15:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save melix/1717426 to your computer and use it in GitHub Desktop.
Save melix/1717426 to your computer and use it in GitHub Desktop.
Proxy generator
/*
* Copyright 2003-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package groovy.util;
import groovy.lang.Closure;
import groovy.lang.GroovyObject;
import groovy.lang.GroovyRuntimeException;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.classgen.asm.BytecodeHelper;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import org.objectweb.asm.*;
import org.objectweb.asm.commons.EmptyVisitor;
import org.objectweb.asm.util.CheckClassAdapter;
import org.objectweb.asm.util.TraceMethodVisitor;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* A proxy generator responsible for mapping a map of closures to a class implementing a list of interfaces. For
* example, the following code:
* <pre>
* abstract class Foo {
* abstract void bar();
* abstract void baz();
* }
* def dyn = [bar: { println 'hello' }, baz: { println 'world'}] as Foo
* </pre>
* will generate a proxy class which extends class <i>Foo</i> and delegates method calls to the provided closures.
*
* The generated proxy implements the {@link GroovyObject} interface.
*
* @author Cedric Champeau
*/
public class ProxyGeneratorAdapter extends ClassAdapter implements Opcodes {
private static final Map<String, DelegateClosure> EMPTY_CLOSURE_MAP = Collections.emptyMap();
private final static AtomicLong pxyCounter = new AtomicLong();
private static final Set<String> GROOVYOBJECT_METHODS;
private static final String WILDCARD = "*";
private static final String WILDCARD_FIELD = "$wildcard";
private static final Object[] EMPTY_ARGS = new Object[0];
static {
List<String> names = new ArrayList<String>();
for (Method method : GroovyObject.class.getMethods()) {
names.add(method.getName());
}
GROOVYOBJECT_METHODS = new HashSet<String>(names);
}
private final Class superClass;
private final InnerLoader loader;
private final String proxyName;
private final List<Class> classList;
private final Map<String, DelegateClosure> closureMap;
private boolean addGroovyObjectSupport = false;
// if emptyBody == true, then we generate an empty body instead throwing error on unimplemented methods
private final boolean emptyBody;
private final boolean hasWildcard;
private final Set<Object> visitedMethods = new LinkedHashSet<Object>();
// cached class
private final Class cachedClass;
private final Constructor cachedNoArgConstructor;
private final Object[] cachedClosures;
/**
* Construct a proxy generator. This generator is used when we need to create a proxy object for a class or an
* interface given a map of closures.
*
* @param closureMap the delegates implementations
* @param superClass corresponding to the superclass class visitor
* @param interfaces extra interfaces the proxy should implement
* @param proxyLoader the class loader which should be used to load the generated proxy
*/
public ProxyGeneratorAdapter(final Map<Object, Object> closureMap, final Class superClass, final Class[] interfaces, ClassLoader proxyLoader, boolean emptyBody) {
super(new ClassWriter(0));
this.closureMap = closureMap.isEmpty()?EMPTY_CLOSURE_MAP:new TreeMap<String, DelegateClosure>();
boolean wildcard = false;
for (Map.Entry<Object, Object> entry : closureMap.entrySet()) {
final Object value = entry.getValue();
Closure cl = value instanceof Closure?(Closure)value:new Closure(null) {
@Override
public Object call(final Object... args) {
// if the supplied value in the map is not a closure, then this value is wrapped in a closure
return value;
}
};
String name = entry.getKey().toString();
if ("*".equals(name)) {
name= WILDCARD_FIELD;
wildcard = true;
}
this.closureMap.put(findFieldName(entry.getKey()), new DelegateClosure(name,cl));
}
this.hasWildcard = wildcard;
// a proxy is supposed to be a concrete class, so it cannot extend an interface.
// If the provided superclass is an interface, then we replace the superclass with Object
// and add this interface to the list of implemented interfaces
boolean isSuperClassAnInterface = superClass.isInterface();
this.superClass = isSuperClassAnInterface ?Object.class:superClass;
// create the base list of classes which have possible methods to be overloaded
this.classList = new LinkedList<Class>();
this.classList.add(superClass);
if (interfaces!=null) {
Collections.addAll(this.classList, interfaces);
}
this.proxyName = proxyName(superClass.getSimpleName());
this.loader = proxyLoader!=null?new InnerLoader(proxyLoader):findClassLoader(superClass);
this.emptyBody = emptyBody;
// generate bytecode
ClassWriter writer = (ClassWriter) cv;
ClassReader cr = createClassVisitor(Object.class);
cr.accept(this, 0);
byte[] b = writer.toByteArray();
// CheckClassAdapter.verify(new ClassReader(b), true, new PrintWriter(System.err) );
cachedClass = loader.defineClass(proxyName, b);
// cache no-arg constructor
Class[] args = new Class[closureMap.size()];
for (int i = 0; i < closureMap.size(); i++) {
args[i] = Closure.class;
}
Constructor constructor;
Object[] values;
try {
constructor = cachedClass.getConstructor(args);
int size = this.closureMap.size();
Iterator<DelegateClosure> delegates = this.closureMap.values().iterator();
values = new Object[size];
for (int i = 0; i < size; i++) {
DelegateClosure delegate = delegates.next();
values[i] = delegate.closure;
}
} catch (NoSuchMethodException e) {
constructor = null;
values = null;
}
cachedNoArgConstructor = constructor;
cachedClosures = values;
}
private InnerLoader findClassLoader(Class clazz) {
ClassLoader cl = clazz.getClassLoader();
if (cl==null) cl = this.getClass().getClassLoader();
return new InnerLoader(cl);
}
/**
* Creates a visitor which will be used as a base class for initiating the visit.
* It is not necessary that the class is the superclass, any will do, as long as
* it can be loaded from a byte[].
* @param baseClass
* @return
*/
private ClassReader createClassVisitor(final Class baseClass) {
try {
String name = baseClass.getName();
String path = name.replace('.', '/') + ".class";
InputStream in = loader.getResourceAsStream(path);
return new ClassReader(in);
} catch (IOException e) {
throw new GroovyRuntimeException("Unable to generate a proxy for " + baseClass +" from class loader "+loader,e);
}
}
@Override
public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) {
Set<String> interfacesSet = new LinkedHashSet<String>();
if (interfaces != null) Collections.addAll(interfacesSet, interfaces);
for (Class extraInterface : classList) {
if (extraInterface.isInterface()) interfacesSet.add(BytecodeHelper.getClassInternalName(extraInterface));
}
addGroovyObjectSupport = !GroovyObject.class.isAssignableFrom(superClass);
if (addGroovyObjectSupport) interfacesSet.add("groovy/lang/GroovyObject");
super.visit(V1_5, ACC_PUBLIC, proxyName, signature, BytecodeHelper.getClassInternalName(superClass), interfacesSet.toArray(new String[interfacesSet.size()]));
addClosureDelegates();
if (addGroovyObjectSupport) {
createGroovyObjectSupport();
}
for (Class clazz : classList) {
visitClass(clazz);
}
}
/**
* Visit every class/interface this proxy should implement, and generate the appropriate
* bytecode for delegation if available.
* @param clazz an class for which to generate bytecode
*/
private void visitClass(final Class clazz) {
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
Class<?>[] exceptionTypes = method.getExceptionTypes();
String[] exceptions = new String[exceptionTypes.length];
for (int i = 0; i < exceptions.length; i++) {
exceptions[i] = BytecodeHelper.getClassInternalName(exceptionTypes[i]);
}
// for each method defined in the class, generate the appropriate delegation bytecode
visitMethod(method.getModifiers(),
method.getName(),
BytecodeHelper.getMethodDescriptor(method.getReturnType(), method.getParameterTypes()),
null,
exceptions);
}
Constructor[] constructors = clazz.getDeclaredConstructors();
for (Constructor method : constructors) {
Class<?>[] exceptionTypes = method.getExceptionTypes();
String[] exceptions = new String[exceptionTypes.length];
for (int i = 0; i < exceptions.length; i++) {
exceptions[i] = BytecodeHelper.getClassInternalName(exceptionTypes[i]);
}
// for each method defined in the class, generate the appropriate delegation bytecode
visitMethod(method.getModifiers(),
"<init>",
BytecodeHelper.getMethodDescriptor(Void.TYPE, method.getParameterTypes()),
null,
exceptions);
}
for (Class intf : clazz.getInterfaces()) {
visitClass(intf);
}
Class superclass = clazz.getSuperclass();
if (superclass!=null) visitClass(superclass);
// Ultimately, methods can be available in the closure map which are not defined by the superclass
// nor the interfaces
for (Map.Entry<String, DelegateClosure> entry : closureMap.entrySet()) {
DelegateClosure delegate = entry.getValue();
if (!delegate.visited) {
// generate a new method
visitMethod(ACC_PUBLIC, delegate.name, "([Ljava/lang/Object;)Ljava/lang/Object;", null, null);
}
}
}
/**
* When an object doesn't implement the GroovyObject interface, we generate bytecode for the
* {@link GroovyObject} interface methods. Otherwise, the superclass is expected to implement them.
*/
private void createGroovyObjectSupport() {
visitField(ACC_PRIVATE + ACC_TRANSIENT, "metaClass", "Lgroovy/lang/MetaClass;", null, null);
// getMetaClass
MethodVisitor mv;
{
mv = super.visitMethod(ACC_PUBLIC, "getMetaClass", "()Lgroovy/lang/MetaClass;", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, proxyName, "metaClass", "Lgroovy/lang/MetaClass;");
Label l1 = new Label();
mv.visitJumpInsn(IFNONNULL, l1);
Label l2 = new Label();
mv.visitLabel(l2);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;");
mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/runtime/InvokerHelper", "getMetaClass", "(Ljava/lang/Class;)Lgroovy/lang/MetaClass;");
mv.visitFieldInsn(PUTFIELD, proxyName, "metaClass", "Lgroovy/lang/MetaClass;");
mv.visitLabel(l1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, proxyName, "metaClass", "Lgroovy/lang/MetaClass;");
mv.visitInsn(ARETURN);
mv.visitMaxs(2, 1);
mv.visitEnd();
}
// getProperty
{
mv = super.visitMethod(ACC_PUBLIC, "getProperty", "(Ljava/lang/String;)Ljava/lang/Object;", null, null);
mv.visitCode();
mv.visitIntInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEINTERFACE, "groovy/lang/GroovyObject", "getMetaClass", "()Lgroovy/lang/MetaClass;");
mv.visitIntInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEINTERFACE, "groovy/lang/MetaClass", "getProperty", "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;");
mv.visitInsn(ARETURN);
mv.visitMaxs(3, 2);
mv.visitEnd();
}
// setProperty
{
mv = super.visitMethod(ACC_PUBLIC, "setProperty", "(Ljava/lang/String;Ljava/lang/Object;)V", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, proxyName, "getMetaClass", "()Lgroovy/lang/MetaClass;");
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEINTERFACE, "groovy/lang/MetaClass", "setProperty", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)V");
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitInsn(RETURN);
Label l2 = new Label();
mv.visitLabel(l2);
mv.visitMaxs(4, 3);
mv.visitEnd();
}
// invokeMethod
{
mv = super.visitMethod(ACC_PUBLIC, "invokeMethod", "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;", null, null);
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, proxyName, "getMetaClass", "()Lgroovy/lang/MetaClass;");
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEINTERFACE, "groovy/lang/MetaClass", "invokeMethod", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;");
mv.visitInsn(ARETURN);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitMaxs(4, 3);
mv.visitEnd();
}
// setMetaClass
{
mv = super.visitMethod(ACC_PUBLIC, "setMetaClass", "(Lgroovy/lang/MetaClass;)V", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitFieldInsn(PUTFIELD, proxyName, "metaClass", "Lgroovy/lang/MetaClass;");
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitInsn(RETURN);
Label l2 = new Label();
mv.visitLabel(l2);
mv.visitMaxs(2, 2);
mv.visitEnd();
}
}
/**
* Creates delegate fields for every closure defined in the map.
*/
private void addClosureDelegates() {
for (String fieldName : closureMap.keySet()) {
visitField(ACC_PRIVATE + ACC_FINAL, fieldName, "Lgroovy/lang/Closure;", null, null);
}
}
private static String findFieldName(final Object keyObject) {
if ("*".equals(keyObject.toString())) return "$delegate$closure$"+WILDCARD_FIELD;
return "$delegate$closure$" + keyObject.toString();
}
private static String proxyName(String name) {
int index = name.lastIndexOf('.');
if (index == -1) return "$Proxy" + name + pxyCounter.incrementAndGet();
return "$Proxy" + name.substring(index + 1, name.length()) + pxyCounter.incrementAndGet();
}
@Override
public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
Object key = Arrays.asList(name, desc);
if (visitedMethods.contains(key)) return new EmptyVisitor();
if (Modifier.isPrivate(access) || Modifier.isNative(access)) {
// do not generate bytecode for private methods
return new EmptyVisitor();
}
int accessFlags = access;
String fieldName = findFieldName(name);
visitedMethods.add(key);
if ((closureMap.containsKey(fieldName) || (!"<init>".equals(name) && hasWildcard)) && !Modifier.isStatic(access) && !Modifier.isFinal(access)) {
DelegateClosure delegate = closureMap.get(fieldName);
if (delegate==null) {
fieldName = findFieldName(WILDCARD);
delegate = closureMap.get(fieldName);
}
delegate.visited = true;
if (Modifier.isAbstract(access)) {
// prevents the proxy from being abstract
accessFlags -= ACC_ABSTRACT;
}
return makeDelegateToClosureCall(name, desc, signature, exceptions, accessFlags, fieldName);
} else if ("<init>".equals(name) && (Modifier.isPublic(access) || Modifier.isProtected(access))) {
return createConstructor(access, name, desc, signature, exceptions);
} else if (Modifier.isAbstract(access) && !GROOVYOBJECT_METHODS.contains(name)) {
accessFlags -= ACC_ABSTRACT;
MethodVisitor mv = super.visitMethod(accessFlags, name, desc, signature, exceptions);
mv.visitCode();
if (emptyBody) {
Type returnType = Type.getReturnType(desc);
if (returnType==Type.VOID_TYPE) {
mv.visitInsn(RETURN);
} else {
int loadIns = getLoadInsn(returnType);
switch (loadIns) {
case ILOAD: mv.visitInsn(ICONST_0);
break;
case LLOAD: mv.visitInsn(LCONST_0);
break;
case FLOAD: mv.visitInsn(FCONST_0);
break;
case DLOAD: mv.visitInsn(DCONST_0);
break;
default:
mv.visitInsn(ACONST_NULL);
}
mv.visitInsn(getReturnInsn(returnType));
mv.visitMaxs(2, 2);
}
} else {
// for compatibility with the legacy proxy generator, we should throw an UnsupportedOperationException
// instead of an AbtractMethodException
mv.visitTypeInsn(NEW, "java/lang/UnsupportedOperationException");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/UnsupportedOperationException", "<init>", "()V");
mv.visitInsn(ATHROW);
mv.visitMaxs(2, 1);
}
mv.visitEnd();
}
return new EmptyVisitor();
}
private MethodVisitor createConstructor(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
Type[] args = Type.getArgumentTypes(desc);
StringBuilder newDesc = new StringBuilder("(");
for (Type arg : args) {
newDesc.append(arg.getDescriptor());
}
for (int i = 0; i < closureMap.size(); i++) {
newDesc.append("Lgroovy/lang/Closure;");
}
newDesc.append(")V");
MethodVisitor mv = super.visitMethod(access, name, newDesc.toString(), signature, exceptions);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
for (int i = 0; i < args.length; i++) {
Type arg = args[i];
if (isPrimitive(arg)) {
mv.visitIntInsn(getLoadInsn(arg), i + 1);
} else {
mv.visitVarInsn(ALOAD, i + 1); // load argument i
}
}
mv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(superClass), "<init>", desc);
initializeDelegates(mv, args.length);
mv.visitInsn(RETURN);
int max = 1 + args.length + closureMap.size();
mv.visitMaxs(max, max);
mv.visitEnd();
return new EmptyVisitor();
}
private void initializeDelegates(final MethodVisitor mv, int argStart) {
int idx = argStart+1;
for (String name : closureMap.keySet()) {
mv.visitIntInsn(ALOAD, 0); // this
mv.visitIntInsn(ALOAD, idx++); // constructor arg n
mv.visitFieldInsn(PUTFIELD, proxyName, name, "Lgroovy/lang/Closure;");
}
}
private MethodVisitor makeDelegateToClosureCall(final String name, final String desc, final String signature, final String[] exceptions, final int accessFlags, final String fieldName) {
MethodVisitor mv = super.visitMethod(accessFlags, name, desc, signature, exceptions);
// TraceMethodVisitor tmv = new TraceMethodVisitor(mv);
// mv = tmv;
mv.visitCode();
int stackSize = 0;
// method body should be:
// this.$delegate$closure$methodName.call(new Object[] { method arguments })
Type[] args = Type.getArgumentTypes(desc);
int arrayStore = args.length+1;
BytecodeHelper.pushConstant(mv, args.length);
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); // stack size = 1
stackSize = 1;
for (int i = 0; i < args.length; i++) {
Type arg = args[i];
mv.visitInsn(DUP); // stack size = 2
BytecodeHelper.pushConstant(mv, i); // array index, stack size = 3
stackSize = 3;
// primitive types must be boxed
if (isPrimitive(arg)) {
mv.visitIntInsn(getLoadInsn(arg), i + 1);
String wrappedType = getWrappedClassDescriptor(arg);
mv.visitMethodInsn(INVOKESTATIC,
wrappedType,
"valueOf",
"(" + arg.getDescriptor() + ")L" + wrappedType + ";");
} else {
mv.visitVarInsn(ALOAD, i + 1); // load argument i
}
stackSize = 4;
mv.visitInsn(AASTORE); // store value into array
}
mv.visitVarInsn(ASTORE, arrayStore); // store array
mv.visitVarInsn(ALOAD, 0); // load this
mv.visitFieldInsn(GETFIELD, proxyName, fieldName, "Lgroovy/lang/Closure;");
mv.visitVarInsn(ALOAD, arrayStore); // load argument array
stackSize++;
mv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Closure", "call", "([Ljava/lang/Object;)Ljava/lang/Object;"); // call closure
Type returnType = Type.getReturnType(desc);
if (returnType==Type.VOID_TYPE) {
mv.visitInsn(POP);
mv.visitInsn(RETURN);
} else {
if (isPrimitive(returnType)) {
BytecodeHelper.unbox(mv, ClassHelper.make(returnType.getClassName()));
} else {
mv.visitTypeInsn(CHECKCAST, returnType.getInternalName());
}
mv.visitInsn(getReturnInsn(returnType));
}
mv.visitMaxs(stackSize, arrayStore+1);
mv.visitEnd();
// System.out.println("tmv.getText() = " + tmv.getText());
return new EmptyVisitor();
}
@SuppressWarnings("unchecked")
public GroovyObject proxy(Object... constructorArgs) {
if (constructorArgs==null && cachedNoArgConstructor!=null) {
// if there isn't any argument, we can make invocation faster using the cached constructor
try {
return (GroovyObject) cachedNoArgConstructor.newInstance(cachedClosures);
} catch (InstantiationException e) {
throw new GroovyRuntimeException(e);
} catch (IllegalAccessException e) {
throw new GroovyRuntimeException(e);
} catch (InvocationTargetException e) {
throw new GroovyRuntimeException(e);
}
}
if (constructorArgs==null) constructorArgs= EMPTY_ARGS;
DelegateClosure[] delegates = closureMap.values().toArray(new DelegateClosure[closureMap.size()]);
Object[] values = new Object[constructorArgs.length + delegates.length];
System.arraycopy(constructorArgs, 0, values, 0, constructorArgs.length);
for (int i = 0; i < delegates.length; i++) {
DelegateClosure delegate = delegates[constructorArgs.length + i];
values[i] = delegate.closure;
}
return DefaultGroovyMethods.<GroovyObject>newInstance(cachedClass, values);
}
private static int getLoadInsn(final Type type) {
if (type == Type.BOOLEAN_TYPE) return ILOAD;
if (type == Type.BYTE_TYPE) return ILOAD;
if (type == Type.CHAR_TYPE) return ILOAD;
if (type == Type.DOUBLE_TYPE) return DLOAD;
if (type == Type.FLOAT_TYPE) return FLOAD;
if (type == Type.INT_TYPE) return ILOAD;
if (type == Type.LONG_TYPE) return LLOAD;
if (type == Type.SHORT_TYPE) return ILOAD;
return ALOAD;
}
private static int getReturnInsn(final Type type) {
if (type == Type.BOOLEAN_TYPE) return IRETURN;
if (type == Type.BYTE_TYPE) return IRETURN;
if (type == Type.CHAR_TYPE) return IRETURN;
if (type == Type.DOUBLE_TYPE) return DRETURN;
if (type == Type.FLOAT_TYPE) return FRETURN;
if (type == Type.INT_TYPE) return IRETURN;
if (type == Type.LONG_TYPE) return LRETURN;
if (type == Type.SHORT_TYPE) return IRETURN;
return ARETURN;
}
private boolean isPrimitive(final Type arg) {
return arg == Type.BOOLEAN_TYPE
|| arg == Type.BYTE_TYPE
|| arg == Type.CHAR_TYPE
|| arg == Type.DOUBLE_TYPE
|| arg == Type.FLOAT_TYPE
|| arg == Type.INT_TYPE
|| arg == Type.LONG_TYPE
|| arg == Type.SHORT_TYPE;
}
private String getWrappedClassDescriptor(Type type) {
if (type == Type.BOOLEAN_TYPE) return "java/lang/Boolean";
if (type == Type.BYTE_TYPE) return "java/lang/Byte";
if (type == Type.CHAR_TYPE) return "java/lang/Character";
if (type == Type.DOUBLE_TYPE) return "java/lang/Double";
if (type == Type.FLOAT_TYPE) return "java/lang/Float";
if (type == Type.INT_TYPE) return "java/lang/Integer";
if (type == Type.LONG_TYPE) return "java/lang/Long";
if (type == Type.SHORT_TYPE) return "java/lang/Short";
throw new IllegalArgumentException("Unexpected type class [" + type + "]");
}
private static class InnerLoader extends ClassLoader {
protected InnerLoader(final ClassLoader parent) {
super(parent);
}
protected Class defineClass(String name, byte[] data) {
return super.defineClass(name, data, 0, data.length);
}
}
private static class DelegateClosure {
private final Closure closure;
private final String name;
private boolean visited = false;
private DelegateClosure(final String name, final Closure closure) {
this.name = name;
this.closure = closure;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment