Skip to content

Commit

Permalink
Trivial tweak for ProxyGenerator[Adaptor]
Browse files Browse the repository at this point in the history
  • Loading branch information
daniellansun committed Jan 11, 2025
1 parent 7c7e0ec commit 86d4cb7
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 76 deletions.
82 changes: 41 additions & 41 deletions src/main/java/groovy/util/ProxyGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,21 +57,22 @@ public class ProxyGenerator {
setMetaClass(GroovySystem.getMetaClassRegistry().getMetaClass(ProxyGenerator.class));
}

public static final ProxyGenerator INSTANCE = new ProxyGenerator(); // TODO: Should we make ProxyGenerator singleton?
public static final ProxyGenerator INSTANCE = new ProxyGenerator();

/**
* Caches proxy classes. When, for example, a call like: <code>map as MyClass</code>
* is found, then a lookup is made into the cache to find if a suitable adapter
* exists already. If so, it is reused instead of generating a new one.
*/
private final Map<CacheKey,ProxyGeneratorAdapter> adapterCache;

private final Map<CacheKey, ProxyGeneratorAdapter> adapterCache;
private static final int CACHE_SIZE = Integer.getInteger("groovy.adapter.cache.default.size", 64);
private static final float LOAD_FACTOR = 0.75f;
private static final int INIT_CAPACITY = (int) Math.ceil(CACHE_SIZE / LOAD_FACTOR) + 1;
{
final int cache_size = Integer.getInteger("groovy.adapter.cache.default.size", 64);
float load_factor = 0.75f; int init_capacity = (int) Math.ceil(cache_size / load_factor) + 1;
adapterCache = new LinkedHashMap<CacheKey, ProxyGeneratorAdapter>(init_capacity, load_factor, true) {
@Override protected boolean removeEldestEntry(Map.Entry<CacheKey, ProxyGeneratorAdapter> entry) {
return size() > cache_size;
adapterCache = new LinkedHashMap<>(INIT_CAPACITY, LOAD_FACTOR, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<CacheKey, ProxyGeneratorAdapter> entry) {
return size() > CACHE_SIZE;
}
};
}
Expand All @@ -80,6 +81,8 @@ public class ProxyGenerator {
private boolean emptyMethods;
private ClassLoader override;

// private ProxyGenerator() {} // TODO: Should we make ProxyGenerator singleton?

public boolean getDebug() {
return debug;
}
Expand Down Expand Up @@ -130,7 +133,7 @@ public GroovyObject instantiateAggregateFromBaseClass(Map map, Class clazz) {
}

public GroovyObject instantiateAggregateFromBaseClass(Closure cl, Class clazz) {
Map<String, Closure> m = new HashMap<String, Closure>();
Map<String, Closure> m = new HashMap<>(4);
m.put("*", cl);
return instantiateAggregateFromBaseClass(m, clazz, null);
}
Expand All @@ -148,7 +151,7 @@ public GroovyObject instantiateAggregateFromInterface(Class clazz) {
}

public GroovyObject instantiateAggregateFromInterface(Map map, Class clazz) {
List<Class> interfaces = new ArrayList<Class>();
List<Class> interfaces = new ArrayList<>(2);
interfaces.add(clazz);
return instantiateAggregate(map, interfaces);
}
Expand All @@ -169,7 +172,7 @@ public GroovyObject instantiateAggregate(Map closureMap, List<Class> interfaces,
if (clazz != null && Modifier.isFinal(clazz.getModifiers())) {
throw new GroovyCastException("Cannot coerce a map to class " + clazz.getName() + " because it is a final class");
}
Map<Object,Object> map = closureMap != null ? closureMap : EMPTY_CLOSURE_MAP;
Map<Object, Object> map = closureMap != null ? closureMap : EMPTY_CLOSURE_MAP;
ProxyGeneratorAdapter adapter = createAdapter(map, interfaces, null, clazz);

return adapter.proxy(map, constructorArgs);
Expand Down Expand Up @@ -206,27 +209,28 @@ public GroovyObject instantiateDelegateWithBaseClass(Map closureMap, List<Class>
* @return a proxy object implementing the specified interfaces, and delegating to the provided object
*/
public GroovyObject instantiateDelegateWithBaseClass(Map closureMap, List<Class> interfaces, Object delegate, Class baseClass, String name) {
Map<Object,Object> map = closureMap != null ? closureMap : EMPTY_CLOSURE_MAP;
Map<Object, Object> map = closureMap != null ? closureMap : EMPTY_CLOSURE_MAP;
ProxyGeneratorAdapter adapter = createAdapter(map, interfaces, delegate.getClass(), baseClass);

return adapter.delegatingProxy(delegate, map, (Object[])null);
return adapter.delegatingProxy(delegate, map, (Object[]) null);
}

private ProxyGeneratorAdapter createAdapter(final Map<Object,Object> closureMap, final List<Class> interfaceList, final Class delegateClass, final Class baseClass) {
Class[] interfaces = interfaceList != null ? interfaceList.toArray(EMPTY_CLASS_ARRAY) : EMPTY_CLASS_ARRAY;
final Class base = baseClass != null ? baseClass : (interfaces.length > 0 ? interfaces[0] : Object.class);
Set<String> methodNames = closureMap.isEmpty() ? Collections.emptySet() : new HashSet<>();
final Class<?> base = baseClass != null ? baseClass : (interfaces.length > 0 ? interfaces[0] : Object.class);
final int closureMapSize = closureMap.size();
Set<String> methodNames = closureMapSize == 0 ? Collections.emptySet() : new HashSet<>(closureMapSize);
for (Object key : closureMap.keySet()) {
methodNames.add(key.toString());
}
boolean useDelegate = (delegateClass != null);
CacheKey key = new CacheKey(base, useDelegate ? delegateClass : Object.class, methodNames, interfaces, emptyMethods, useDelegate);
ClassLoader classLoader = useDelegate ? delegateClass.getClassLoader() : base.getClassLoader();

synchronized (adapterCache) {
return adapterCache.computeIfAbsent(key, k -> {
ClassLoader classLoader = useDelegate ? delegateClass.getClassLoader() : base.getClassLoader();
return new ProxyGeneratorAdapter(closureMap, base, interfaces, classLoader, emptyMethods, useDelegate ? delegateClass : null);
});
return adapterCache.computeIfAbsent(key,
k -> new ProxyGeneratorAdapter(closureMap, base, interfaces, classLoader, emptyMethods,
useDelegate ? delegateClass : null));
}
}

Expand All @@ -250,29 +254,32 @@ private static final class CacheKey {
if (Traits.isTrait(o2)) return 1;
return o1.getName().compareTo(o2.getName());
};
private final boolean emptyMethods;
private final boolean useDelegate;
private final Set<String> methods;
private final ClassReference delegateClass;
private final ClassReference baseClass;
private final ClassReference delegateClass;
private final Set<String> methods;
private final ClassReference[] interfaces;
private final boolean emptyMethods;
private final boolean useDelegate;

private CacheKey(final Class baseClass, final Class delegateClass, final Set<String> methods, final Class[] interfaces, final boolean emptyMethods, final boolean useDelegate) {
this.useDelegate = useDelegate;
this.baseClass = new ClassReference(baseClass);
this.delegateClass = new ClassReference(delegateClass);
this.methods = methods;
this.emptyMethods = emptyMethods;
this.interfaces = interfaces == null ? null : new ClassReference[interfaces.length];
this.useDelegate = useDelegate;
final int interfaceCount = interfaces.length;
if (interfaces != null) {
Class[] interfacesCopy = new Class[interfaces.length];
System.arraycopy(interfaces, 0, interfacesCopy, 0, interfaces.length);
Class[] interfacesCopy = new Class[interfaceCount];
System.arraycopy(interfaces, 0, interfacesCopy, 0, interfaceCount);
Arrays.sort(interfacesCopy, INTERFACE_COMPARATOR);
for (int i = 0; i < interfacesCopy.length; i++) {
Class anInterface = interfacesCopy[i];
this.interfaces[i] = new ClassReference(anInterface);
ClassReference[] interfaceReferences = new ClassReference[interfaceCount];
for (int i = 0, n = interfacesCopy.length; i < n; i++) {
interfaceReferences[i] = new ClassReference(interfacesCopy[i]);
}
this.interfaces = interfaceReferences;
} else {
this.interfaces = null;
}
this.methods = methods;
}

@Override
Expand All @@ -284,8 +291,8 @@ public boolean equals(final Object o) {

if (emptyMethods != cacheKey.emptyMethods) return false;
if (useDelegate != cacheKey.useDelegate) return false;
if (!Objects.equals(baseClass, cacheKey.baseClass)) return false;
if (!Objects.equals(delegateClass, cacheKey.delegateClass)) return false;
if (!Objects.equals(baseClass, cacheKey.baseClass)) return false;
if (!Arrays.equals(interfaces, cacheKey.interfaces)) return false;
if (!Objects.equals(methods, cacheKey.methods)) return false;

Expand All @@ -294,20 +301,13 @@ public boolean equals(final Object o) {

@Override
public int hashCode() {
int result = (emptyMethods ? 1 : 0);
result = 31 * result + (useDelegate ? 1 : 0);
result = 31 * result + (methods != null ? methods.hashCode() : 0);
result = 31 * result + (baseClass != null ? baseClass.hashCode() : 0);
result = 31 * result + (delegateClass != null ? delegateClass.hashCode() : 0);
result = 31 * result + (interfaces != null ? Arrays.hashCode(interfaces) : 0);
return result;
return Objects.hash(emptyMethods, useDelegate, delegateClass, baseClass, Arrays.hashCode(interfaces), methods);
}

/**
* A weak reference which delegates equals and hashcode to the referent.
*/
private static class ClassReference extends WeakReference<Class> {

public ClassReference(Class referent) {
super(referent);
}
Expand All @@ -317,8 +317,8 @@ public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Class thisClass = this.get();
ClassReference that = (ClassReference) o;
if (thisClass == null) return false;
ClassReference that = (ClassReference) o;
return thisClass.equals(that.get());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public class ProxyGeneratorAdapter extends ClassVisitor {

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 AtomicLong PROXY_COUNTER = new AtomicLong();

private static final List<Method> OBJECT_METHODS = getInheritedMethods(Object.class, new ArrayList<>());
private static final List<Method> GROOVYOBJECT_METHODS = getInheritedMethods(GroovyObject.class, new ArrayList<>());
Expand Down Expand Up @@ -349,7 +349,7 @@ private static boolean hasMatchingParameterTypes(final Method method, final Meth
Class<?>[] candidateParamTypes = candidate.getParameterTypes();
Class<?>[] methodParamTypes = method.getParameterTypes();
if (candidateParamTypes.length != methodParamTypes.length) return false;
for (int i = 0; i < methodParamTypes.length; i++) {
for (int i = 0, n = methodParamTypes.length; i < n; i++) {
if (!candidateParamTypes[i].equals(methodParamTypes[i])) return false;
}
return true;
Expand Down Expand Up @@ -401,16 +401,16 @@ private void visitClass(final Class<?> clazz) {
exceptions);
}
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
for (Constructor<?> method : constructors) {
Class<?>[] exceptionTypes = method.getExceptionTypes();
for (Constructor<?> constructor : constructors) {
Class<?>[] exceptionTypes = constructor.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(),
// for each constructor defined in the class, generate the appropriate delegation bytecode
visitMethod(constructor.getModifiers(),
"<init>",
BytecodeHelper.getMethodDescriptor(Void.TYPE, method.getParameterTypes()),
BytecodeHelper.getMethodDescriptor(Void.TYPE, constructor.getParameterTypes()),
null,
exceptions);
}
Expand Down Expand Up @@ -447,14 +447,10 @@ private void createGroovyObjectSupport() {
{
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);
Expand All @@ -472,16 +468,10 @@ private void createGroovyObjectSupport() {
{
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();
}
Expand All @@ -491,7 +481,7 @@ private void createGroovyObjectSupport() {
* Creates delegate fields for every closure defined in the map.
*/
private void addDelegateFields() {
visitField(ACC_PRIVATE + ACC_FINAL, CLOSURES_MAP_FIELD, "Ljava/util/Map;", null, null);
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);
}
Expand All @@ -503,8 +493,8 @@ private String proxyName() {
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";
if (index == -1) return name + PROXY_COUNTER.incrementAndGet() + "_groovyProxy";
return name.substring(index + 1) + PROXY_COUNTER.incrementAndGet() + "_groovyProxy";
}

private static boolean isImplemented(final Class<?> clazz, final String name, final String desc) {
Expand Down Expand Up @@ -546,10 +536,8 @@ public MethodVisitor visitMethod(final int access, final String name, final Stri
}
} else if ("getProxyTarget".equals(name) && "()Ljava/lang/Object;".equals(desc)) {
return createGetProxyTargetMethod(access, name, desc, signature, exceptions);

} else if ("<init>".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();
Expand Down Expand Up @@ -683,14 +671,14 @@ protected MethodVisitor makeDelegateCall(final String name, final String desc, f
mv.visitVarInsn(ALOAD, 0); // load this
mv.visitFieldInsn(GETFIELD, proxyName, DELEGATE_OBJECT_FIELD, BytecodeHelper.getTypeDescription(delegateClass)); // load delegate
// using InvokerHelper to allow potential intercepted calls
int size;
mv.visitLdcInsn(name); // method name
Type[] args = Type.getArgumentTypes(desc);
BytecodeHelper.pushConstant(mv, args.length);
final int argCount = args.length;
BytecodeHelper.pushConstant(mv, argCount);
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
size = 6;
int size = 6;
int idx = 1;
for (int i = 0; i < args.length; i++) {
for (int i = 0; i < argCount; i++) {
Type arg = args[i];
mv.visitInsn(DUP);
BytecodeHelper.pushConstant(mv, i);
Expand All @@ -703,7 +691,6 @@ protected MethodVisitor makeDelegateCall(final String name, final String desc, f
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;
}

Expand All @@ -715,11 +702,12 @@ protected MethodVisitor makeDelegateToClosureCall(final String name, final Strin
// 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);
final int argCount = args.length;
int arrayStore = argCount + 1;
BytecodeHelper.pushConstant(mv, argCount);
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); // stack size = 1
int idx = 1;
for (int i = 0; i < args.length; i++) {
for (int i = 0; i < argCount; i++) {
Type arg = args[i];
mv.visitInsn(DUP); // stack size = 2
BytecodeHelper.pushConstant(mv, i); // array index, stack size = 3
Expand Down Expand Up @@ -753,7 +741,6 @@ protected MethodVisitor makeDelegateToClosureCall(final String name, final Strin
unwrapResult(mv, desc);
mv.visitMaxs(0, 0);
mv.visitEnd();
// System.out.println("tmv.getText() = " + tmv.getText());
return null;
}

Expand Down Expand Up @@ -793,8 +780,9 @@ public GroovyObject proxy(final Map<Object, Object> map, Object... constructorAr
}
}
if (constructorArgs == null) constructorArgs = EMPTY_ARGS;
Object[] values = new Object[constructorArgs.length + 1];
System.arraycopy(constructorArgs, 0, values, 0, constructorArgs.length);
final int constructorArgCount = constructorArgs.length;
Object[] values = new Object[constructorArgCount + 1];
System.arraycopy(constructorArgs, 0, values, 0, constructorArgCount);
values[values.length - 1] = map;
return DefaultGroovyMethods.<GroovyObject>newInstance(cachedClass, values);
}
Expand All @@ -810,8 +798,9 @@ public GroovyObject delegatingProxy(final Object delegate, final Map<Object, Obj
}
}
if (constructorArgs == null) constructorArgs = EMPTY_ARGS;
Object[] values = new Object[constructorArgs.length + 2];
System.arraycopy(constructorArgs, 0, values, 0, constructorArgs.length);
final int constructorArgCount = constructorArgs.length;
Object[] values = new Object[constructorArgCount + 2];
System.arraycopy(constructorArgs, 0, values, 0, constructorArgCount);
values[values.length - 2] = map;
values[values.length - 1] = delegate;
return DefaultGroovyMethods.<GroovyObject>newInstance(cachedClass, values);
Expand Down

0 comments on commit 86d4cb7

Please sign in to comment.