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


Reply via email to