GROOVY-7973: Class.this not evaluated correctly within a closure within an 
inner class (closes #451)


Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/5714ed7f
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/5714ed7f
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/5714ed7f

Branch: refs/heads/parrot
Commit: 5714ed7f8a4f90ad89a831172f7f2f1a72979d82
Parents: b0406dd
Author: paulk <pa...@asert.com.au>
Authored: Thu Oct 27 20:51:22 2016 +1000
Committer: paulk <pa...@asert.com.au>
Committed: Fri Oct 28 14:12:54 2016 +1000

----------------------------------------------------------------------
 .../groovy/classgen/AsmClassGenerator.java      | 17 ++--
 .../stc/StaticTypeCheckingVisitor.java          | 18 ++--
 src/test/groovy/bugs/Groovy7973Bug.groovy       | 94 ++++++++++++++++++++
 3 files changed, 116 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/5714ed7f/src/main/org/codehaus/groovy/classgen/AsmClassGenerator.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/classgen/AsmClassGenerator.java 
b/src/main/org/codehaus/groovy/classgen/AsmClassGenerator.java
index 3fb441a..849bf6f 100644
--- a/src/main/org/codehaus/groovy/classgen/AsmClassGenerator.java
+++ b/src/main/org/codehaus/groovy/classgen/AsmClassGenerator.java
@@ -961,16 +961,21 @@ public class AsmClassGenerator extends ClassGenerator {
                 String ownerName = 
BytecodeHelper.getClassInternalName(iterType);
                 if (iterType.getOuterClass()==null) break;
                 FieldNode thisField = iterType.getField("this$0");
-                if (thisField==null) break;
-                ClassNode thisFieldType = thisField.getType();
                 iterType = iterType.getOuterClass();
-                if (ClassHelper.CLOSURE_TYPE.equals(thisFieldType)) {
-                    mv.visitFieldInsn(GETFIELD, ownerName, "this$0", 
BytecodeHelper.getTypeDescription(ClassHelper.CLOSURE_TYPE));
+                if (thisField == null) {
+                    // closure within inner class
                     mv.visitMethodInsn(INVOKEVIRTUAL, 
BytecodeHelper.getClassInternalName(ClassHelper.CLOSURE_TYPE), "getThisObject", 
"()Ljava/lang/Object;", false);
                     mv.visitTypeInsn(CHECKCAST, 
BytecodeHelper.getClassInternalName(iterType));
                 } else {
-                    String typeName = 
BytecodeHelper.getTypeDescription(iterType);
-                    mv.visitFieldInsn(GETFIELD, ownerName, "this$0", typeName);
+                    ClassNode thisFieldType = thisField.getType();
+                    if (ClassHelper.CLOSURE_TYPE.equals(thisFieldType)) {
+                        mv.visitFieldInsn(GETFIELD, ownerName, "this$0", 
BytecodeHelper.getTypeDescription(ClassHelper.CLOSURE_TYPE));
+                        mv.visitMethodInsn(INVOKEVIRTUAL, 
BytecodeHelper.getClassInternalName(ClassHelper.CLOSURE_TYPE), "getThisObject", 
"()Ljava/lang/Object;", false);
+                        mv.visitTypeInsn(CHECKCAST, 
BytecodeHelper.getClassInternalName(iterType));
+                    } else {
+                        String typeName = 
BytecodeHelper.getTypeDescription(iterType);
+                        mv.visitFieldInsn(GETFIELD, ownerName, "this$0", 
typeName);
+                    }
                 }
             }
             controller.getOperandStack().push(type);

http://git-wip-us.apache.org/repos/asf/groovy/blob/5714ed7f/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
----------------------------------------------------------------------
diff --git 
a/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java 
b/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index d5259b6..dc8f63d 100644
--- a/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -1179,16 +1179,20 @@ public class StaticTypeCheckingVisitor extends 
ClassCodeVisitorSupport {
 
         boolean staticOnlyAccess = 
isClassClassNodeWrappingConcreteType(objectExpressionType);
         if ("this".equals(propertyName) && staticOnlyAccess) {
-            // Outer.this
+            // Outer.this for any level of nesting
             ClassNode outerNode = 
objectExpressionType.getGenericsTypes()[0].getType();
-            ClassNode current = typeCheckingContext.getEnclosingClassNode();
-            if (!current.isStaticClass() && current instanceof InnerClassNode) 
{
-                InnerClassNode icn = (InnerClassNode) current;
-                if (outerNode.equals(icn.getOuterClass())) {
-                    storeType(pexp, outerNode);
-                    return true;
+            List<ClassNode> candidates = 
typeCheckingContext.getEnclosingClassNodes();
+            ClassNode found = null;
+            for (ClassNode current : candidates) {
+                if (!current.isStaticClass() && current instanceof 
InnerClassNode && outerNode.equals(current.getOuterClass())) {
+                    found = current;
+                    break;
                 }
             }
+            if (found != null) {
+                storeType(pexp, outerNode);
+                return true;
+            }
         }
 
         if (objectExpressionType.isArray() && 
"length".equals(pexp.getPropertyAsString())) {

http://git-wip-us.apache.org/repos/asf/groovy/blob/5714ed7f/src/test/groovy/bugs/Groovy7973Bug.groovy
----------------------------------------------------------------------
diff --git a/src/test/groovy/bugs/Groovy7973Bug.groovy 
b/src/test/groovy/bugs/Groovy7973Bug.groovy
new file mode 100644
index 0000000..a6ab96c
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy7973Bug.groovy
@@ -0,0 +1,94 @@
+/*
+ *  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
+
+class Groovy7973Bug extends GroovyTestCase {
+
+    private static final String SCRIPT1 = '''
+        class Test {
+            def op1() { 'A'.with{ this.class.name } }
+            def op2a() { new Object() { def inner() {
+                this.class.name + Test.this.class.name
+            } } }
+            def op2b() { new Object() { def inner() {
+                'B'.with{ this.class.name + Test.this.class.name }
+            } } }
+            def op3a() { new Object() { def inner() { new Object() { def 
innerinner() {
+                this.class.name + Test$3.this.class.name + Test.this.class.name
+            } } } } }
+            def op3b() { new Object() { def inner() { new Object() { def 
innerinner() {
+                'C'.with{ this.class.name + Test$5.this.class.name + 
Test.this.class.name }
+            } } } } }
+        }
+
+        def t = new Test()
+        assert t.op1() == 'Test'
+        assert t.op2a().inner() == 'Test$1Test'
+        assert t.op2b().inner() == 'Test$2Test'
+        assert t.op3a().inner().innerinner() == 'Test$3$4Test$3Test'
+        assert t.op3b().inner().innerinner() == 'Test$5$6Test$5Test'
+    '''
+
+    private static final String SCRIPT2 = '''
+        class Test {
+            def op1() { this }
+            def op2() { ''.with{ this } }
+            def op3() { new Object() { def inner() { this } } }
+            def op4() { new Object() { def inner() { ''.with{ this } } } }
+            def op5() { new Object() { def inner() { Test.this } } }
+            def op6() { new Object() { def inner() { ''.with{ Test.this } } } }
+            class Inner {
+                def inner1() { this }
+                def inner2() { ''.with { this } }
+                def inner3() { Test.this }
+                def inner4() { ''.with { Test.this } }
+            }
+        }
+
+        def t = new Test()
+        assert t.op1().class.name == 'Test'
+        assert t.op2().class.name == 'Test'
+        assert t.op3().inner().class.name == 'Test$1'
+        assert t.op4().inner().class.name == 'Test$2'
+        assert t.op5().inner().class.name == 'Test'
+        assert t.op6().inner().class.name == 'Test'
+
+        def inner = new Test.Inner(t)
+        assert inner.inner1().class.name == 'Test$Inner'
+        assert inner.inner2().class.name == 'Test$Inner'
+        assert inner.inner3().class.name == 'Test'
+        assert inner.inner4().class.name == 'Test'
+    '''
+
+    void testClassDotThis() {
+        assertScript SCRIPT1
+    }
+
+    void testClassDotThis_CS() {
+        assertScript '@groovy.transform.CompileStatic\n' + SCRIPT1
+    }
+
+    void testClassDotThisAIC() {
+        assertScript SCRIPT2
+    }
+
+    void testClassDotThisAIC_CS() {
+        assertScript '@groovy.transform.CompileStatic\n' + SCRIPT2
+    }
+}

Reply via email to