Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.codehaus.groovy.runtime.ProxyGeneratorAdapter Maven / Gradle / Ivy
package org.codehaus.groovy.runtime;
import groovy.lang.Closure;
import groovy.lang.GeneratedGroovyProxy;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;
import groovy.lang.GroovyRuntimeException;
import groovy.transform.Trait;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.classgen.asm.BytecodeHelper;
import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.ErrorCollector;
import org.codehaus.groovy.control.Phases;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.tools.GroovyClass;
import org.codehaus.groovy.transform.trait.Traits;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
public class ProxyGeneratorAdapter extends ClassVisitor implements Opcodes {
private static final Map EMPTY_DELEGATECLOSURE_MAP = Collections.emptyMap();
private static final Set EMPTY_STRING_SET = Collections.emptySet();
private static final String CLOSURES_MAP_FIELD = "$closures$delegate$map" ;
private static final String DELEGATE_OBJECT_FIELD = "$delegate" ;
private static List OBJECT_METHODS = getInheritedMethods(Object.class, new ArrayList());
private static List GROOVYOBJECT_METHODS = getInheritedMethods(GroovyObject.class, new ArrayList());
private static final AtomicLong pxyCounter = new AtomicLong();
private static final Set GROOVYOBJECT_METHOD_NAMESS;
private static final Object[] EMPTY_ARGS = new Object[0 ];
static {
List names = new ArrayList();
for (Method method : GroovyObject.class.getMethods()) {
names.add(method.getName());
}
GROOVYOBJECT_METHOD_NAMESS = new HashSet(names);
}
private final Class superClass;
private final Class delegateClass;
private final InnerLoader loader;
private final String proxyName;
private final LinkedHashSet classList;
private final Map delegatedClosures;
private final boolean emptyBody;
private final boolean hasWildcard;
private final boolean generateDelegateField;
private final Set objectDelegateMethods;
private final Set visitedMethods;
private final Class cachedClass;
private final Constructor cachedNoArgConstructor;
public ProxyGeneratorAdapter (
final Map closureMap,
final Class superClass,
final Class[] interfaces,
final ClassLoader proxyLoader,
final boolean emptyBody,
final Class delegateClass) {
super (Opcodes.ASM4, new ClassWriter(0 ));
this .loader = proxyLoader!=null ?createInnerLoader(proxyLoader):findClassLoader(superClass);
this .visitedMethods = new LinkedHashSet();
this .delegatedClosures = closureMap.isEmpty()? EMPTY_DELEGATECLOSURE_MAP :new HashMap();
boolean wildcard = false ;
for (Map.Entry entry : closureMap.entrySet()) {
String name = entry.getKey().toString();
if ("*" .equals(name)) {
wildcard = true ;
}
this .delegatedClosures.put(name, Boolean.FALSE);
}
this .hasWildcard = wildcard;
Class fixedSuperClass = adjustSuperClass(superClass, interfaces);
this .generateDelegateField = delegateClass!=null ;
this .objectDelegateMethods = generateDelegateField?createDelegateMethodList(fixedSuperClass, delegateClass, interfaces):EMPTY_STRING_SET;
this .delegateClass = delegateClass;
this .superClass = fixedSuperClass;
this .classList = new LinkedHashSet();
this .classList.add(superClass);
if (generateDelegateField) {
classList.add(delegateClass);
Collections.addAll(this .classList, delegateClass.getInterfaces());
}
if (interfaces!=null ) {
Collections.addAll(this .classList, interfaces);
}
this .proxyName = proxyName();
this .emptyBody = emptyBody;
ClassWriter writer = (ClassWriter) cv;
this .visit(Opcodes.V1_5, ACC_PUBLIC, proxyName, null , null , null );
byte [] b = writer.toByteArray();
cachedClass = loader.defineClass(proxyName.replace('/' ,'.' ), b);
Class[] args = generateDelegateField?new Class[] { Map.class, delegateClass }:new Class[] { Map.class };
Constructor constructor;
try {
constructor = cachedClass.getConstructor(args);
} catch (NoSuchMethodException e) {
constructor = null ;
}
cachedNoArgConstructor = constructor;
}
private Class adjustSuperClass (Class superClass, final Class[] interfaces) {
boolean isSuperClassAnInterface = superClass.isInterface();
if (!isSuperClassAnInterface) {
return superClass;
}
Class result = Object.class;
Set traits = new LinkedHashSet();
collectTraits(superClass, traits);
if (interfaces!=null ) {
for (Class anInterface : interfaces) {
collectTraits(anInterface, traits);
}
}
if (!traits.isEmpty()) {
String name = superClass.getName() + "$TraitAdapter" ;
ClassNode cn = new ClassNode(name, ACC_PUBLIC | ACC_ABSTRACT, ClassHelper.OBJECT_TYPE, traits.toArray(new ClassNode[traits.size()]), null );
CompilationUnit cu = new CompilationUnit(loader);
CompilerConfiguration config = new CompilerConfiguration();
SourceUnit su = new SourceUnit(name+"wrapper" , "" , config, loader, new ErrorCollector(config));
cu.addSource(su);
cu.compile(Phases.CONVERSION);
su.getAST().addClass(cn);
cu.compile(Phases.CLASS_GENERATION);
@SuppressWarnings ("unchecked" )
List classes = (List) cu.getClasses();
for (GroovyClass groovyClass : classes) {
if (groovyClass.getName().equals(name)) {
return loader.defineClass(name,groovyClass.getBytes());
}
}
}
return result;
}
private static void collectTraits (final Class clazz, final Set traits) {
Annotation annotation = clazz.getAnnotation(Trait.class);
if (annotation!=null ) {
ClassNode trait = ClassHelper.make(clazz);
traits.add(trait.getPlainNodeReference());
LinkedHashSet selfTypes = new LinkedHashSet();
Traits.collectSelfTypes(trait, selfTypes, true , true );
for (ClassNode selfType : selfTypes) {
if (Traits.isTrait(selfType)) {
traits.add(selfType.getPlainNodeReference());
}
}
}
}
private static InnerLoader createInnerLoader (final ClassLoader parent) {
return AccessController.doPrivileged(new PrivilegedAction() {
public InnerLoader run () {
return new InnerLoader(parent);
}
});
}
private InnerLoader findClassLoader (Class clazz) {
ClassLoader cl = clazz.getClassLoader();
if (cl==null ) cl = this .getClass().getClassLoader();
return createInnerLoader(cl);
}
private static Set createDelegateMethodList (Class superClass, Class delegateClass, Class[] interfaces) {
Set selectedMethods = new HashSet();
List interfaceMethods = new ArrayList();
List superClassMethods = new ArrayList();
Collections.addAll(superClassMethods, superClass.getDeclaredMethods());
if (interfaces!=null ) {
for (Class thisInterface : interfaces) {
getInheritedMethods(thisInterface, interfaceMethods);
}
for (Method method : interfaceMethods) {
if (!(containsEquivalentMethod(superClassMethods, method))) {
selectedMethods.add(method.getName()+Type.getMethodDescriptor(method));
}
}
}
List additionalMethods = getInheritedMethods(delegateClass, new ArrayList());
for (Method method : additionalMethods) {
if (method.getName().indexOf('$' ) != -1 )
continue ;
if (!containsEquivalentMethod(interfaceMethods, method) &&
!containsEquivalentMethod(OBJECT_METHODS, method) &&
!containsEquivalentMethod(GROOVYOBJECT_METHODS, method)) {
selectedMethods.add(method.getName()+Type.getMethodDescriptor(method));
}
}
return selectedMethods;
}
private static List getInheritedMethods (Class baseClass, List methods) {
Collections.addAll(methods, baseClass.getMethods());
Class currentClass = baseClass;
while (currentClass != null ) {
Method[] protectedMethods = currentClass.getDeclaredMethods();
for (Method method : protectedMethods) {
if (method.getName().indexOf('$' ) != -1 )
continue ;
if (Modifier.isProtected(method.getModifiers()) && !containsEquivalentMethod(methods, method))
methods.add(method);
}
currentClass = currentClass.getSuperclass();
}
return methods;
}
private static boolean containsEquivalentMethod (Collection publicAndProtectedMethods, Method candidate) {
for (Method method : publicAndProtectedMethods) {
if (candidate.getName().equals(method.getName()) &&
candidate.getReturnType().equals(method.getReturnType()) &&
hasMatchingParameterTypes(candidate, method)) {
return true ;
}
}
return false ;
}
private static boolean hasMatchingParameterTypes (Method method, Method candidate) {
Class[] candidateParamTypes = candidate.getParameterTypes();
Class[] methodParamTypes = method.getParameterTypes();
if (candidateParamTypes.length != methodParamTypes.length) return false ;
for (int i = 0 ; i < methodParamTypes.length; i++) {
if (!candidateParamTypes[i].equals(methodParamTypes[i])) return false ;
}
return true ;
}
@Override
public void visit (final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) {
Set interfacesSet = new LinkedHashSet();
if (interfaces != null ) Collections.addAll(interfacesSet, interfaces);
for (Class extraInterface : classList) {
if (extraInterface.isInterface()) interfacesSet.add(BytecodeHelper.getClassInternalName(extraInterface));
}
final boolean addGroovyObjectSupport = !GroovyObject.class.isAssignableFrom(superClass);
if (addGroovyObjectSupport) interfacesSet.add("groovy/lang/GroovyObject" );
if (generateDelegateField) {
classList.add(GeneratedGroovyProxy.class);
interfacesSet.add("groovy/lang/GeneratedGroovyProxy" );
}
super .visit(V1_5, ACC_PUBLIC, proxyName, signature, BytecodeHelper.getClassInternalName(superClass), interfacesSet.toArray(new String[interfacesSet.size()]));
visitMethod(ACC_PUBLIC, " " , "()V" , null , null );
addDelegateFields();
if (addGroovyObjectSupport) {
createGroovyObjectSupport();
}
for (Class clazz : classList) {
visitClass(clazz);
}
}
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]);
}
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]);
}
visitMethod(method.getModifiers(),
" " ,
BytecodeHelper.getMethodDescriptor(Void.TYPE, method.getParameterTypes()),
null ,
exceptions);
}
for (Class intf : clazz.getInterfaces()) {
visitClass(intf);
}
Class superclass = clazz.getSuperclass();
if (superclass!=null ) visitClass(superclass);
for (Map.Entry entry : delegatedClosures.entrySet()) {
Boolean visited = entry.getValue();
if (!visited) {
String name = entry.getKey();
if (!"*" .equals(name)) {
visitMethod(ACC_PUBLIC, name, "([Ljava/lang/Object;)Ljava/lang/Object;" , null , null );
}
}
}
}
private void createGroovyObjectSupport () {
visitField(ACC_PRIVATE + ACC_TRANSIENT, "metaClass" , "Lgroovy/lang/MetaClass;" , null , null );
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;" , false );
mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/runtime/InvokerHelper" , "getMetaClass" , "(Ljava/lang/Class;)Lgroovy/lang/MetaClass;" , false );
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();
}
{
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;" , true );
mv.visitIntInsn(ALOAD, 0 );
mv.visitVarInsn(ALOAD, 1 );
mv.visitMethodInsn(INVOKEINTERFACE, "groovy/lang/MetaClass" , "getProperty" , "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;" , true );
mv.visitInsn(ARETURN);
mv.visitMaxs(3 , 2 );
mv.visitEnd();
}
{
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;" , false );
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" , true );
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitInsn(RETURN);
Label l2 = new Label();
mv.visitLabel(l2);
mv.visitMaxs(4 , 3 );
mv.visitEnd();
}
{
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;" , false );
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;" , true );
mv.visitInsn(ARETURN);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitMaxs(4 , 3 );
mv.visitEnd();
}
{
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();
}
}
private void addDelegateFields () {
visitField(ACC_PRIVATE + ACC_FINAL, CLOSURES_MAP_FIELD, "Ljava/util/Map;" , null , null );
if (generateDelegateField) {
visitField(ACC_PRIVATE+ACC_FINAL, DELEGATE_OBJECT_FIELD, BytecodeHelper.getTypeDescription(delegateClass), null , null );
}
}
private String proxyName () {
String name = delegateClass != null ? delegateClass.getName() : superClass.getName();
if (name.startsWith("[" ) && name.endsWith(";" )) {
name = name.substring(1 , name.length() - 1 ) + "_array" ;
}
int index = name.lastIndexOf('.' );
if (index == -1 ) return name + pxyCounter.incrementAndGet() + "_groovyProxy" ;
return name.substring(index + 1 , name.length()) + pxyCounter.incrementAndGet() + "_groovyProxy" ;
}
private static boolean isImplemented (Class clazz, String name, String desc) {
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if (method.getName().equals(name)) {
if (desc.equals(Type.getMethodDescriptor(method))) {
return !Modifier.isAbstract(method.getModifiers());
}
}
}
Class parent = clazz.getSuperclass();
return parent != null && isImplemented(parent, name, desc);
}
@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 null ;
if (Modifier.isPrivate(access) || Modifier.isNative(access) || ((access&ACC_SYNTHETIC)!=0 )) {
return null ;
}
int accessFlags = access;
visitedMethods.add(key);
if ((objectDelegateMethods.contains(name+desc) || delegatedClosures.containsKey(name) || (!" " .equals(name) && hasWildcard)) && !Modifier.isStatic(access) && !Modifier.isFinal(access)) {
if (!GROOVYOBJECT_METHOD_NAMESS.contains(name)) {
if (Modifier.isAbstract(access)) {
accessFlags -= ACC_ABSTRACT;
}
if (delegatedClosures.containsKey(name) || (!" " .equals(name) && hasWildcard)) {
delegatedClosures.put(name, Boolean.TRUE);
return makeDelegateToClosureCall(name, desc, signature, exceptions, accessFlags);
}
if (generateDelegateField && objectDelegateMethods.contains(name+desc)) {
return makeDelegateCall(name, desc, signature, exceptions, accessFlags);
}
delegatedClosures.put(name, Boolean.TRUE);
return makeDelegateToClosureCall(name, desc, signature, exceptions, accessFlags);
}
} else if ("getProxyTarget" .equals(name) && "()Ljava/lang/Object;" .equals(desc)) {
return createGetProxyTargetMethod(access, name, desc, signature, exceptions);
} else if (" " .equals(name) && (Modifier.isPublic(access) || Modifier.isProtected(access))) {
return createConstructor(access, name, desc, signature, exceptions);
} else if (Modifier.isAbstract(access) && !GROOVYOBJECT_METHOD_NAMESS.contains(name)) {
if (isImplemented(superClass, name, desc)) {
return null ;
}
accessFlags -= ACC_ABSTRACT;
MethodVisitor mv = super .visitMethod(accessFlags, name, desc, signature, exceptions);
mv.visitCode();
Type[] args = Type.getArgumentTypes(desc);
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 , registerLen(args)+1 );
}
} else {
mv.visitTypeInsn(NEW, "java/lang/UnsupportedOperationException" );
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/UnsupportedOperationException" , " " , "()V" , false );
mv.visitInsn(ATHROW);
mv.visitMaxs(2 , registerLen(args)+1 );
}
mv.visitEnd();
}
return null ;
}
private MethodVisitor createGetProxyTargetMethod (final int access, final String name, final String desc, final String signature, final String[] exceptions) {
MethodVisitor mv = super .visitMethod(ACC_PUBLIC | ACC_FINAL, name, desc, signature, exceptions);
mv.visitCode();
mv.visitIntInsn(ALOAD,0 );
mv.visitFieldInsn(GETFIELD, proxyName, DELEGATE_OBJECT_FIELD, BytecodeHelper.getTypeDescription(delegateClass));
mv.visitInsn(ARETURN);
mv.visitMaxs(1 ,1 );
mv.visitEnd();
return null ;
}
private static int registerLen (Type[] args) {
int i = 0 ;
for (Type arg : args) {
i += registerLen(arg);
}
return i;
}
private static int registerLen (final Type arg) {
return arg== Type.DOUBLE_TYPE||arg==Type.LONG_TYPE?2 :1 ;
}
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());
}
newDesc.append("Ljava/util/Map;" );
if (generateDelegateField) {
newDesc.append(BytecodeHelper.getTypeDescription(delegateClass));
}
newDesc.append(")V" );
MethodVisitor mv = super .visitMethod(access, name, newDesc.toString(), signature, exceptions);
mv.visitCode();
initializeDelegateClosure(mv, args);
if (generateDelegateField) {
initializeDelegateObject(mv, args);
}
mv.visitVarInsn(ALOAD, 0 );
int idx = 1 ;
for (Type arg : args) {
if (isPrimitive(arg)) {
mv.visitIntInsn(getLoadInsn(arg), idx);
} else {
mv.visitVarInsn(ALOAD, idx);
}
idx += registerLen(arg);
}
mv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(superClass), " " , desc, false );
mv.visitInsn(RETURN);
int max = idx + 1 + (generateDelegateField?1 :0 );
mv.visitMaxs(max, max);
mv.visitEnd();
return null ;
}
private void initializeDelegateClosure (final MethodVisitor mv, Type[] args) {
int idx = 1 + getTypeArgsRegisterLength(args);
mv.visitIntInsn(ALOAD, 0 );
mv.visitIntInsn(ALOAD, idx);
mv.visitFieldInsn(PUTFIELD, proxyName, CLOSURES_MAP_FIELD, "Ljava/util/Map;" );
}
private void initializeDelegateObject (final MethodVisitor mv, Type[] args) {
int idx = 2 + getTypeArgsRegisterLength(args);
mv.visitIntInsn(ALOAD, 0 );
mv.visitIntInsn(ALOAD, idx);
mv.visitFieldInsn(PUTFIELD, proxyName, DELEGATE_OBJECT_FIELD, BytecodeHelper.getTypeDescription(delegateClass));
}
private static int getTypeArgsRegisterLength (Type[] args) {
int length = 0 ;
for (Type type : args) { length += registerLen(type); }
return length;
}
protected MethodVisitor makeDelegateCall (final String name, final String desc, final String signature, final String[] exceptions, final int accessFlags) {
MethodVisitor mv = super .visitMethod(accessFlags, name, desc, signature, exceptions);
mv.visitVarInsn(ALOAD, 0 );
mv.visitFieldInsn(GETFIELD, proxyName, DELEGATE_OBJECT_FIELD, BytecodeHelper.getTypeDescription(delegateClass));
int size;
mv.visitLdcInsn(name);
Type[] args = Type.getArgumentTypes(desc);
BytecodeHelper.pushConstant(mv, args.length);
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object" );
size = 6 ;
int idx = 1 ;
for (int i = 0 ; i < args.length; i++) {
Type arg = args[i];
mv.visitInsn(DUP);
BytecodeHelper.pushConstant(mv, i);
if (isPrimitive(arg)) {
mv.visitIntInsn(getLoadInsn(arg), idx);
String wrappedType = getWrappedClassDescriptor(arg);
mv.visitMethodInsn(INVOKESTATIC, wrappedType, "valueOf" , "(" + arg.getDescriptor() + ")L" + wrappedType + ";" , false );
} else {
mv.visitVarInsn(ALOAD, idx);
}
size = Math.max(size, 5 +registerLen(arg));
idx += registerLen(arg);
mv.visitInsn(AASTORE);
}
mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/runtime/InvokerHelper" , "invokeMethod" , "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;" , false );
unwrapResult(mv, desc);
mv.visitMaxs(size, registerLen(args) + 1 );
return mv;
}
protected MethodVisitor makeDelegateToClosureCall (final String name, final String desc, final String signature, final String[] exceptions, final int accessFlags) {
MethodVisitor mv = super .visitMethod(accessFlags, name, desc, signature, exceptions);
mv.visitCode();
int stackSize = 0 ;
Type[] args = Type.getArgumentTypes(desc);
int arrayStore = args.length+1 ;
BytecodeHelper.pushConstant(mv, args.length);
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object" );
stackSize = 1 ;
int idx = 1 ;
for (int i = 0 ; i < args.length; i++) {
Type arg = args[i];
mv.visitInsn(DUP);
BytecodeHelper.pushConstant(mv, i);
stackSize = 3 ;
if (isPrimitive(arg)) {
mv.visitIntInsn(getLoadInsn(arg), idx);
String wrappedType = getWrappedClassDescriptor(arg);
mv.visitMethodInsn(INVOKESTATIC, wrappedType, "valueOf" , "(" + arg.getDescriptor() + ")L" + wrappedType + ";" , false );
} else {
mv.visitVarInsn(ALOAD, idx);
}
idx += registerLen(arg);
stackSize = Math.max(4 , 3 +registerLen(arg));
mv.visitInsn(AASTORE);
}
mv.visitVarInsn(ASTORE, arrayStore);
int arrayIndex = arrayStore;
mv.visitVarInsn(ALOAD, 0 );
mv.visitFieldInsn(GETFIELD, proxyName, CLOSURES_MAP_FIELD, "Ljava/util/Map;" );
mv.visitLdcInsn(name);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map" , "get" , "(Ljava/lang/Object;)Ljava/lang/Object;" , true );
arrayStore++;
mv.visitVarInsn(ASTORE, arrayStore);
Label notNull = new Label();
mv.visitIntInsn(ALOAD, arrayStore);
mv.visitJumpInsn(IFNONNULL, notNull);
mv.visitVarInsn(ALOAD, 0 );
mv.visitFieldInsn(GETFIELD, proxyName, CLOSURES_MAP_FIELD, "Ljava/util/Map;" );
mv.visitLdcInsn("*" );
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map" , "get" , "(Ljava/lang/Object;)Ljava/lang/Object;" , true );
mv.visitVarInsn(ASTORE, arrayStore);
mv.visitLabel(notNull);
mv.visitVarInsn(ALOAD, arrayStore);
mv.visitMethodInsn(INVOKESTATIC, BytecodeHelper.getClassInternalName(this .getClass()), "ensureClosure" , "(Ljava/lang/Object;)Lgroovy/lang/Closure;" , false );
mv.visitVarInsn(ALOAD, arrayIndex);
stackSize++;
mv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Closure" , "call" , "([Ljava/lang/Object;)Ljava/lang/Object;" , false );
unwrapResult(mv, desc);
mv.visitMaxs(stackSize, arrayStore+1 );
mv.visitEnd();
return null ;
}
private static void unwrapResult (final MethodVisitor mv, final String desc) {
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));
}
}
@SuppressWarnings ("unchecked" )
public GroovyObject proxy (Map map, Object... constructorArgs) {
if (constructorArgs==null && cachedNoArgConstructor!=null ) {
try {
return (GroovyObject) cachedNoArgConstructor.newInstance(map);
} 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;
Object[] values = new Object[constructorArgs.length + 1 ];
System.arraycopy(constructorArgs, 0 , values, 0 , constructorArgs.length);
values[values.length-1 ] = map;
return DefaultGroovyMethods.newInstance(cachedClass, values);
}
@SuppressWarnings ("unchecked" )
public GroovyObject delegatingProxy (Object delegate,Map map, Object... constructorArgs) {
if (constructorArgs==null && cachedNoArgConstructor!=null ) {
try {
return (GroovyObject) cachedNoArgConstructor.newInstance(map, delegate);
} 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;
Object[] values = new Object[constructorArgs.length + 2 ];
System.arraycopy(constructorArgs, 0 , values, 0 , constructorArgs.length);
values[values.length-2 ] = map;
values[values.length-1 ] = delegate;
return DefaultGroovyMethods.newInstance(cachedClass, values);
}
@SuppressWarnings ("unchecked" )
public static Closure ensureClosure (Object o) {
if (o==null ) throw new UnsupportedOperationException();
if (o instanceof Closure) return (Closure) o;
return new ReturnValueWrappingClosure(o);
}
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 static 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 static 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 GroovyClassLoader {
protected InnerLoader (final ClassLoader parent) {
super (parent);
}
public Class defineClass (String name, byte [] data) {
return super .defineClass(name, data, 0 , data.length);
}
}
private static class ReturnValueWrappingClosure extends Closure {
private static final long serialVersionUID = 1313135457715304501L ;
private final V value;
public ReturnValueWrappingClosure (V returnValue) {
super (null );
value = returnValue;
}
@Override
public V call (final Object... args) {
return value;
}
}
}