This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new f773c06a8d refactor static property handling
f773c06a8d is described below
commit f773c06a8dcc9a1a19f2b5be8d05567661330be1
Author: Eric Milles <[email protected]>
AuthorDate: Sat Jan 24 06:40:02 2026 -0600
refactor static property handling
---
.../groovy/transform/ASTTestTransformation.groovy | 8 +-
src/main/java/groovy/lang/MetaClass.java | 4 +-
src/main/java/groovy/lang/MetaClassImpl.java | 195 +++++++++------------
.../classgen/InnerClassCompletionVisitor.java | 33 ++--
src/test/groovy/bugs/Groovy4006.groovy | 142 +++++++++++++++
src/test/groovy/bugs/Groovy4006Bug.groovy | 151 ----------------
.../groovy/util/ReferenceManagerTest.groovy | 5 +-
7 files changed, 253 insertions(+), 285 deletions(-)
diff --git
a/src/main/groovy/org/codehaus/groovy/transform/ASTTestTransformation.groovy
b/src/main/groovy/org/codehaus/groovy/transform/ASTTestTransformation.groovy
index 456f3bb36e..897894bd17 100644
--- a/src/main/groovy/org/codehaus/groovy/transform/ASTTestTransformation.groovy
+++ b/src/main/groovy/org/codehaus/groovy/transform/ASTTestTransformation.groovy
@@ -99,8 +99,8 @@ class ASTTestTransformation implements ASTTransformation,
CompilationUnitAware {
private final Binding binding = new Binding([:].withDefault { null })
@Override
- void call(final SourceUnit source) {
- if (source == sourceUnit) {
+ void call(final SourceUnit unit) {
+ if (unit == sourceUnit) {
test()
}
}
@@ -113,6 +113,8 @@ class ASTTestTransformation implements ASTTransformation,
CompilationUnitAware {
sb = sb[testClosure.columnNumber..<sb.length()]
String testSource = sb[0..<sb.lastIndexOf('}')]
+ def compilationUnit = ASTTestTransformation.this.@compilationUnit
+
binding['node'] = astNode
binding['sourceUnit'] = sourceUnit
binding['compilationUnit'] = compilationUnit
@@ -140,7 +142,7 @@ class ASTTestTransformation implements ASTTransformation,
CompilationUnitAware {
}
}
- static class LabelFinder extends ClassCodeVisitorSupport {
+ private static class LabelFinder extends ClassCodeVisitorSupport {
static List<Statement> lookup(final MethodNode node, final String
label) {
LabelFinder finder = new LabelFinder(label, null)
diff --git a/src/main/java/groovy/lang/MetaClass.java
b/src/main/java/groovy/lang/MetaClass.java
index b06d4c6cc5..3b72d8c74c 100644
--- a/src/main/java/groovy/lang/MetaClass.java
+++ b/src/main/java/groovy/lang/MetaClass.java
@@ -49,7 +49,7 @@ public interface MetaClass extends MetaObjectProtocol {
* @param isCallToSuper Whether the method is a call to a super class
method
* @param fromInsideClass Whether the call was invoked from the inside or
the outside of the class
*
- * @return The return value of the method
+ * @return The method's return value.
*/
Object invokeMethod(Class sender, Object receiver, String methodName,
Object[] arguments, boolean isCallToSuper, boolean fromInsideClass);
@@ -66,7 +66,7 @@ public interface MetaClass extends MetaObjectProtocol {
* @param isCallToSuper Whether the call is to a super class property
* @param fromInsideClass ??
*
- * @return The properties value
+ * @return The property's value.
*/
Object getProperty(Class sender, Object receiver, String property,
boolean isCallToSuper, boolean fromInsideClass);
diff --git a/src/main/java/groovy/lang/MetaClassImpl.java
b/src/main/java/groovy/lang/MetaClassImpl.java
index 2dcd9c6ca0..94c972b72e 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -832,64 +832,43 @@ public class MetaClassImpl implements MetaClass,
MutableMetaClass {
MetaBeanProperty property = findPropertyInClassHierarchy(propertyName,
theCachedClass);
if (property != null) {
onSuperPropertyFoundInHierarchy(property);
- if (!isGetter) {
+ if (isGetter) {
+ return property.getProperty(instance);
+ } else {
property.setProperty(instance, optionalValue);
return null;
}
- return property.getProperty(instance);
}
- // look for getProperty or setProperty overrides
+ // look for getProperty or setProperty
if (isGetter) {
- final Class<?>[] getPropertyArgs = {String.class};
- final MetaMethod method =
findMethodInClassHierarchy(instance.getClass(), GET_PROPERTY_METHOD,
getPropertyArgs, this);
+ MetaMethod method =
findMethodInClassHierarchy(instance.getClass(), GET_PROPERTY_METHOD,
GETTER_MISSING_ARGS, this);
if (method instanceof ClosureMetaMethod) {
onGetPropertyFoundInHierarchy(method);
return method.invoke(instance, new Object[]{propertyName});
}
} else {
- final Class<?>[] setPropertyArgs = {String.class, Object.class};
- final MetaMethod method =
findMethodInClassHierarchy(instance.getClass(), SET_PROPERTY_METHOD,
setPropertyArgs, this);
+ MetaMethod method =
findMethodInClassHierarchy(instance.getClass(), SET_PROPERTY_METHOD,
SETTER_MISSING_ARGS, this);
if (method instanceof ClosureMetaMethod) {
onSetPropertyFoundInHierarchy(method);
return method.invoke(instance, new Object[]{propertyName,
optionalValue});
}
}
- try {
- if (!(instance instanceof Class)) {
- if (isGetter) {
- if (propertyMissingGet != null) {
- return propertyMissingGet.invoke(instance, new
Object[]{propertyName});
- }
- } else {
- if (propertyMissingSet != null) {
- return propertyMissingSet.invoke(instance, new
Object[]{propertyName, optionalValue});
- }
- }
- }
- } catch (InvokerInvocationException iie) {
- boolean shouldHandle = isGetter && propertyMissingGet != null;
- if (!shouldHandle) shouldHandle = !isGetter && propertyMissingSet
!= null;
- if (shouldHandle && iie.getCause() instanceof
MissingPropertyException) {
- throw (MissingPropertyException) iie.getCause();
- }
- throw iie;
- }
-
- Class<?> theClass = instance instanceof Class ? (Class<?>) instance :
instance.getClass();
- if (instance instanceof Class && theClass != Class.class) {
- final MetaProperty metaProperty =
InvokerHelper.getMetaClass(Class.class).hasProperty(instance, propertyName);
- if (metaProperty != null) {
- if (isGetter) {
- return metaProperty.getProperty(instance);
+ if (!(instance instanceof Class)) {
+ MetaMethod propertyMissing = isGetter ? propertyMissingGet :
propertyMissingSet;
+ if (propertyMissing != null) {
+ var arguments = isGetter ? new Object[]{propertyName}
+ : new Object[]{propertyName,
optionalValue};
+ try {
+ return propertyMissing.invoke(instance, arguments);
+ } catch (InvokerInvocationException iie) {
+ throw iie.getCause() instanceof MissingPropertyException
mpe ? mpe : iie;
}
- metaProperty.setProperty(instance, optionalValue);
- return null;
}
}
- throw new MissingPropertyExceptionNoStack(propertyName, theClass);
+ throw new MissingPropertyExceptionNoStack(propertyName, instance
instanceof Class ? (Class<?>) instance : instance.getClass());
}
private Object invokeMissingMethod(final Object instance, final String
methodName, final Object[] arguments, final RuntimeException original, final
boolean isCallToSuper) {
@@ -973,33 +952,29 @@ public class MetaClassImpl implements MetaClass,
MutableMetaClass {
}
/**
- * Hook to deal with the case of MissingProperty for static properties.
The method will look attempt to look up
- * "propertyMissing" handlers and invoke them otherwise thrown a
MissingPropertyException
+ * Hook to deal with the case of missing property for static properties.
The
+ * method attempts to look up "$static_propertyMissing" handlers and invoke
+ * them otherwise throws a MissingPropertyException.
*
- * @param instance The instance
- * @param propertyName The name of the property
- * @param optionalValue The value in the case of a setter
- * @param isGetter True if it's a getter
- * @return The value in the case of a getter or a MissingPropertyException
- */
- protected Object invokeStaticMissingProperty(Object instance, String
propertyName, Object optionalValue, boolean isGetter) {
- MetaClass mc = instance instanceof Class ?
registry.getMetaClass((Class<?>) instance) : this;
- if (isGetter) {
- MetaMethod propertyMissing =
mc.getMetaMethod(STATIC_PROPERTY_MISSING, GETTER_MISSING_ARGS);
- if (propertyMissing != null) {
- return propertyMissing.invoke(instance, new
Object[]{propertyName});
- }
- } else {
- MetaMethod propertyMissing =
mc.getMetaMethod(STATIC_PROPERTY_MISSING, SETTER_MISSING_ARGS);
- if (propertyMissing != null) {
- return propertyMissing.invoke(instance, new
Object[]{propertyName, optionalValue});
+ * @param instance the class instance
+ * @param propertyName the name of the property
+ * @param optionalValue the value in the case of a setter
+ * @param isGetter true for property read, false for property write
+ * @return The value in the case of a getter or null in the case of a
setter.
+ * @throws MissingPropertyException
+ */
+ protected Object invokeStaticMissingProperty(final Object instance, final
String propertyName, final Object optionalValue, final boolean isGetter) {
+ MetaMethod propertyMissing = getMetaMethod(STATIC_PROPERTY_MISSING,
isGetter ? GETTER_MISSING_ARGS : SETTER_MISSING_ARGS);
+ if (propertyMissing != null) {
+ var arguments = isGetter ? new Object[]{propertyName} : new
Object[]{propertyName, optionalValue};
+ try {
+ return propertyMissing.invoke(instance, arguments);
+ } catch (InvokerInvocationException iie) {
+ throw iie.getCause() instanceof MissingPropertyException mpe ?
mpe : iie;
}
}
- if (instance instanceof Class) {
- throw new MissingPropertyException(propertyName, (Class<?>)
instance);
- }
- throw new MissingPropertyException(propertyName, theClass);
+ throw new MissingPropertyExceptionNoStack(propertyName, theClass);
}
/**
@@ -1322,13 +1297,14 @@ public class MetaClassImpl implements MetaClass,
MutableMetaClass {
}
private MetaClass lookupObjectMetaClass(Object object) {
- if (object instanceof GroovyObject go) {
- return go.getMetaClass();
+ if (object instanceof GroovyObject groovyObject) {
+ return groovyObject.getMetaClass();
+ }
+ Class<?> ownerClass = object.getClass();
+ if (ownerClass == Class.class) {
+ ownerClass = (Class<?>) object;
}
- Class ownerClass = object.getClass();
- if (ownerClass == Class.class) ownerClass = (Class) object;
- MetaClass metaClass = registry.getMetaClass(ownerClass);
- return metaClass;
+ return registry.getMetaClass(ownerClass);
}
private static Object invokeMethodOnGroovyObject(String methodName,
Object[] originalArguments, Object owner) {
@@ -1818,9 +1794,6 @@ public class MetaClassImpl implements MetaClass,
MutableMetaClass {
}
}
- /**
- * @return the given property's value on the object
- */
@Override
public Object getProperty(final Class sender, final Object object, final
String name, final boolean useSuper, final boolean fromInsideClass) {
@@ -1904,37 +1877,42 @@ public class MetaClassImpl implements MetaClass,
MutableMetaClass {
//------------------------------------------------------------------
MetaMethod metaMethod = VM_PLUGIN.transformMetaMethod(this,
method);
return metaMethod.doMethodInvoke(object, arguments);
- } else {
-
//------------------------------------------------------------------
- // special cases -- maybe these cases should be special
MetaClasses!
-
//------------------------------------------------------------------
- if (isStatic) {
- MetaClass cmc = registry.getMetaClass(Class.class);
- return cmc.getProperty(Class.class, object, name, false,
false);
- }
- if (object instanceof Collection) {
- return DefaultGroovyMethods.getAt((Collection<?>) object,
name);
- }
- if (object instanceof Object[]) {
- return DefaultGroovyMethods.getAt(Arrays.asList((Object[])
object), name);
- }
- MetaMethod addListenerMethod = listeners.get(name);
- if (addListenerMethod != null) {
- // TODO: one day we could try return the previously registered
Closure listener for easy removal
- return null;
- }
+ }
+
+ //------------------------------------------------------------------
+ // special cases -- maybe these cases should be special MetaClasses!
+ //------------------------------------------------------------------
+ if (isStatic) {
+ return getClassProperty(sender, (Class<?>) object, name);
+ }
+ if (object instanceof Collection) {
+ return DefaultGroovyMethods.getAt((Collection<?>) object, name);
+ }
+ if (object instanceof Object[]) {
+ return DefaultGroovyMethods.getAt(Arrays.asList((Object[])
object), name);
+ }
+ if (listeners.get(name) != null) {
+ // TODO: one day we could try return the previously registered
Closure listener for easy removal
+ return null;
}
//----------------------------------------------------------------------
// missing property protocol
//----------------------------------------------------------------------
- if (object instanceof Class) {
- return invokeStaticMissingProperty(object, name, null, true);
- }
return invokeMissingProperty(object, name, null, true);
}
- public MetaProperty getEffectiveGetMetaProperty(final Class sender, final
Object object, String name, final boolean useSuper) {
+ private Object getClassProperty(final Class<?> sender, final Class<?>
receiver, final String name) throws MissingPropertyException {
+ try {
+ MetaClass cmc = registry.getMetaClass(Class.class);
+ return cmc.getProperty(Class.class, receiver, name, false, false);
+ } catch (MissingPropertyException ignore) {
+ // try $static_propertyMissing / throw MissingPropertyException
+ return invokeStaticMissingProperty(receiver, name, null, true);
+ }
+ }
+
+ public MetaProperty getEffectiveGetMetaProperty(final Class sender, final
Object object, final String name, final boolean useSuper) {
//----------------------------------------------------------------------
// handling of static
@@ -2022,12 +2000,11 @@ public class MetaClassImpl implements MetaClass,
MutableMetaClass {
//----------------------------------------------------------------------
// special cases
//----------------------------------------------------------------------
- if (theClass != Class.class && object instanceof Class) {
+ if (isStatic) {
return new ReadOnlyMetaProperty(name) {
@Override
public Object getProperty(final Object receiver) {
- MetaClass cmc = registry.getMetaClass(Class.class);
- return cmc.getProperty(Class.class, receiver, getName(),
false, false);
+ return getClassProperty(sender, (Class<?>) receiver,
getName());
}
};
}
@@ -2047,8 +2024,7 @@ public class MetaClassImpl implements MetaClass,
MutableMetaClass {
}
};
}
- MetaMethod addListenerMethod = listeners.get(name);
- if (addListenerMethod != null) {
+ if (listeners.get(name) != null) {
return new ReadOnlyMetaProperty(name) {
@Override
public Object getProperty(final Object receiver) { return
null; }
@@ -2056,16 +2032,8 @@ public class MetaClassImpl implements MetaClass,
MutableMetaClass {
}
//----------------------------------------------------------------------
- // error due to missing method/field
+ // missing property protocol
//----------------------------------------------------------------------
- if (isStatic || object instanceof Class) {
- return new ReadOnlyMetaProperty(name) {
- @Override
- public Object getProperty(final Object receiver) {
- return invokeStaticMissingProperty(receiver, getName(),
null, true);
- }
- };
- }
return new ReadOnlyMetaProperty(name) {
@Override
public Object getProperty(final Object receiver) {
@@ -2791,6 +2759,15 @@ public class MetaClassImpl implements MetaClass,
MutableMetaClass {
return;
}
+
//----------------------------------------------------------------------
+ // metaClass property
+
//----------------------------------------------------------------------
+ if (isStatic && name.equals("metaClass")) {
+ MetaClass cmc = registry.getMetaClass(Class.class);
+ cmc.setProperty(Class.class, object, name, newValue, false, false);
+ return;
+ }
+
//----------------------------------------------------------------------
// missing property protocol
//----------------------------------------------------------------------
@@ -2800,10 +2777,10 @@ public class MetaClassImpl implements MetaClass,
MutableMetaClass {
if (mp != null) {
throw new ReadOnlyPropertyException(name, theClass);
}
- if (object instanceof Class && !"metaClass".equals(name)) {
- invokeStaticMissingProperty(object, name, newValue, false);
- } else {
+ if (!isStatic) {
invokeMissingProperty(object, name, newValue, false);
+ } else {
+ invokeStaticMissingProperty(object, name, newValue, false);
}
}
diff --git
a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
index 5cde1cc739..fb70379836 100644
---
a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
+++
b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
@@ -18,8 +18,6 @@
*/
package org.codehaus.groovy.classgen;
-import groovy.lang.MissingMethodException;
-import groovy.lang.MissingPropertyException;
import groovy.transform.CompileStatic;
import groovy.transform.stc.POJO;
import org.codehaus.groovy.ast.ClassHelper;
@@ -90,10 +88,6 @@ public class InnerClassCompletionVisitor extends
InnerClassVisitorHelper {
private FieldNode thisField;
private final SourceUnit sourceUnit;
- private static final String
- CLOSURE_INTERNAL_NAME =
BytecodeHelper.getClassInternalName(CLOSURE_TYPE),
- CLOSURE_DESCRIPTOR =
BytecodeHelper.getTypeDescription(CLOSURE_TYPE);
-
public InnerClassCompletionVisitor(CompilationUnit cu, SourceUnit su) {
sourceUnit = su;
}
@@ -124,8 +118,8 @@ public class InnerClassCompletionVisitor extends
InnerClassVisitorHelper {
super.visitClass(node);
- boolean innerPojo = hasAnnotation(node,
ClassHelper.make(POJO.class))
- && hasAnnotation(node,
ClassHelper.make(CompileStatic.class));
+ boolean innerPojo = hasAnnotation(node, new ClassNode(POJO.class))
+ && hasAnnotation(node, new ClassNode(CompileStatic.class));
if (!innerPojo) {
addMopMethods(innerClass);
}
@@ -213,11 +207,11 @@ public class InnerClassCompletionVisitor extends
InnerClassVisitorHelper {
setPropertyGetterDispatcher(block, VariableExpression.THIS_EXPRESSION,
method.getParameters());
}
- private void getThis(MethodVisitor mv, String classInternalName, String
outerClassDescriptor, String innerClassInternalName) {
+ private void getThis(final MethodVisitor mv, final String
classInternalName, final String outerClassDescriptor, final String
innerClassInternalName) {
mv.visitVarInsn(ALOAD, 0);
if (thisField != null && CLOSURE_TYPE.equals(thisField.getType())) {
- mv.visitFieldInsn(GETFIELD, classInternalName, "this$0",
CLOSURE_DESCRIPTOR);
- mv.visitMethodInsn(INVOKEVIRTUAL, CLOSURE_INTERNAL_NAME,
"getThisObject", "()Ljava/lang/Object;", false);
+ mv.visitFieldInsn(GETFIELD, classInternalName, "this$0",
BytecodeHelper.getTypeDescription(CLOSURE_TYPE));
+ mv.visitMethodInsn(INVOKEVIRTUAL,
BytecodeHelper.getClassInternalName(CLOSURE_TYPE), "getThisObject",
"()Ljava/lang/Object;", false);
mv.visitTypeInsn(CHECKCAST, innerClassInternalName);
} else {
mv.visitFieldInsn(GETFIELD, classInternalName, "this$0",
outerClassDescriptor);
@@ -347,26 +341,31 @@ public class InnerClassCompletionVisitor extends
InnerClassVisitorHelper {
final ClassNode returnType, final Parameter[] parameters, final
BiConsumer<BlockStatement, Parameter[]> consumer) {
MethodNode method = innerClass.getDeclaredMethod(methodName,
parameters);
if (method == null) {
+ // try {
+ // <consumer dispatch>
+ // } catch (MissingMethodException notFound) {
+ // throw new MissingMethodException(notFound.method, this,
notFound.arguments)
+ // }
Parameter catchParam = param(OBJECT_TYPE, "notFound"); // dummy
type
ClassNode exceptionT;
Expression newException;
Expression selfType = varX("this");
if ((modifiers & ACC_STATIC) == 0) selfType = callX(selfType,
"getClass");
if (methodName.endsWith("methodMissing")) {
- exceptionT = ClassHelper.make(MissingMethodException.class);
+ exceptionT =
ClassHelper.make(groovy.lang.MissingMethodException.class);
newException = ctorX(exceptionT,
args(propX(varX(catchParam),"method"), selfType,
propX(varX(catchParam),"arguments")));
} else {
- exceptionT = ClassHelper.make(MissingPropertyException.class);
+ exceptionT =
ClassHelper.make(groovy.lang.MissingPropertyException.class);
newException = ctorX(exceptionT, args(propX(varX(catchParam),
"property"), selfType, propX(varX(catchParam), "cause")));
}
-
catchParam.setType(exceptionT);
catchParam.setOriginType(exceptionT);
BlockStatement handleMissing = block();
consumer.accept(handleMissing, parameters);
- TryCatchStatement methodBody = tryCatchS(handleMissing);
- methodBody.addCatch(catchS(catchParam, throwS(newException)));
- innerClass.addSyntheticMethod(methodName, modifiers, returnType,
parameters, ClassNode.EMPTY_ARRAY, methodBody);
+ TryCatchStatement tryCatch = tryCatchS(handleMissing);
+ tryCatch.addCatch(catchS(catchParam, throwS(newException)));
+
+ innerClass.addSyntheticMethod(methodName, modifiers, returnType,
parameters, ClassNode.EMPTY_ARRAY, tryCatch);
// if there is a user-defined method, add compiler error and
continue
} else if (isStatic(innerClass) && (method.getModifiers() &
ACC_SYNTHETIC) == 0) {
diff --git a/src/test/groovy/bugs/Groovy4006.groovy
b/src/test/groovy/bugs/Groovy4006.groovy
new file mode 100644
index 0000000000..5fdad19a2f
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy4006.groovy
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 bugs
+
+import org.junit.jupiter.api.Test
+
+import static groovy.test.GroovyAssert.shouldFail
+
+final class Groovy4006 {
+
+ @Test
+ void testOuterThisReferenceImplicitPassingToInnerClassConstructorNoArg() {
+ def ex = shouldFail RuntimeException, '''
+ class MyOuterClass4006V1 {
+ def outerName = 'OC1'
+ def foo() {
+ def ic = new MyInnerClass4006V1()
+ ic.bar()
+ }
+ class MyInnerClass4006V1 {
+ def innerName
+ MyInnerClass4006V1() {
+ this.innerName = 'IC1'
+ }
+ def bar() {
+ assert this.innerName == 'IC1'
+ assert this.outerName == 'OC1'
+ this.outerName = 'OC1New'
+ assert this.outerName == 'OC1New'
+ throw new RuntimeException('V1 - Inner class now
successfully refers to implicitly passed outer this reference!')
+ }
+ }
+ }
+ def oc = new MyOuterClass4006V1()
+ oc.foo()
+ '''
+ assert ex.message == 'V1 - Inner class now successfully refers to
implicitly passed outer this reference!'
+ }
+
+ @Test
+ void testOuterThisReferenceImplicitPassingToInnerClassConstructorWithArg()
{
+ def ex = shouldFail RuntimeException, '''
+ class MyOuterClass4006V2 {
+ def outerName = 'OC2'
+ def foo() {
+ def ic = new MyInnerClass4006V1('IC2')
+ ic.bar()
+ }
+ class MyInnerClass4006V1 {
+ def innerName
+ MyInnerClass4006V1(innerName) {
+ this.innerName = innerName
+ }
+ def bar() {
+ assert this.innerName == 'IC2'
+ assert this.outerName == 'OC2'
+ this.outerName = 'OC2New'
+ assert this.outerName == 'OC2New'
+ throw new RuntimeException('V2 - Inner class now
successfully refers to implicitly passed outer this reference!')
+ }
+ }
+ }
+ def oc = new MyOuterClass4006V2()
+ oc.foo()
+ '''
+ assert ex.message == 'V2 - Inner class now successfully refers to
implicitly passed outer this reference!'
+ }
+
+ @Test
+ void
testOuterThisReferenceImplicitPassingToInnerClassConstructorWithArgInAProp() {
+ def ex = shouldFail RuntimeException, '''
+ class MyOuterClass4006V3 {
+ def outerName = 'OC3'
+ def icField = new MyInnerClass4006V3('IC3');
+ def foo() {
+ icField.bar()
+ }
+ class MyInnerClass4006V3 {
+ def innerName
+ MyInnerClass4006V3(innerName) {
+ this.innerName = innerName
+ }
+ def bar() {
+ assert this.innerName == 'IC3'
+ assert this.outerName == 'OC3'
+ this.outerName = 'OC3New'
+ assert this.outerName == 'OC3New'
+ throw new RuntimeException('V3 - Inner class now
successfully refers to implicitly passed outer this reference!')
+ }
+ }
+ }
+ def oc = new MyOuterClass4006V3()
+ oc.foo()
+ '''
+ assert ex.message == 'V3 - Inner class now successfully refers to
implicitly passed outer this reference!'
+ }
+
+ @Test
+ void
testOuterThisReferenceImplicitPassingToInnerClassConstructorWithArgInAField() {
+ def ex = shouldFail RuntimeException, '''
+ class MyOuterClass4006V4 {
+ def outerName = 'OC4'
+ private def icField = new MyInnerClass4006V4('IC4');
+ def foo() {
+ icField.bar()
+ }
+ class MyInnerClass4006V4 {
+ def innerName
+ MyInnerClass4006V4(innerName) {
+ this.innerName = innerName
+ }
+ def bar() {
+ assert this.innerName == 'IC4'
+ assert this.outerName == 'OC4'
+ this.outerName = 'OC4New'
+ assert this.outerName == 'OC4New'
+ throw new RuntimeException('V4 - Inner class now
successfully refers to implicitly passed outer this reference!')
+ }
+ }
+ }
+ def oc = new MyOuterClass4006V4()
+ oc.foo()
+ '''
+ assert ex.message == 'V4 - Inner class now successfully refers to
implicitly passed outer this reference!'
+ }
+}
diff --git a/src/test/groovy/bugs/Groovy4006Bug.groovy
b/src/test/groovy/bugs/Groovy4006Bug.groovy
deleted file mode 100644
index f5045605f0..0000000000
--- a/src/test/groovy/bugs/Groovy4006Bug.groovy
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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 bugs
-
-import groovy.test.GroovyTestCase
-
-class Groovy4006Bug extends GroovyTestCase {
- void testOuterThisReferenceImplicitPassingToInnerClassConstructorNoArg() {
- try {
- assertScript """
- class MyOuterClass4006V1 {
- def outerName = 'OC1'
- def foo() {
- def ic = new MyInnerClass4006V1()
- ic.bar()
- }
- class MyInnerClass4006V1 {
- def innerName
- MyInnerClass4006V1() {
- this.innerName = 'IC1'
- }
- def bar() {
- assert this.innerName == 'IC1'
- assert this.outerName == 'OC1'
- this.outerName = 'OC1New'
- assert this.outerName == 'OC1New'
- throw new RuntimeException('V1 - Inner class now
successfully refers to implicitly passed outer this reference!')
- }
- }
- }
- def oc = new MyOuterClass4006V1()
- oc.foo()
- """
- fail('The script run should have failed with RuntimeException,
coming from bar() of inner class')
- } catch (RuntimeException ex) {
- assert ex.message == 'V1 - Inner class now successfully refers to
implicitly passed outer this reference!'
- }
- }
-
- void testOuterThisReferenceImplicitPassingToInnerClassConstructorWithArg()
{
- try {
- assertScript """
- class MyOuterClass4006V2 {
- def outerName = 'OC2'
- def foo() {
- def ic = new MyInnerClass4006V1('IC2')
- ic.bar()
- }
- class MyInnerClass4006V1 {
- def innerName
- MyInnerClass4006V1(innerName) {
- this.innerName = innerName
- }
- def bar() {
- assert this.innerName == 'IC2'
- assert this.outerName == 'OC2'
- this.outerName = 'OC2New'
- assert this.outerName == 'OC2New'
- throw new RuntimeException('V2 - Inner class now
successfully refers to implicitly passed outer this reference!')
- }
- }
- }
- def oc = new MyOuterClass4006V2()
- oc.foo()
- """
- fail('The script run should have failed with RuntimeException,
coming from bar() of inner class')
- } catch (RuntimeException ex) {
- assert ex.message == 'V2 - Inner class now successfully refers to
implicitly passed outer this reference!'
- }
- }
-
- void
testOuterThisReferenceImplicitPassingToInnerClassConstructorWithArgInAProp() {
- try {
- assertScript """
- class MyOuterClass4006V3 {
- def outerName = 'OC3'
- def icField = new MyInnerClass4006V3('IC3');
- def foo() {
- icField.bar()
- }
- class MyInnerClass4006V3 {
- def innerName
- MyInnerClass4006V3(innerName) {
- this.innerName = innerName
- }
- def bar() {
- assert this.innerName == 'IC3'
- assert this.outerName == 'OC3'
- this.outerName = 'OC3New'
- assert this.outerName == 'OC3New'
- throw new RuntimeException('V3 - Inner class now
successfully refers to implicitly passed outer this reference!')
- }
- }
- }
- def oc = new MyOuterClass4006V3()
- oc.foo()
- """
- fail('The script run should have failed with RuntimeException,
coming from bar() of inner class')
- } catch (RuntimeException ex) {
- assert ex.message == 'V3 - Inner class now successfully refers to
implicitly passed outer this reference!'
- }
- }
-
- void
testOuterThisReferenceImplicitPassingToInnerClassConstructorWithArgInAField() {
- try {
- assertScript """
- class MyOuterClass4006V4 {
- def outerName = 'OC4'
- private def icField = new MyInnerClass4006V4('IC4');
- def foo() {
- icField.bar()
- }
- class MyInnerClass4006V4 {
- def innerName
- MyInnerClass4006V4(innerName) {
- this.innerName = innerName
- }
- def bar() {
- assert this.innerName == 'IC4'
- assert this.outerName == 'OC4'
- this.outerName = 'OC4New'
- assert this.outerName == 'OC4New'
- throw new RuntimeException('V4 - Inner class now
successfully refers to implicitly passed outer this reference!')
- }
- }
- }
- def oc = new MyOuterClass4006V4()
- oc.foo()
- """
- fail('The script run should have failed with RuntimeException,
coming from bar() of inner class')
- } catch (RuntimeException ex) {
- assert ex.message == 'V4 - Inner class now successfully refers to
implicitly passed outer this reference!'
- }
- }
-}
diff --git
a/src/test/groovy/org/codehaus/groovy/util/ReferenceManagerTest.groovy
b/src/test/groovy/org/codehaus/groovy/util/ReferenceManagerTest.groovy
index 0e518890f0..44de389707 100644
--- a/src/test/groovy/org/codehaus/groovy/util/ReferenceManagerTest.groovy
+++ b/src/test/groovy/org/codehaus/groovy/util/ReferenceManagerTest.groovy
@@ -110,13 +110,12 @@ final class ReferenceManagerTest {
// Populate queue with enough references that call back into
removeStallEntries
// so that would normally generate a StackOverflowError
10000.times {
- TestReference<Object> ref = new TestReference<Object>(new
Object(), new Finalizable() {
+ queue.add(new TestReference<>(new Object(), new Finalizable() {
@Override
void finalizeReference() {
callback.removeStallEntries()
}
- })
- queue.add(ref)
+ }))
}
callback.removeStallEntries()
// Success if we made it this far with no StackOverflowError