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

sunlan pushed a commit to branch GROOVY_2_5_X
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push:
     new 7156a3d  GROOVY-7812: Static inner classes cannot be accessed from 
other files when running by 'groovy' command(closes #853)
7156a3d is described below

commit 7156a3d4541fe6d761a524f682e6e04f3096a6d9
Author: Daniel Sun <sun...@apache.org>
AuthorDate: Wed Jan 16 21:54:22 2019 +0800

    GROOVY-7812: Static inner classes cannot be accessed from other files when 
running by 'groovy' command(closes #853)
    
    (cherry picked from commit ade7ecb915ece738193deaa667c54b8a483093a2)
---
 .../java/org/codehaus/groovy/ast/CompileUnit.java  | 43 ++++++++++++-
 .../codehaus/groovy/control/ResolveVisitor.java    | 74 +++++++++++++++++++++-
 .../groovy/bugs/groovy7812/Main.groovy             | 29 +++++++++
 .../groovy/bugs/groovy7812/MainWithErrors.groovy   | 22 +++++++
 .../groovy/bugs/groovy7812/Outer.groovy            | 36 +++++++++++
 src/test/groovy/bugs/Groovy7812Bug.groovy          | 52 +++++++++++++++
 6 files changed, 253 insertions(+), 3 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/CompileUnit.java 
b/src/main/java/org/codehaus/groovy/ast/CompileUnit.java
index 065098d..5758fea 100644
--- a/src/main/java/org/codehaus/groovy/ast/CompileUnit.java
+++ b/src/main/java/org/codehaus/groovy/ast/CompileUnit.java
@@ -20,11 +20,13 @@ package org.codehaus.groovy.ast;
 
 import groovy.lang.GroovyClassLoader;
 import org.codehaus.groovy.GroovyBugError;
+import groovy.transform.Internal;
 import org.codehaus.groovy.control.CompilerConfiguration;
 import org.codehaus.groovy.control.SourceUnit;
 import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
 import org.codehaus.groovy.syntax.SyntaxException;
 import org.codehaus.groovy.util.ListHashMap;
+import org.objectweb.asm.Opcodes;
 
 import java.security.CodeSource;
 import java.util.ArrayList;
@@ -52,6 +54,7 @@ public class CompileUnit {
     private final Map<String, ClassNode> classesToCompile = new 
HashMap<String, ClassNode>();
     private final Map<String, SourceUnit> classNameToSource = new 
HashMap<String, SourceUnit>();
     private final Map<String, InnerClassNode> generatedInnerClasses = new 
HashMap();
+    private final Map<String, ConstructedOuterNestedClassNode> 
classesToResolve = new HashMap<>();
     private ListHashMap metaDataMap = null;
 
     public CompileUnit(GroovyClassLoader classLoader, CompilerConfiguration 
config) {
@@ -91,7 +94,7 @@ public class CompileUnit {
     /**
      * @return a list of all the classes in each module in the compilation unit
      */
-    public List getClasses() {
+    public List<ClassNode> getClasses() {
         List<ClassNode> answer = new ArrayList<ClassNode>();
         for (ModuleNode module : modules) {
             answer.addAll(module.getClasses());
@@ -192,6 +195,24 @@ public class CompileUnit {
         return Collections.unmodifiableMap(generatedInnerClasses);
     }
 
+    public Map<String, ClassNode> getClassesToCompile() {
+        return classesToCompile;
+    }
+
+    public Map<String, ConstructedOuterNestedClassNode> getClassesToResolve() {
+        return classesToResolve;
+    }
+
+    /**
+     * Add a constructed class node as a placeholder to resolve outer nested 
class further.
+     *
+     * @param cn the constructed class node
+     */
+    public void addClassNodeToResolve(ConstructedOuterNestedClassNode cn) {
+        classesToResolve.put(cn.getUnresolvedName(), cn);
+    }
+
+
     /**
      * Gets the node meta data for the provided key.
      *
@@ -266,4 +287,24 @@ public class CompileUnit {
     public ListHashMap getMetaDataMap() {
         return metaDataMap;
     }
+
+
+
+    /**
+     * Represents a resolved type as a placeholder, SEE GROOVY-7812
+     */
+    @Internal
+    public static class ConstructedOuterNestedClassNode extends ClassNode {
+        private final ClassNode enclosingClassNode;
+
+        public ConstructedOuterNestedClassNode(ClassNode outer, String 
innerClassName) {
+            super(innerClassName, Opcodes.ACC_PUBLIC, ClassHelper.OBJECT_TYPE);
+            this.enclosingClassNode = outer;
+            this.isPrimaryNode = false;
+        }
+
+        public ClassNode getEnclosingClassNode() {
+            return enclosingClassNode;
+        }
+    }
 }
diff --git a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java 
b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
index 80f226d..62ed420 100644
--- a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
+++ b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
@@ -78,6 +78,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import static 
org.codehaus.groovy.ast.CompileUnit.ConstructedOuterNestedClassNode;
 import static org.codehaus.groovy.ast.GenericsType.GenericsTypeName;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.inSamePackage;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.isDefaultVisibility;
@@ -145,6 +146,7 @@ public class ResolveVisitor extends 
ClassCodeExpressionTransformer {
     }
 
 
+
     private static String replacePoints(String name) {
         return name.replace('.','$');
     }
@@ -339,9 +341,44 @@ public class ResolveVisitor extends 
ClassCodeExpressionTransformer {
     private void resolveOrFail(ClassNode type, String msg, ASTNode node) {
         if (resolve(type)) return;
         if (resolveToInner(type)) return;
+        if (resolveToOuterNested(type)) return;
+
         addError("unable to resolve class " + type.getName() + " " + msg, 
node);
     }
 
+    // GROOVY-7812(#1): Static inner classes cannot be accessed from other 
files when running by 'groovy' command
+    // if the type to resolve is an inner class and it is in an outer class 
which is not resolved,
+    // we set the resolved type to a placeholder class node, i.e. a 
ConstructedOuterNestedClass instance
+    // when resolving the outer class later, we set the resolved type of 
ConstructedOuterNestedClass instance to the actual inner class node(SEE 
GROOVY-7812(#2))
+    private boolean resolveToOuterNested(ClassNode type) {
+        CompileUnit compileUnit = currentClass.getCompileUnit();
+        Map<String, ClassNode> classesToCompile = 
compileUnit.getClassesToCompile();
+
+        for (Map.Entry<String, ClassNode> entry : classesToCompile.entrySet()) 
{
+            String enclosingClassName = entry.getKey();
+            ClassNode enclosingClassNode = entry.getValue();
+
+            String outerNestedClassName = 
tryToConstructOuterNestedClassName(type, enclosingClassName);
+            if (null != outerNestedClassName) {
+                compileUnit.addClassNodeToResolve(new 
ConstructedOuterNestedClassNode(enclosingClassNode, outerNestedClassName));
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private String tryToConstructOuterNestedClassName(ClassNode type, String 
enclosingClassName) {
+        for (String typeName = type.getName(), ident = typeName; 
ident.contains("."); ) {
+            ident = ident.substring(0, ident.lastIndexOf("."));
+            if (enclosingClassName.endsWith(ident)) {
+                return enclosingClassName + 
typeName.substring(ident.length()).replace(".", "$");
+            }
+        }
+
+        return null;
+    }
+
     private void resolveOrFail(ClassNode type, ASTNode node, boolean 
prefereImports) {
         resolveGenericsTypes(type.getGenericsTypes());
         if (prefereImports && resolveAliasFromModule(type)) return;
@@ -791,10 +828,10 @@ public class ResolveVisitor extends 
ClassCodeExpressionTransformer {
             }
             return true;
         }
+
         return false;
     }
 
-
     public Expression transform(Expression exp) {
         if (exp == null) return null;
         Expression ret;
@@ -1394,9 +1431,42 @@ public class ResolveVisitor extends 
ClassCodeExpressionTransformer {
         
         super.visitClass(node);
 
+        resolveOuterNestedClassFurther(node);
+
         currentClass = oldNode;
     }
-    
+
+    // GROOVY-7812(#2): Static inner classes cannot be accessed from other 
files when running by 'groovy' command
+    private void resolveOuterNestedClassFurther(ClassNode node) {
+        CompileUnit compileUnit = currentClass.getCompileUnit();
+
+        if (null == compileUnit) return;
+
+        Map<String, ConstructedOuterNestedClassNode> classesToResolve = 
compileUnit.getClassesToResolve();
+        List<String> resolvedInnerClassNameList = new LinkedList<>();
+
+        for (Map.Entry<String, ConstructedOuterNestedClassNode> entry : 
classesToResolve.entrySet()) {
+            String innerClassName = entry.getKey();
+            ConstructedOuterNestedClassNode constructedOuterNestedClass = 
entry.getValue();
+
+            // When the outer class is resolved, all inner classes are 
resolved too
+            if 
(node.getName().equals(constructedOuterNestedClass.getEnclosingClassNode().getName()))
 {
+                ClassNode innerClassNode = 
compileUnit.getClass(innerClassName); // find the resolved inner class
+
+                if (null == innerClassNode) {
+                    return; // "unable to resolve class" error can be thrown 
already, no need to `addError`, so just return
+                }
+
+                constructedOuterNestedClass.setRedirect(innerClassNode);
+                resolvedInnerClassNameList.add(innerClassName);
+            }
+        }
+
+        for (String innerClassName : resolvedInnerClassNameList) {
+            classesToResolve.remove(innerClassName);
+        }
+    }
+
     private void checkCyclicInheritance(ClassNode originalNode, ClassNode 
parentToCompare, ClassNode[] interfacesToCompare) {
         if(!originalNode.isInterface()) {
             if(parentToCompare == null) return;
diff --git a/src/test-resources/groovy/bugs/groovy7812/Main.groovy 
b/src/test-resources/groovy/bugs/groovy7812/Main.groovy
new file mode 100644
index 0000000..16d2fa4
--- /dev/null
+++ b/src/test-resources/groovy/bugs/groovy7812/Main.groovy
@@ -0,0 +1,29 @@
+/*
+ *  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 groovy.bugs.groovy7812
+
+assert new Outer()
+assert new Outer.Inner()
+assert new groovy.bugs.groovy7812.Outer.Inner()
+assert new Outer.Inner.Innest()
+assert new groovy.bugs.groovy7812.Outer.Inner.Innest()
+assert "2" == new Outer.Inner2().innerName
+assert "3" == new Outer.Inner3().innerName
+assert "1.Innest" == new Outer.Inner.Innest().name
+assert "3.Innest" == new Outer.Inner3.Innest().name
diff --git a/src/test-resources/groovy/bugs/groovy7812/MainWithErrors.groovy 
b/src/test-resources/groovy/bugs/groovy7812/MainWithErrors.groovy
new file mode 100644
index 0000000..091a59e
--- /dev/null
+++ b/src/test-resources/groovy/bugs/groovy7812/MainWithErrors.groovy
@@ -0,0 +1,22 @@
+/*
+ *  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 groovy.bugs.groovy7812
+
+assert new Outer()
+assert new Outer.Inner123()
diff --git a/src/test-resources/groovy/bugs/groovy7812/Outer.groovy 
b/src/test-resources/groovy/bugs/groovy7812/Outer.groovy
new file mode 100644
index 0000000..753bbe5
--- /dev/null
+++ b/src/test-resources/groovy/bugs/groovy7812/Outer.groovy
@@ -0,0 +1,36 @@
+/*
+ *  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 groovy.bugs.groovy7812
+
+class Outer {
+    static class Inner {
+        static class Innest {
+            def name = "1.Innest"
+        }
+    }
+    static class Inner2 {
+        def innerName = "2"
+    }
+    static class Inner3 {
+        def innerName = "3"
+        static class Innest {
+            def name = "3.Innest"
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/test/groovy/bugs/Groovy7812Bug.groovy 
b/src/test/groovy/bugs/Groovy7812Bug.groovy
new file mode 100644
index 0000000..ac94ee6
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy7812Bug.groovy
@@ -0,0 +1,52 @@
+/*
+ *  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 groovy.bugs
+
+import org.codehaus.groovy.tools.GroovyStarter
+
+class Groovy7812Bug extends GroovyTestCase {
+    void testResolvingOuterNestedClass() {
+        def mainScriptPath = new 
File(this.getClass().getResource('/groovy/bugs/groovy7812/Main.groovy').toURI()).absolutePath
+        runScript(mainScriptPath)
+    }
+
+//   Even if try to catch `Throwable`, the expected error is thrown all the 
same..., as a result, the test fails due to the weired problem...
+//
+//    org.codehaus.groovy.control.MultipleCompilationErrorsException: startup 
failed:
+//D:\_APPS\git_apps\groovy\out\test\resources\groovy\bugs\groovy7812\MainWithErrors.groovy:
 22: unable to resolve class Outer.Inner123
+// @ line 22, column 8.
+//   assert new Outer.Inner123()
+//          ^
+//
+//1 error
+//
+//    void testUnexistingInnerClass() {
+//        try {
+//            def mainScriptPath = new 
File(this.getClass().getResource('/groovy/bugs/groovy7812/MainWithErrors.groovy').toURI()).absolutePath
+//            runScript(mainScriptPath)
+//        } catch (Throwable t) {
+//            assert t.getMessage().contains('unable to resolve class 
Outer.Inner123')
+//        }
+//    }
+
+
+    static void runScript(String path) {
+        GroovyStarter.main(new String[] { "--main", "groovy.ui.GroovyMain", 
path })
+    }
+}

Reply via email to