This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch GROOVY-11719
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit f54031dcfda0bfe0018687e16bc2a315967d0b05
Author: Eric Milles <eric.mil...@thomsonreuters.com>
AuthorDate: Tue Jul 22 09:23:05 2025 -0500

    GROOVY-9526, GROOVY-11719: pop field state on exception
---
 .../codehaus/groovy/control/ResolveVisitor.java    | 258 +++++++++++----------
 .../util/GroovyScriptEngineReloadingTest.groovy    |  64 +++--
 2 files changed, 183 insertions(+), 139 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java 
b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
index f8bbf25e17..8e3a08716f 100644
--- a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
+++ b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
@@ -271,14 +271,15 @@ public class ResolveVisitor extends 
ClassCodeExpressionTransformer {
         if (!canSeeTypeVars(node.getModifiers(), node.getDeclaringClass())) {
             genericParameterNames = Collections.emptyMap();
         }
-
-        if (!fieldTypesChecked.contains(node)) {
-            ClassNode t = node.getOriginType();
-            resolveOrFail(t, t);
+        try {
+            if (!fieldTypesChecked.contains(node)) {
+                ClassNode t = node.getOriginType();
+                resolveOrFail(t, t);
+            }
+            super.visitField(node);
+        } finally {
+            genericParameterNames = oldNames;
         }
-        super.visitField(node);
-
-        genericParameterNames = oldNames;
     }
 
     @Override
@@ -287,14 +288,15 @@ public class ResolveVisitor extends 
ClassCodeExpressionTransformer {
         if (!canSeeTypeVars(node.getModifiers(), node.getDeclaringClass())) {
             genericParameterNames = Collections.emptyMap();
         }
+        try {
+            ClassNode t = node.getOriginType();
+            resolveOrFail(t, t);
+            fieldTypesChecked.add(node.getField());
 
-        ClassNode t = node.getOriginType();
-        resolveOrFail(t, t);
-        fieldTypesChecked.add(node.getField());
-
-        super.visitProperty(node);
-
-        genericParameterNames = oldNames;
+            super.visitProperty(node);
+        } finally {
+            genericParameterNames = oldNames;
+        }
     }
 
     private static boolean canSeeTypeVars(final int mods, final ClassNode 
node) {
@@ -303,39 +305,39 @@ public class ResolveVisitor extends 
ClassCodeExpressionTransformer {
 
     @Override
     protected void visitConstructorOrMethod(final MethodNode node, final 
boolean isConstructor) {
+        MethodNode oldMethod = currentMethod;
         VariableScope oldScope = currentScope;
         currentScope = node.getVariableScope();
         Map<GenericsTypeName, GenericsType> oldNames = genericParameterNames;
         genericParameterNames =
                 canSeeTypeVars(node.getModifiers(), node.getDeclaringClass())
                     ? new HashMap<>(genericParameterNames) : new HashMap<>();
+        try {
+            resolveGenericsHeader(node.getGenericsTypes());
 
-        resolveGenericsHeader(node.getGenericsTypes());
-
-        {
-            ClassNode t = node.getReturnType();
-            resolveOrFail(t, t);
-        }
-        for (Parameter p : node.getParameters()) {
-            p.setInitialExpression(transform(p.getInitialExpression()));
-            ClassNode t = p.getOriginType();
-            resolveOrFail(t, t);
-            visitAnnotations(p);
-        }
-        if (node.getExceptions() != null) {
-            for (ClassNode t : node.getExceptions()) {
+            {
+                ClassNode t = node.getReturnType();
                 resolveOrFail(t, t);
             }
-        }
-
-        MethodNode oldCurrentMethod = currentMethod;
-        currentMethod = node;
-
-        super.visitConstructorOrMethod(node, isConstructor);
+            for (Parameter p : node.getParameters()) {
+                p.setInitialExpression(transform(p.getInitialExpression()));
+                ClassNode t = p.getOriginType();
+                resolveOrFail(t, t);
+                visitAnnotations(p);
+            }
+            if (node.getExceptions() != null) {
+                for (ClassNode t : node.getExceptions()) {
+                    resolveOrFail(t, t);
+                }
+            }
 
-        currentMethod = oldCurrentMethod;
-        genericParameterNames = oldNames;
-        currentScope = oldScope;
+            currentMethod = node;
+            super.visitConstructorOrMethod(node, isConstructor);
+        } finally {
+            genericParameterNames = oldNames;
+            currentMethod = oldMethod;
+            currentScope = oldScope;
+        }
     }
 
     private void resolveOrFail(final ClassNode type, final ASTNode node) {
@@ -1249,107 +1251,112 @@ public class ResolveVisitor extends 
ClassCodeExpressionTransformer {
 
     @Override
     public void visitClass(final ClassNode node) {
-        ClassNode oldNode = currentClass;
-        currentClass = node;
-
-        ModuleNode module = node.getModule();
-        if (!module.hasImportsResolved()) {
-            for (ImportNode importNode : module.getImports()) {
-                currentImport = importNode;
-                ClassNode type = importNode.getType();
-                if (resolve(type, false, false, true)) {
-                    currentImport = null;
-                    continue;
-                }
-                currentImport = null;
-                addError("unable to resolve class " + type.getName(), type);
-            }
-            for (ImportNode importNode : module.getStarImports()) {
-                if (importNode.getLineNumber() > 0) {
+        ClassNode oldNode = currentClass; currentClass = node;
+        Map<GenericsTypeName, GenericsType> outerNames = null;
+        try {
+            ModuleNode module = node.getModule();
+            if (!module.hasImportsResolved()) {
+                for (ImportNode importNode : module.getImports()) {
                     currentImport = importNode;
-                    String importName = importNode.getPackageName();
-                    importName = importName.substring(0, 
importName.length()-1);
-                    ClassNode type = 
ClassHelper.makeWithoutCaching(importName);
-                    if (resolve(type, false, false, true)) {
-                        importNode.setType(type);
+                    try {
+                        ClassNode type = importNode.getType();
+                        if (!resolve(type, false, false, true)) {
+                            addError("unable to resolve class " + 
type.getName(), type);
+                        }
+                    } finally {
+                        currentImport = null;
                     }
-                    currentImport = null;
                 }
-            }
-            for (ImportNode importNode : module.getStaticImports().values()) {
-                ClassNode type = importNode.getType();
-                if (!resolve(type, true, true, true))
-                    addError("unable to resolve class " + type.getName(), 
type);
-            }
-            for (ImportNode importNode : 
module.getStaticStarImports().values()) {
-                ClassNode type = importNode.getType();
-                if (!resolve(type, true, true, true))
-                    addError("unable to resolve class " + type.getName(), 
type);
-            }
+                for (ImportNode importNode : module.getStarImports()) {
+                    if (importNode.getLineNumber() > 0) {
+                        currentImport = importNode;
+                        try {
+                            String importName = importNode.getPackageName();
+                            importName = importName.substring(0, 
importName.length()-1);
+                            ClassNode type = 
ClassHelper.makeWithoutCaching(importName);
+                            if (resolve(type, false, false, true)) {
+                                importNode.setType(type);
+                            }
+                        } finally {
+                            currentImport = null;
+                        }
+                    }
+                }
+                for (ImportNode importNode : 
module.getStaticImports().values()) {
+                    ClassNode type = importNode.getType();
+                    if (!resolve(type, true, true, true))
+                        addError("unable to resolve class " + type.getName(), 
type);
+                }
+                for (ImportNode importNode : 
module.getStaticStarImports().values()) {
+                    ClassNode type = importNode.getType();
+                    if (!resolve(type, true, true, true))
+                        addError("unable to resolve class " + type.getName(), 
type);
+                }
 
-            module.setImportsResolved(true);
-        }
+                module.setImportsResolved(true);
+            }
 
-        //
+            //
 
-        if (phase < 2) node.putNodeMetaData(AnnotationNode[].class, new 
LinkedHashSet<>());
+            if (phase < 2) node.putNodeMetaData(AnnotationNode[].class, new 
LinkedHashSet<>());
 
-        Map<GenericsTypeName, GenericsType> outerNames = null;
-        if (node instanceof InnerClassNode) {
-            outerNames = genericParameterNames;
-            genericParameterNames = new HashMap<>();
-            if (!Modifier.isStatic(node.getModifiers()))
-                genericParameterNames.putAll(outerNames); // outer names 
visible
-        } else {
-            genericParameterNames.clear(); // outer class: new generic 
namespace
-        }
-        resolveGenericsHeader(node.getGenericsTypes());
-        switch (phase) { // GROOVY-9866, GROOVY-10466
-          case 0:
-          case 1:
-            ClassNode sn = node.getUnresolvedSuperClass();
-            if (sn != null) {
-                resolveOrFail(sn, "", node, true);
-            }
-            for (ClassNode in : node.getInterfaces()) {
-                resolveOrFail(in, "", node, true);
+            if (node instanceof InnerClassNode) {
+                outerNames = genericParameterNames;
+                genericParameterNames = new HashMap<>();
+                if (!Modifier.isStatic(node.getModifiers()))
+                    genericParameterNames.putAll(outerNames); // outer names 
visible
+            } else {
+                genericParameterNames.clear(); // outer class: new generic 
namespace
             }
+            resolveGenericsHeader(node.getGenericsTypes());
+            switch (phase) { // GROOVY-9866, GROOVY-10466
+              case 0:
+              case 1:
+                ClassNode sn = node.getUnresolvedSuperClass();
+                if (sn != null) {
+                    resolveOrFail(sn, "", node, true);
+                }
+                for (ClassNode in : node.getInterfaces()) {
+                    resolveOrFail(in, "", node, true);
+                }
 
-            if (sn != null) checkCyclicInheritance(node, sn);
-            for (ClassNode in : node.getInterfaces()) {
-                checkCyclicInheritance(node, in);
-            }
-          case 2:
-            // VariableScopeVisitor visits anon. inner class body inline, so 
resolve now
-            for (Iterator<InnerClassNode> it = node.getInnerClasses(); 
it.hasNext(); ) {
-                InnerClassNode cn = it.next();
-                if (cn.isAnonymous()) {
-                    MethodNode enclosingMethod = cn.getEnclosingMethod();
-                    if (enclosingMethod != null) {
-                        
resolveGenericsHeader(enclosingMethod.getGenericsTypes()); // GROOVY-6977
+                if (sn != null) checkCyclicInheritance(node, sn);
+                for (ClassNode in : node.getInterfaces()) {
+                    checkCyclicInheritance(node, in);
+                }
+              case 2:
+                // VariableScopeVisitor visits anon. inner class body inline, 
so resolve now
+                for (Iterator<InnerClassNode> it = node.getInnerClasses(); 
it.hasNext(); ) {
+                    InnerClassNode cn = it.next();
+                    if (cn.isAnonymous()) {
+                        MethodNode enclosingMethod = cn.getEnclosingMethod();
+                        if (enclosingMethod != null) {
+                            
resolveGenericsHeader(enclosingMethod.getGenericsTypes()); // GROOVY-6977
+                        }
+                        resolveOrFail(cn.getUnresolvedSuperClass(false), cn); 
// GROOVY-9642
                     }
-                    resolveOrFail(cn.getUnresolvedSuperClass(false), cn); // 
GROOVY-9642
                 }
-            }
-            if (phase == 1) break; // resolve other class headers before 
members, et al.
+                if (phase == 1) break; // resolve other class headers before 
members, et al.
 
-            // initialize scopes/variables now that imports and super types 
are resolved
-            new VariableScopeVisitor(source).visitClass(node);
+                // initialize scopes/variables now that imports and super 
types are resolved
+                new VariableScopeVisitor(source).visitClass(node);
 
-            visitPackage(node.getPackage());
-            visitImports(node.getModule());
-            visitAnnotations(node);
+                visitPackage(node.getPackage());
+                visitImports(node.getModule());
+                visitAnnotations(node);
 
-            @SuppressWarnings("unchecked") // grab the collected annotations 
and stop collecting
-            var headerAnnotations = (Set<AnnotationNode>) 
node.putNodeMetaData(AnnotationNode[].class, null);
+                @SuppressWarnings("unchecked") // grab the collected 
annotations and stop collecting
+                var headerAnnotations = (Set<AnnotationNode>) 
node.putNodeMetaData(AnnotationNode[].class, null);
 
-            node.visitContents(this);
-            visitObjectInitializerStatements(node);
-            // GROOVY-10750, GROOVY-11179: resolve and inline
-            headerAnnotations.forEach(this::visitAnnotation);
+                node.visitContents(this);
+                visitObjectInitializerStatements(node);
+                // GROOVY-10750, GROOVY-11179: resolve and inline
+                headerAnnotations.forEach(this::visitAnnotation);
+            }
+        } finally {
+            if (outerNames != null) genericParameterNames = outerNames;
+            currentClass = oldNode;
         }
-        if (outerNames != null) genericParameterNames = outerNames;
-        currentClass = oldNode;
     }
 
     private void addFatalError(final String text, final ASTNode node) {
@@ -1401,8 +1408,11 @@ public class ResolveVisitor extends 
ClassCodeExpressionTransformer {
     public void visitBlockStatement(final BlockStatement block) {
         VariableScope oldScope = currentScope;
         currentScope = block.getVariableScope();
-        super.visitBlockStatement(block);
-        currentScope = oldScope;
+        try {
+            super.visitBlockStatement(block);
+        } finally {
+            currentScope = oldScope;
+        }
     }
 
     private boolean resolveGenericsTypes(final GenericsType[] types) {
diff --git a/src/test/groovy/groovy/util/GroovyScriptEngineReloadingTest.groovy 
b/src/test/groovy/groovy/util/GroovyScriptEngineReloadingTest.groovy
index 660f4ddcf0..dc8653ca81 100644
--- a/src/test/groovy/groovy/util/GroovyScriptEngineReloadingTest.groovy
+++ b/src/test/groovy/groovy/util/GroovyScriptEngineReloadingTest.groovy
@@ -21,9 +21,9 @@ package groovy.util
 import groovy.transform.AutoFinal
 import groovy.transform.Canonical
 import groovy.transform.TupleConstructor
-import org.junit.Before
-import org.junit.BeforeClass
-import org.junit.Test
+import org.junit.jupiter.api.BeforeAll
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
 
 import static groovy.test.GroovyAssert.shouldFail
 
@@ -32,7 +32,7 @@ final class GroovyScriptEngineReloadingTest {
 
     private GroovyScriptEngine gse
 
-    @BeforeClass
+    @BeforeAll
     static void setUpTestSuite() {
         URL.setURLStreamHandlerFactory(protocol -> {
             if (protocol == MapUrlConnection.PROTOCOL) {
@@ -41,7 +41,7 @@ final class GroovyScriptEngineReloadingTest {
         })
     }
 
-    @Before
+    @BeforeEach
     void setUpTestCase() {
         makeGSE()
     }
@@ -72,9 +72,9 @@ final class GroovyScriptEngineReloadingTest {
         gse.@time += i
     }
 
-    private void execute(intervall, sleepTime, expected) {
-        gse.config.minimumRecompilationInterval = intervall
-        sleep intervall
+    private void execute(interval, sleepTime, expected) {
+        gse.config.minimumRecompilationInterval = interval
+        sleep interval
 
         Binding binding = new Binding()
         int val = 0
@@ -152,8 +152,8 @@ final class GroovyScriptEngineReloadingTest {
         execute(1000, 10000, 2)
     }
 
-    @Test // ensures new source is ignored till minimumRecompilationIntervall 
is passed
-    void testRecompilationIntervall() {
+    @Test // ensures new source is ignored till minimumRecompilationInterval 
is passed
+    void testRecompilationInterval() {
         execute(100000, 10000, 1)
         execute(100000, 10000, 1)
         execute(100000, 200000, 2)
@@ -178,8 +178,37 @@ final class GroovyScriptEngineReloadingTest {
 
         // make a change to the sub-class so that it gets recompiled
         MapFileSystem.instance.modFile('SubClass.groovy', subClassText + '\n', 
gse.@time)
+
         gse.loadScriptByName('SubClass.groovy')
+    }
+
+    // GROOVY-9526, GROOVY-11719
+    @Test
+    void testRecompilingWithGenerics2() {
+        MapFileSystem.instance.modFile('BaseClass.groovy', 'class BaseClass<T> 
{}', gse.@time)
+
+        MapFileSystem.instance.modFile('SomeClass.groovy', '''
+            class SomeClass {
+                public static final String CONSTANT = String.valueOf("")
+            }
+        ''', gse.@time)
+
+        def subClassText = '''
+            class SubClass extends BaseClass<String> {
+                public static final String CONSTANT = SomeClass.CONSTANT
+            }
+        '''
+        MapFileSystem.instance.modFile('SubClass.groovy', subClassText, 
gse.@time)
 
+        MapFileSystem.instance.modFile('subClassUsage.groovy', 
'SubClass.CONSTANT', gse.@time)
+
+        gse.loadScriptByName('subClassUsage.groovy')
+        sleep 1000
+
+        // make a change to the sub-class so that it gets recompiled
+        MapFileSystem.instance.modFile('SubClass.groovy', subClassText + '\n', 
gse.@time)
+
+        gse.loadScriptByName('subClassUsage.groovy')
     }
 
     @Test
@@ -380,7 +409,8 @@ final class GroovyScriptEngineReloadingTest {
         assert aScript instanceof CustomBaseClass
     }
 
-    @Test // GROOVY-3893
+    // GROOVY-3893
+    @Test
     void testGSEWithNoScriptRoots() {
         shouldFail ResourceException, {
             String[] emptyScriptRoots = []
@@ -389,7 +419,8 @@ final class GroovyScriptEngineReloadingTest {
         }
     }
 
-    @Test // GROOVY-6203
+    // GROOVY-6203
+    @Test
     void testGSEBaseClass() {
         gse.config = new 
org.codehaus.groovy.control.CompilerConfiguration(scriptBaseClass: 
CustomBaseClass.name)
 
@@ -401,7 +432,8 @@ final class GroovyScriptEngineReloadingTest {
         assert script instanceof CustomBaseClass
     }
 
-    @Test // GROOVY-4013
+    // GROOVY-4013
+    @Test
     void testGSENoCachingOfInnerClasses() {
         MapFileSystem.instance.modFile('Groovy4013Helper.groovy', '''
             import java.awt.event.*
@@ -424,7 +456,8 @@ final class GroovyScriptEngineReloadingTest {
         assert klazz.name == 'Groovy4013Helper' // we should still get the 
outer class, not inner one
     }
 
-    @Test // GROOVY-4234
+    // GROOVY-4234
+    @Test
     void testGSERunningAScriptThatHasMultipleClasses() {
         MapFileSystem.instance.modFile('Groovy4234Helper.groovy', '''
             class Foo4234 {
@@ -443,7 +476,8 @@ final class GroovyScriptEngineReloadingTest {
         gse.run('Groovy4234Helper.groovy', new Binding())
     }
 
-    @Test // GROOVY-2811, GROOVY-4286
+    // GROOVY-2811, GROOVY-4286
+    @Test
     void testReloadingInterval() {
         gse.config.minimumRecompilationInterval = 1500
         def binding = new Binding([:])

Reply via email to