You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2019/05/06 22:43:24 UTC

[groovy] branch master updated: GROOVY-9063: @CompileStatic generates invalid bytecode when accessing protected instance member from 2 level of nested closures

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

paulk 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 b3be233  GROOVY-9063: @CompileStatic generates invalid bytecode when accessing protected instance member from 2 level of nested closures
b3be233 is described below

commit b3be233f0232f3bb6760e1f8300dd1792f15c0c9
Author: Paul King <pa...@asert.com.au>
AuthorDate: Tue May 7 08:43:10 2019 +1000

    GROOVY-9063: @CompileStatic generates invalid bytecode when accessing protected instance member from 2 level of nested closures
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 40 +++++++++++++++------
 src/test/groovy/bugs/Groovy9063Bug.groovy          | 42 ++++++++++++++++++++++
 2 files changed, 71 insertions(+), 11 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 8b12515..725a013 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -531,7 +531,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
      */
     private String checkOrMarkInnerPropertyOwnerAccess(Expression source, FieldNode fn, boolean lhsOfAssignment, String delegationData) {
         if (fn == null || fn.isStatic() || fn.isPrivate() || "delegate".equals(delegationData)) return delegationData;
-        if (source instanceof PropertyExpression && typeCheckingContext.getEnclosingClosure() != null) {
+        if (source instanceof PropertyExpression && typeCheckingContext.getEnclosingClosureStack().size() == 1) {
             PropertyExpression pe = (PropertyExpression) source;
             boolean ownerProperty = !("this".equals(pe.getPropertyAsString()));
             if (ownerProperty && pe.getObjectExpression() instanceof VariableExpression) {
@@ -699,16 +699,23 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
         TypeCheckingContext.EnclosingClosure enclosingClosure = typeCheckingContext.getEnclosingClosure();
         if (enclosingClosure != null) {
-            String name = vexp.getName();
-            if (name.equals("owner") || name.equals("thisObject")) {
-                storeType(vexp, typeCheckingContext.getEnclosingClassNode());
-                return;
-            } else if ("delegate".equals(name)) {
-                DelegationMetadata md = getDelegationMetadata(enclosingClosure.getClosureExpression());
-                ClassNode type = typeCheckingContext.getEnclosingClassNode();
-                if (md != null) type = md.getType();
-                storeType(vexp, type);
-                return;
+            switch (vexp.getName()) {
+                case "delegate":
+                    DelegationMetadata md = getDelegationMetadata(enclosingClosure.getClosureExpression());
+                    if (md != null) {
+                        storeType(vexp, md.getType());
+                        return;
+                    }
+                    // falls through
+                case "owner":
+                    if (typeCheckingContext.getEnclosingClosureStack().size() > 1) {
+                        storeType(vexp, CLOSURE_TYPE);
+                        return;
+                    }
+                    // falls through
+                case "thisObject":
+                    storeType(vexp, typeCheckingContext.getEnclosingClassNode());
+                    return;
             }
         }
 
@@ -3299,6 +3306,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                     if (dmd.getParent() == null) {
                         receivers.addAll(owners);
                     } else {
+                        //receivers.add(new Receiver<String>(CLOSURE_TYPE, path + "owner"));
                         addReceivers(receivers, owners, dmd.getParent(), path + "owner.");
                     }
                 }
@@ -3308,6 +3316,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 if (dmd.getParent() == null) {
                     receivers.addAll(owners);
                 } else {
+                    //receivers.add(new Receiver<String>(CLOSURE_TYPE, path + "owner"));
                     addReceivers(receivers, owners, dmd.getParent(), path + "owner.");
                 }
                 if (strategy == Closure.OWNER_FIRST) {
@@ -3808,6 +3817,15 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 && ((VariableExpression) objectExpression).getName().equals("it")) {
             owners.add(Receiver.<String>make(typeCheckingContext.lastImplicitItType));
         }
+        if (typeCheckingContext.delegationMetadata != null
+                && objectExpression instanceof VariableExpression
+                && ((VariableExpression) objectExpression).getName().equals("owner")
+                && /*isNested:*/typeCheckingContext.delegationMetadata.getParent() != null) {
+            owners.clear();
+            List<Receiver<String>> enclosingClass = Collections.singletonList(
+                    Receiver.<String>make(typeCheckingContext.getEnclosingClassNode()));
+            addReceivers(owners, enclosingClass, typeCheckingContext.delegationMetadata.getParent(), "owner.");
+        }
         return owners;
     }
 
diff --git a/src/test/groovy/bugs/Groovy9063Bug.groovy b/src/test/groovy/bugs/Groovy9063Bug.groovy
new file mode 100644
index 0000000..9374e82
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy9063Bug.groovy
@@ -0,0 +1,42 @@
+/*
+ *  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 Groovy9063Bug extends GroovyTestCase {
+    void testProtectedFieldInClosureInEnum() {
+        assertScript '''
+            import groovy.transform.CompileStatic
+
+            @CompileStatic
+            class Groovy9063 {
+                protected String message = "hello"
+
+                int nestedClosures() {
+                    { ->
+                        { ->
+                            message.length()
+                        }.call()
+                    }.call()
+                }
+            }
+
+            assert new Groovy9063().nestedClosures() == 5
+        '''
+    }
+}