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 org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.classgen.asm.BytecodeHelper;
import org.codehaus.groovy.classgen.asm.util.TypeUtil;
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.Type;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
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;
import static org.codehaus.groovy.control.CompilerConfiguration.ASM_API_VERSION;
import static org.codehaus.groovy.reflection.ReflectionUtils.isSealed;
import static org.objectweb.asm.Opcodes.AASTORE;
import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
import static org.objectweb.asm.Opcodes.ACC_NATIVE;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
import static org.objectweb.asm.Opcodes.ACC_TRANSIENT;
import static org.objectweb.asm.Opcodes.ACONST_NULL;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.ANEWARRAY;
import static org.objectweb.asm.Opcodes.ARETURN;
import static org.objectweb.asm.Opcodes.ASTORE;
import static org.objectweb.asm.Opcodes.ATHROW;
import static org.objectweb.asm.Opcodes.CHECKCAST;
import static org.objectweb.asm.Opcodes.DCONST_0;
import static org.objectweb.asm.Opcodes.DLOAD;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.FCONST_0;
import static org.objectweb.asm.Opcodes.FLOAD;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.ICONST_0;
import static org.objectweb.asm.Opcodes.IFNONNULL;
import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.LCONST_0;
import static org.objectweb.asm.Opcodes.LLOAD;
import static org.objectweb.asm.Opcodes.NEW;
import static org.objectweb.asm.Opcodes.POP;
import static org.objectweb.asm.Opcodes.PUTFIELD;
import static org.objectweb.asm.Opcodes.RETURN;
@SuppressWarnings ("rawtypes" )
public class ProxyGeneratorAdapter extends ClassVisitor {
private static final Map EMPTY_DELEGATECLOSURE_MAP = Collections.emptyMap();
private static final Object[] EMPTY_ARGS = new Object[0 ];
private static final String CLOSURES_MAP_FIELD = "$closures$delegate$map" ;
private static final String DELEGATE_OBJECT_FIELD = "$delegate" ;
private static final AtomicLong proxyCounter = new AtomicLong();
private static final List OBJECT_METHODS = getInheritedMethods(Object.class , new ArrayList<>());
private static final List GROOVYOBJECT_METHODS = getInheritedMethods(GroovyObject.class , new ArrayList<>());
private static final Set GROOVYOBJECT_METHOD_NAMES;
static {
Set names = new HashSet<>();
for (Method method : GroovyObject.class .getMethods()) {
names.add(method.getName());
}
GROOVYOBJECT_METHOD_NAMES = Collections.unmodifiableSet(names);
}
private final String proxyName;
private final Class superClass;
private final Class delegateClass;
private final InnerLoader innerLoader;
private final Set implClasses;
private final Set visitedMethods;
private final Set objectDelegateMethods;
private final Map delegatedClosures;
private final boolean emptyBody;
private final boolean hasWildcard;
private final boolean generateDelegateField;
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 (ASM_API_VERSION, new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES));
this .innerLoader = proxyLoader != null ? createInnerLoader(proxyLoader, interfaces) : findClassLoader(superClass, interfaces);
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) : Collections.emptySet();
this .delegateClass = delegateClass;
this .superClass = fixedSuperClass;
implClasses = new LinkedHashSet<>();
implClasses.add(superClass);
if (generateDelegateField) {
implClasses.add(delegateClass);
Arrays.stream(delegateClass.getInterfaces()).filter(i -> !isSealed(i)).forEach(implClasses: :add);
}
if (interfaces != null ) {
Collections.addAll(implClasses, interfaces);
}
this .proxyName = proxyName();
this .emptyBody = emptyBody;
ClassWriter writer = (ClassWriter) cv;
this .visit(CompilerConfiguration.DEFAULT.getBytecodeVersion(), ACC_PUBLIC, proxyName, null , null , null );
byte [] b = writer.toByteArray();
cachedClass = innerLoader.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(final Class superClass, Class[] interfaces) {
if (!superClass.isInterface()) {
return superClass;
}
if (interfaces == null || interfaces.length == 0 ) {
interfaces = new Class[]{superClass};
}
assert Arrays.asList(interfaces).contains(superClass);
Set traits = collectTraits(interfaces);
if (!traits.isEmpty()) {
String name = superClass.getName() + "$TraitAdapter" ;
ClassNode cn = new ClassNode(name, ACC_PUBLIC | ACC_ABSTRACT, ClassHelper.OBJECT_TYPE, traits.toArray(ClassNode.EMPTY_ARRAY), null );
CompilationUnit cu = new CompilationUnit(innerLoader);
CompilerConfiguration config = new CompilerConfiguration();
SourceUnit su = new SourceUnit(name + "wrapper" , "" , config, innerLoader, new ErrorCollector(config));
cu.addSource(su);
cu.compile(Phases.CONVERSION);
su.getAST().addClass(cn);
cu.compile(Phases.CLASS_GENERATION);
List classes = cu.getClasses();
for (GroovyClass groovyClass : classes) {
if (groovyClass.getName().equals(name)) {
return innerLoader.defineClass(name, groovyClass.getBytes());
}
}
}
return Object.class ;
}
private static Set collectTraits(final Class[] interfaces) {
Set traits = new LinkedHashSet<>();
for (Class face : interfaces) {
for (ClassNode node : ClassHelper.make(face).getAllInterfaces()) {
if (Traits.isTrait(node)) {
traits.add(node.getPlainNodeReference());
LinkedHashSet selfTypes = new LinkedHashSet<>();
Traits.collectSelfTypes(node, selfTypes, true , true );
for (ClassNode selfType : selfTypes) {
if (Traits.isTrait(selfType)) {
traits.add(selfType.getPlainNodeReference());
}
}
}
}
}
return traits;
}
@SuppressWarnings ("removal" )
private static InnerLoader createInnerLoader(final ClassLoader parent, final Class[] interfaces) {
return java.security.AccessController.doPrivileged((java.security.PrivilegedAction) () -> new InnerLoader(parent, interfaces));
}
private InnerLoader findClassLoader(final Class clazz, final Class[] interfaces) {
ClassLoader cl = clazz.getClassLoader();
if (cl == null ) cl = this .getClass().getClassLoader();
return createInnerLoader(cl, interfaces);
}
private static Set createDelegateMethodList(final Class superClass, final Class delegateClass, final 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(final Class baseClass, final 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(final Collection publicAndProtectedMethods, final 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(final Method method, final 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 : implClasses) {
if (extraInterface.isInterface()) interfacesSet.add(BytecodeHelper.getClassInternalName(extraInterface));
}
final boolean addGroovyObjectSupport = !GroovyObject.class .isAssignableFrom(superClass);
if (addGroovyObjectSupport) interfacesSet.add("groovy/lang/GroovyObject" );
if (generateDelegateField) {
implClasses.add(GeneratedGroovyProxy.class );
interfacesSet.add("groovy/lang/GeneratedGroovyProxy" );
}
super .visit(CompilerConfiguration.DEFAULT.getBytecodeVersion(), ACC_PUBLIC, proxyName, signature, BytecodeHelper.getClassInternalName(superClass), interfacesSet.toArray(new String[0 ]));
visitMethod(ACC_PUBLIC, " " , "()V" , null , null );
addDelegateFields();
if (addGroovyObjectSupport) {
createGroovyObjectSupport();
}
for (Class clazz : implClasses) {
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(0 , 0 );
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(0 , 0 );
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 + proxyCounter.incrementAndGet() + "_groovyProxy" ;
return name.substring(index + 1 ) + proxyCounter.incrementAndGet() + "_groovyProxy" ;
}
private static boolean isImplemented(final Class clazz, final String name, final 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) {
if ((access & (ACC_FINAL | ACC_NATIVE | ACC_PRIVATE | ACC_SYNTHETIC)) != 0 ) return null ;
Object key = Arrays.asList(name, desc);
if (!visitedMethods.add(key)) return null ;
boolean objectDelegate = objectDelegateMethods.contains(name + desc);
boolean closureDelegate = delegatedClosures.containsKey(name);
boolean wildcardDelegate = hasWildcard && !" " .equals(name);
if ((objectDelegate || closureDelegate || wildcardDelegate) && !Modifier.isStatic(access)) {
if (!GROOVYOBJECT_METHOD_NAMES.contains(name)
&& (!Modifier.isAbstract(superClass.getModifiers()) || !isImplemented(superClass, name, desc)
|| ((objectDelegate || closureDelegate) && Arrays.stream(superClass.getMethods()).filter(m -> m.getName().equals(name)).mapToInt(Method: :getModifiers).noneMatch(Modifier: :isAbstract)))) {
if (closureDelegate || wildcardDelegate || !(objectDelegate && generateDelegateField)) {
delegatedClosures.put(name, Boolean.TRUE);
return makeDelegateToClosureCall(name, desc, signature, exceptions, access & ~ACC_ABSTRACT);
}
return makeDelegateCall(name, desc, signature, exceptions, access & ~ACC_ABSTRACT);
}
} 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_NAMES.contains(name) && !isImplemented(superClass, name, desc)) {
MethodVisitor mv = super .visitMethod(access & ~ACC_ABSTRACT, 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(0 , 0 );
}
} else {
mv.visitTypeInsn(NEW, "java/lang/UnsupportedOperationException" );
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/UnsupportedOperationException" , " " , "()V" , false );
mv.visitInsn(ATHROW);
mv.visitMaxs(0 , 0 );
}
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(0 , 0 );
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);
mv.visitMaxs(0 , 0 );
mv.visitEnd();
return null ;
}
private void initializeDelegateClosure(final MethodVisitor mv, final 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, final 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);
boxPrimitiveType(mv, idx, arg);
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(0 , 0 );
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();
Type[] args = Type.getArgumentTypes(desc);
int arrayStore = args.length + 1 ;
BytecodeHelper.pushConstant(mv, args.length);
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object" );
int idx = 1 ;
for (int i = 0 ; i < args.length; i++) {
Type arg = args[i];
mv.visitInsn(DUP);
BytecodeHelper.pushConstant(mv, i);
boxPrimitiveType(mv, idx, arg);
idx += 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);
mv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Closure" , "call" , "([Ljava/lang/Object;)Ljava/lang/Object;" , false );
unwrapResult(mv, desc);
mv.visitMaxs(0 , 0 );
mv.visitEnd();
return null ;
}
private void boxPrimitiveType(final MethodVisitor mv, final int idx, final Type arg) {
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);
}
}
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(final Map map, Object... constructorArgs) {
if (constructorArgs == null && cachedNoArgConstructor != null ) {
try {
return (GroovyObject) cachedNoArgConstructor.newInstance(map);
} catch (InstantiationException | InvocationTargetException | IllegalAccessException 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(final Object delegate, final Map map, Object... constructorArgs) {
if (constructorArgs == null && cachedNoArgConstructor != null ) {
try {
return (GroovyObject) cachedNoArgConstructor.newInstance(map, delegate);
} catch (InstantiationException | InvocationTargetException | IllegalAccessException 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(final 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) {
return TypeUtil.getLoadInsnByType(type);
}
private static int getReturnInsn(final Type type) {
return TypeUtil.getReturnInsnByType(type);
}
private static boolean isPrimitive(final Type type) {
return TypeUtil.isPrimitiveType(type);
}
private static String getWrappedClassDescriptor(final Type type) {
return TypeUtil.getWrappedClassDescriptor(type);
}
private static class InnerLoader extends GroovyClassLoader {
List internalClassLoaders = null ;
protected InnerLoader(final ClassLoader parent, final Class[] interfaces) {
super (parent);
if (interfaces != null ) {
for (Class c : interfaces) {
if (c.getClassLoader() != parent) {
if (internalClassLoaders == null )
internalClassLoaders = new ArrayList<>(interfaces.length);
if (!internalClassLoaders.contains(c.getClassLoader())) {
internalClassLoaders.add(c.getClassLoader());
}
}
}
}
}
@Override
public Class defineClass(final String name, final byte [] data) {
return super .defineClass(name, data, 0 , data.length);
}
@Override
public Class loadClass(final String name) throws ClassNotFoundException {
Class loadedClass = findLoadedClass(name);
if (loadedClass != null ) return loadedClass;
try {
loadedClass = findClass(name);
} catch (ClassNotFoundException ignore) {
}
if (loadedClass != null ) return loadedClass;
ClassNotFoundException ex = null ;
try {
loadedClass = super .loadClass(name);
} catch (ClassNotFoundException e) {
ex = e;
}
if (loadedClass != null ) return loadedClass;
if (internalClassLoaders != null ) {
for (ClassLoader i : internalClassLoaders) {
try {
loadedClass = i.loadClass(name);
if (loadedClass != null ) return loadedClass;
} catch (ClassNotFoundException e) {
}
}
}
if (ex != null ) throw ex;
throw new ClassNotFoundException(name);
}
}
private static class ReturnValueWrappingClosure extends Closure {
private static final long serialVersionUID = 1313135457715304501 L;
private final V value;
public ReturnValueWrappingClosure(final V returnValue) {
super (null );
value = returnValue;
}
@Override
public V call(final Object... args) {
return value;
}
}
}