You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2019/09/04 11:52:57 UTC
[groovy] branch master updated: GROOVY-9243: Fail to resolve nested
type defined in base type written in Groovy
This is an automated email from the ASF dual-hosted git repository.
sunlan 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 89c4dfa GROOVY-9243: Fail to resolve nested type defined in base type written in Groovy
89c4dfa is described below
commit 89c4dfa95777fb40af362730f12a81f9daf863b8
Author: Daniel Sun <su...@apache.org>
AuthorDate: Tue Sep 3 00:15:16 2019 +0800
GROOVY-9243: Fail to resolve nested type defined in base type written in Groovy
---
.../java/org/codehaus/groovy/ast/CompileUnit.java | 16 +++++-
.../codehaus/groovy/control/ResolveVisitor.java | 67 +++++++++++++++++-----
.../groovy/bugs/groovy9243/Base.groovy | 29 ++++++++++
.../groovy/bugs/groovy9243/Main.groovy | 49 ++++++++++++++++
src/test/groovy/bugs/Groovy9243Bug.groovy | 32 +++++++++++
5 files changed, 178 insertions(+), 15 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/ast/CompileUnit.java b/src/main/java/org/codehaus/groovy/ast/CompileUnit.java
index 79f76f6..656cc23 100644
--- a/src/main/java/org/codehaus/groovy/ast/CompileUnit.java
+++ b/src/main/java/org/codehaus/groovy/ast/CompileUnit.java
@@ -34,6 +34,7 @@ import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.BiConsumer;
/**
* Represents the entire contents of a compilation step which consists of one or more
@@ -186,7 +187,7 @@ public class CompileUnit implements NodeMetaDataHandler {
public InnerClassNode getGeneratedInnerClass(String name) {
return generatedInnerClasses.get(name);
}
-
+
public void addGeneratedInnerClass(InnerClassNode icn) {
generatedInnerClasses.put(icn.getName(), icn);
}
@@ -228,6 +229,7 @@ public class CompileUnit implements NodeMetaDataHandler {
@Internal
public static class ConstructedOuterNestedClassNode extends ClassNode {
private final ClassNode enclosingClassNode;
+ private final List<BiConsumer<ConstructedOuterNestedClassNode, ClassNode>> setRedirectListenerList = new ArrayList<>();
public ConstructedOuterNestedClassNode(ClassNode outer, String innerClassName) {
super(innerClassName, Opcodes.ACC_PUBLIC, ClassHelper.OBJECT_TYPE);
@@ -235,8 +237,20 @@ public class CompileUnit implements NodeMetaDataHandler {
this.isPrimaryNode = false;
}
+ @Override
+ public void setRedirect(ClassNode cn) {
+ for (BiConsumer<ConstructedOuterNestedClassNode, ClassNode> setRedirectListener : setRedirectListenerList) {
+ setRedirectListener.accept(this, cn);
+ }
+ super.setRedirect(cn);
+ }
+
public ClassNode getEnclosingClassNode() {
return enclosingClassNode;
}
+
+ public void addSetRedirectListener(BiConsumer<ConstructedOuterNestedClassNode, ClassNode> setRedirectListener) {
+ setRedirectListenerList.add(setRedirectListener);
+ }
}
}
diff --git a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
index 9f6cf24..958a233 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.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.BiConsumer;
import static org.codehaus.groovy.ast.CompileUnit.ConstructedOuterNestedClassNode;
import static org.codehaus.groovy.ast.GenericsType.GenericsTypeName;
@@ -354,10 +355,12 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
// 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) {
+ private boolean resolveToOuterNested(final ClassNode type) {
CompileUnit compileUnit = currentClass.getCompileUnit();
String typeName = type.getName();
+ BiConsumer<ConstructedOuterNestedClassNode, ClassNode> setRedirectListener = (s, c) -> type.setRedirect(s);
+
ModuleNode module = currentClass.getModule();
for (ImportNode importNode : module.getStaticImports().values()) {
String importFieldName = importNode.getFieldName();
@@ -365,7 +368,7 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
if (!typeName.equals(importAlias)) continue;
- ConstructedOuterNestedClassNode constructedOuterNestedClassNode = tryToConstructOuterNestedClassNodeViaStaticImport(compileUnit, importNode, importFieldName);
+ ConstructedOuterNestedClassNode constructedOuterNestedClassNode = tryToConstructOuterNestedClassNodeViaStaticImport(compileUnit, importNode, importFieldName, setRedirectListener);
if (null != constructedOuterNestedClassNode) {
compileUnit.addClassNodeToResolve(constructedOuterNestedClassNode);
return true;
@@ -374,7 +377,7 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
for (Map.Entry<String, ClassNode> entry : compileUnit.getClassesToCompile().entrySet()) {
ClassNode outerClassNode = entry.getValue();
- ConstructedOuterNestedClassNode constructedOuterNestedClassNode = tryToConstructOuterNestedClassNode(type, outerClassNode);
+ ConstructedOuterNestedClassNode constructedOuterNestedClassNode = tryToConstructOuterNestedClassNode(type, outerClassNode, setRedirectListener);
if (null != constructedOuterNestedClassNode) {
compileUnit.addClassNodeToResolve(constructedOuterNestedClassNode);
return true;
@@ -383,40 +386,71 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
boolean toResolveFurther = false;
for (ImportNode importNode : module.getStaticStarImports().values()) {
- ConstructedOuterNestedClassNode constructedOuterNestedClassNode = tryToConstructOuterNestedClassNodeViaStaticImport(compileUnit, importNode, typeName);
+ ConstructedOuterNestedClassNode constructedOuterNestedClassNode = tryToConstructOuterNestedClassNodeViaStaticImport(compileUnit, importNode, typeName, setRedirectListener);
if (null != constructedOuterNestedClassNode) {
compileUnit.addClassNodeToResolve(constructedOuterNestedClassNode);
toResolveFurther = true; // do not return here to try all static star imports because currently we do not know which outer class the class to resolve is declared in.
}
}
+ if (toResolveFurther) return true;
+
+ // GROOVY-9243
+ toResolveFurther = false;
+ if (!whetherClassNameContainsDot(typeName)) {
+ Map<String, ClassNode> hierClasses = findHierClasses(currentClass);
+ for (ClassNode cn : hierClasses.values()) {
+ ConstructedOuterNestedClassNode constructedOuterNestedClassNode = tryToConstructOuterNestedClassNodeForBaseType(compileUnit, typeName, cn, setRedirectListener);
+ if (null != constructedOuterNestedClassNode) {
+ compileUnit.addClassNodeToResolve(constructedOuterNestedClassNode);
+ toResolveFurther = true;
+ }
+ }
+ }
return toResolveFurther;
}
- private ConstructedOuterNestedClassNode tryToConstructOuterNestedClassNodeViaStaticImport(CompileUnit compileUnit, ImportNode importNode, String typeName) {
+ private static boolean whetherClassNameContainsDot(String typeName) {
+ return typeName.contains(".");
+ }
+
+ private ConstructedOuterNestedClassNode tryToConstructOuterNestedClassNodeViaStaticImport(CompileUnit compileUnit, ImportNode importNode, String typeName, BiConsumer<ConstructedOuterNestedClassNode, ClassNode> setRedirectListener) {
String importClassName = importNode.getClassName();
ClassNode outerClassNode = compileUnit.getClass(importClassName);
if (null == outerClassNode) return null;
String outerNestedClassName = importClassName + "$" + typeName.replace(".", "$");
- return new ConstructedOuterNestedClassNode(outerClassNode, outerNestedClassName);
+ ConstructedOuterNestedClassNode constructedOuterNestedClassNode = new ConstructedOuterNestedClassNode(outerClassNode, outerNestedClassName);
+ constructedOuterNestedClassNode.addSetRedirectListener(setRedirectListener);
+ return constructedOuterNestedClassNode;
}
- private ConstructedOuterNestedClassNode tryToConstructOuterNestedClassNode(ClassNode type, ClassNode outerClassNode) {
+ private ConstructedOuterNestedClassNode tryToConstructOuterNestedClassNode(final ClassNode type, ClassNode outerClassNode, BiConsumer<ConstructedOuterNestedClassNode, ClassNode> setRedirectListener) {
String outerClassName = outerClassNode.getName();
for (String typeName = type.getName(), ident = typeName; ident.contains("."); ) {
ident = ident.substring(0, ident.lastIndexOf("."));
if (outerClassName.endsWith(ident)) {
String outerNestedClassName = outerClassName + typeName.substring(ident.length()).replace(".", "$");
- return new ConstructedOuterNestedClassNode(outerClassNode, outerNestedClassName);
+ ConstructedOuterNestedClassNode constructedOuterNestedClassNode = new ConstructedOuterNestedClassNode(outerClassNode, outerNestedClassName);
+ constructedOuterNestedClassNode.addSetRedirectListener(setRedirectListener);
+ return constructedOuterNestedClassNode;
}
}
return null;
}
+ private ConstructedOuterNestedClassNode tryToConstructOuterNestedClassNodeForBaseType(CompileUnit compileUnit, String typeName, ClassNode cn, BiConsumer<ConstructedOuterNestedClassNode, ClassNode> setRedirectListener) {
+ if (!compileUnit.getClassesToCompile().containsValue(cn)) return null;
+
+ String outerNestedClassName = cn.getName() + "$" + typeName;
+ ConstructedOuterNestedClassNode constructedOuterNestedClassNode = new ConstructedOuterNestedClassNode(cn, outerNestedClassName);
+ constructedOuterNestedClassNode.addSetRedirectListener(setRedirectListener);
+ return constructedOuterNestedClassNode;
+ }
+
private void resolveOrFail(ClassNode type, ASTNode node, boolean prefereImports) {
resolveGenericsTypes(type.getGenericsTypes());
if (prefereImports && resolveAliasFromModule(type)) return;
@@ -477,12 +511,7 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
// to access that class directly, so A becomes a valid
// name in X.
// GROOVY-4043: Do this check up the hierarchy, if needed
- Map<String, ClassNode> hierClasses = new LinkedHashMap<String, ClassNode>();
- for(ClassNode classToCheck = currentClass; classToCheck != ClassHelper.OBJECT_TYPE;
- classToCheck = classToCheck.getSuperClass()) {
- if(classToCheck == null || hierClasses.containsKey(classToCheck.getName())) break;
- hierClasses.put(classToCheck.getName(), classToCheck);
- }
+ Map<String, ClassNode> hierClasses = findHierClasses(currentClass);
for (ClassNode classToCheck : hierClasses.values()) {
if (setRedirect(type, classToCheck)) return true;
@@ -1706,4 +1735,14 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
public void setClassNodeResolver(ClassNodeResolver classNodeResolver) {
this.classNodeResolver = classNodeResolver;
}
+
+ private static Map<String, ClassNode> findHierClasses(ClassNode currentClass) {
+ Map<String, ClassNode> hierClasses = new LinkedHashMap<String, ClassNode>();
+ for(ClassNode classToCheck = currentClass; classToCheck != ClassHelper.OBJECT_TYPE;
+ classToCheck = classToCheck.getSuperClass()) {
+ if(classToCheck == null || hierClasses.containsKey(classToCheck.getName())) break;
+ hierClasses.put(classToCheck.getName(), classToCheck);
+ }
+ return hierClasses;
+ }
}
diff --git a/src/test-resources/groovy/bugs/groovy9243/Base.groovy b/src/test-resources/groovy/bugs/groovy9243/Base.groovy
new file mode 100644
index 0000000..3ee9acb
--- /dev/null
+++ b/src/test-resources/groovy/bugs/groovy9243/Base.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.groovy9243
+
+class Base {
+ static class X {
+ def name = "staticClassX"
+ }
+
+ class Y {
+ def name = "classY"
+ }
+}
diff --git a/src/test-resources/groovy/bugs/groovy9243/Main.groovy b/src/test-resources/groovy/bugs/groovy9243/Main.groovy
new file mode 100644
index 0000000..72b8d47
--- /dev/null
+++ b/src/test-resources/groovy/bugs/groovy9243/Main.groovy
@@ -0,0 +1,49 @@
+/*
+ * 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.groovy9243
+
+class Groovy9243 extends Base {
+ def accessX() {
+ assert 'staticClassX' == new X().name
+ }
+ def accessBaseX() {
+ assert 'staticClassX' == new Base.X().name
+ }
+ def accessBaseX2() {
+ assert 'staticClassX' == new groovy.bugs.groovy9243.Base.X().name
+ }
+ def accessY() {
+ assert 'classY' == this.new Y().name
+ }
+ def accessY2() {
+ def g = new Groovy9243()
+ assert 'classY' == g.new Y().name
+ }
+ def accessY3() {
+ assert 'classY' == new Groovy9243().new Y().name
+ }
+}
+
+def g = new Groovy9243()
+g.accessX()
+g.accessBaseX()
+g.accessBaseX2()
+g.accessY()
+g.accessY2()
+g.accessY3()
diff --git a/src/test/groovy/bugs/Groovy9243Bug.groovy b/src/test/groovy/bugs/Groovy9243Bug.groovy
new file mode 100644
index 0000000..e0fb10b
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy9243Bug.groovy
@@ -0,0 +1,32 @@
+/*
+ * 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 Groovy9243Bug extends GroovyTestCase {
+ void testResolveNestedClassFromBaseType() {
+ def mainScriptPath = new File(this.getClass().getResource('/groovy/bugs/groovy9243/Main.groovy').toURI()).absolutePath
+ runScript(mainScriptPath)
+ }
+
+ static void runScript(String path) {
+ GroovyStarter.main(new String[] { "--main", "groovy.ui.GroovyMain", path })
+ }
+}