You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/01/07 20:40:44 UTC

[groovy] branch GROOVY-5001 created (now 770e2b8)

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

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


      at 770e2b8  GROOVY-5001, GROOVY-5491: map property precedence

This branch includes the following new commits:

     new 770e2b8  GROOVY-5001, GROOVY-5491: map property precedence

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[groovy] 01/01: GROOVY-5001, GROOVY-5491: map property precedence

Posted by em...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 770e2b805bffc6dc25860b6868e463e043e4b35b
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Jan 7 13:13:30 2022 -0600

    GROOVY-5001, GROOVY-5491: map property precedence
    
    see also GROOVY-662, GROOVY-8065, GROOVY-8074
---
 src/main/java/groovy/lang/MetaClassImpl.java       | 221 ++++++++++-----------
 .../classgen/asm/sc/StaticTypesCallSiteWriter.java |  12 +-
 src/test/groovy/MapTest.groovy                     | 105 ++++++++--
 src/test/groovy/bugs/Groovy662.groovy              |  90 +++++++++
 src/test/groovy/bugs/Groovy662Bug.groovy           |  96 ---------
 .../{Groovy8065Bug.groovy => Groovy8065.groovy}    |  25 ++-
 .../stc/ArraysAndCollectionsSTCTest.groovy         |  23 ++-
 .../ArraysAndCollectionsStaticCompileTest.groovy   |  13 --
 8 files changed, 320 insertions(+), 265 deletions(-)

diff --git a/src/main/java/groovy/lang/MetaClassImpl.java b/src/main/java/groovy/lang/MetaClassImpl.java
index 93f78ee..f40b88f 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -1959,100 +1959,105 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
      * @return the given property's value on the object
      */
     @Override
-    public Object getProperty(Class sender, Object object, String name, boolean useSuper, boolean fromInsideClass) {
+    public Object getProperty(final Class sender, final Object object, final String name, final boolean useSuper, final boolean fromInsideClass) {
 
         //----------------------------------------------------------------------
         // handling of static
         //----------------------------------------------------------------------
-        boolean isStatic = theClass != Class.class && object instanceof Class;
+        boolean isStatic = (theClass != Class.class && object instanceof Class);
         if (isStatic && object != theClass) {
-            MetaClass mc = registry.getMetaClass((Class) object);
+            MetaClass mc = registry.getMetaClass((Class<?>) object);
             return mc.getProperty(sender, object, name, useSuper, false);
         }
 
         checkInitalised();
 
         //----------------------------------------------------------------------
-        // turn getProperty on a Map to get on the Map itself
+        // getter
         //----------------------------------------------------------------------
-        if (!isStatic && this.isMap) {
-            return ((Map) object).get(name);
-        }
-
         Tuple2<MetaMethod, MetaProperty> methodAndProperty = createMetaMethodAndMetaProperty(sender, sender, name, useSuper, isStatic);
         MetaMethod method = methodAndProperty.getV1();
 
-        //----------------------------------------------------------------------
-        // getter
-        //----------------------------------------------------------------------
-        MetaProperty mp = methodAndProperty.getV2();
+        if (method == null || "class".equals(name) || (isMap && "empty".equals(name))) {
+            //------------------------------------------------------------------
+            // public field
+            //------------------------------------------------------------------
+            MetaProperty mp = methodAndProperty.getV2();
+            if (mp != null && Modifier.isPublic(mp.getModifiers())) {
+                try {
+                    return mp.getProperty(object);
+                } catch (IllegalArgumentException | CacheAccessControlException e) {
+                    // can't access the field directly but there may be a getter
+                    mp = null;
+                }
+            }
 
-        //----------------------------------------------------------------------
-        // field
-        //----------------------------------------------------------------------
-        if (method == null && mp != null) {
-            try {
-                return mp.getProperty(object);
-            } catch (IllegalArgumentException | CacheAccessControlException e) {
-                // can't access the field directly but there may be a getter
-                mp = null;
+            //------------------------------------------------------------------
+            // java.util.Map get method
+            //------------------------------------------------------------------
+            if (isMap && !isStatic) {
+                return ((Map<?,?>) object).get(name);
             }
-        }
 
-        // check for propertyMissing provided through a category
-        Object[] arguments = EMPTY_ARGUMENTS;
-        if (method == null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInCurrentThread()) {
-            method = getCategoryMethodGetter(sender, PROPERTY_MISSING, true);
-            if (method != null) arguments = new Object[]{name};
+            //------------------------------------------------------------------
+            // non-public field
+            //------------------------------------------------------------------
+            if (mp != null) {
+                try {
+                    return mp.getProperty(object);
+                } catch (IllegalArgumentException | CacheAccessControlException e) {
+                }
+            }
         }
 
-
         //----------------------------------------------------------------------
-        // generic get method
+        // propertyMissing (via category) or generic get method
         //----------------------------------------------------------------------
-        // check for a generic get method provided through a category
+        Object[] arguments = EMPTY_ARGUMENTS;
         if (method == null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInCurrentThread()) {
-            method = getCategoryMethodGetter(sender, "get", true);
+            // check for propertyMissing provided through a category; TODO:should this have lower precedence?
+            method = getCategoryMethodGetter(sender, PROPERTY_MISSING, true);
+            if (method == null) {
+                // check for a generic get method provided through a category
+                method = getCategoryMethodGetter(sender, "get", true);
+            }
             if (method != null) arguments = new Object[]{name};
         }
-
-        // the generic method is valid, if available (!=null), if static or
-        // if it is not static and we do no static access
-        if (method == null && genericGetMethod != null && !(!genericGetMethod.isStatic() && isStatic)) {
+        if (method == null && genericGetMethod != null && (genericGetMethod.isStatic() || !isStatic)) {
             arguments = new Object[]{name};
             method = genericGetMethod;
         }
 
-        //----------------------------------------------------------------------
-        // special cases
-        //----------------------------------------------------------------------
         if (method == null) {
-            /* todo these special cases should be special MetaClasses maybe */
+            //------------------------------------------------------------------
+            // special cases
+            //------------------------------------------------------------------
+            // TODO: maybe these special cases should be special MetaClasses
             if (theClass != Class.class && object instanceof Class) {
                 MetaClass mc = registry.getMetaClass(Class.class);
                 return mc.getProperty(Class.class, object, name, useSuper, false);
             }
             if (object instanceof Collection) {
-                return DefaultGroovyMethods.getAt((Collection) object, name);
+                return DefaultGroovyMethods.getAt((Collection<?>) object, name);
             }
             if (object instanceof Object[]) {
                 return DefaultGroovyMethods.getAt(Arrays.asList((Object[]) object), name);
             }
             MetaMethod addListenerMethod = listeners.get(name);
             if (addListenerMethod != null) {
-                //TODO: one day we could try return the previously registered Closure listener for easy removal
+                // TODO: one day we could try return the previously registered Closure listener for easy removal
                 return null;
             }
         } else {
-            //----------------------------------------------------------------------
-            // executing the getter method
-            //----------------------------------------------------------------------
+            //------------------------------------------------------------------
+            // executing the method
+            //------------------------------------------------------------------
             MetaMethod transformedMetaMethod = VM_PLUGIN.transformMetaMethod(this, method);
             return transformedMetaMethod.doMethodInvoke(object, arguments);
         }
 
         //----------------------------------------------------------------------
-        // error due to missing method/field
+        // missing property protocol
         //----------------------------------------------------------------------
         if (isStatic || object instanceof Class) {
             return invokeStaticMissingProperty(object, name, null, true);
@@ -2065,13 +2070,12 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
         //----------------------------------------------------------------------
         // handling of static
         //----------------------------------------------------------------------
-        boolean isStatic = theClass != Class.class && object instanceof Class;
+        boolean isStatic = (theClass != Class.class && object instanceof Class);
         if (isStatic && object != theClass) {
             return new MetaProperty(name, Object.class) {
-                final MetaClass mc = registry.getMetaClass((Class) object);
-
                 @Override
                 public Object getProperty(Object object) {
+                    MetaClass mc = registry.getMetaClass((Class<?>) object);
                     return mc.getProperty(sender, object, name, useSuper, false);
                 }
 
@@ -2085,73 +2089,65 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
         checkInitalised();
 
         //----------------------------------------------------------------------
-        // turn getProperty on a Map to get on the Map itself
+        // getter
         //----------------------------------------------------------------------
-        if (!isStatic && this.isMap) {
-            return new MetaProperty(name, Object.class) {
-                @Override
-                public Object getProperty(Object object) {
-                    return ((Map) object).get(name);
-                }
-
-                @Override
-                public void setProperty(Object object, Object newValue) {
-                    throw new UnsupportedOperationException();
-                }
-            };
-        }
-
         Tuple2<MetaMethod, MetaProperty> methodAndProperty = createMetaMethodAndMetaProperty(sender, theClass, name, useSuper, isStatic);
         MetaMethod method = methodAndProperty.getV1();
 
-        //----------------------------------------------------------------------
-        // getter
-        //----------------------------------------------------------------------
-        MetaProperty mp = methodAndProperty.getV2();
+        if (method == null || "class".equals(name) || (isMap && "empty".equals(name))) {
+            //------------------------------------------------------------------
+            // public field
+            //------------------------------------------------------------------
+            MetaProperty mp = methodAndProperty.getV2();
+            if (mp != null && Modifier.isPublic(mp.getModifiers())) {
+                return mp;
+            }
 
-        //----------------------------------------------------------------------
-        // field
-        //----------------------------------------------------------------------
-        if (method != null) {
-            MetaMethod transformedMetaMethod = VM_PLUGIN.transformMetaMethod(this, method);
-            return new GetBeanMethodMetaProperty(name, transformedMetaMethod);
+            //----------------------------------------------------------------------
+            // java.util.Map get method
+            //----------------------------------------------------------------------
+            if (isMap && !isStatic) {
+                return new MetaProperty(name, Object.class) {
+                    @Override
+                    public Object getProperty(Object object) {
+                        return ((Map<?,?>) object).get(name);
+                    }
+
+                    @Override
+                    public void setProperty(Object object, Object newValue) {
+                        throw new UnsupportedOperationException();
+                    }
+                };
+            }
+
+            //------------------------------------------------------------------
+            // non-public field
+            //------------------------------------------------------------------
+            if (mp != null) {
+                return mp;
+            }
         }
 
-        if (mp != null) {
-            return mp;
-//            try {
-//                return mp.getProperty(object);
-//            } catch (IllegalArgumentException e) {
-//                // can't access the field directly but there may be a getter
-//                mp = null;
-//            }
+        if (method != null) {
+            return new GetBeanMethodMetaProperty(name, VM_PLUGIN.transformMetaMethod(this, method));
         }
 
         //----------------------------------------------------------------------
         // generic get method
         //----------------------------------------------------------------------
-        // check for a generic get method provided through a category
         if (!useSuper && !isStatic && GroovyCategorySupport.hasCategoryInCurrentThread()) {
             method = getCategoryMethodGetter(sender, "get", true);
             if (method != null) {
-                MetaMethod transformedMetaMethod = VM_PLUGIN.transformMetaMethod(this, method);
-                return new GetMethodMetaProperty(name, transformedMetaMethod);
+                return new GetMethodMetaProperty(name, VM_PLUGIN.transformMetaMethod(this, method));
             }
-
         }
-
-        // the generic method is valid, if available (!=null), if static or
-        // if it is not static and we do no static access
-        if (genericGetMethod != null && !(!genericGetMethod.isStatic() && isStatic)) {
-            method = genericGetMethod;
-            MetaMethod transformedMetaMethod = VM_PLUGIN.transformMetaMethod(this, method);
-            return new GetMethodMetaProperty(name, transformedMetaMethod);
+        if (genericGetMethod != null && (genericGetMethod.isStatic() || !isStatic)) {
+            return new GetMethodMetaProperty(name, VM_PLUGIN.transformMetaMethod(this, genericGetMethod));
         }
 
         //----------------------------------------------------------------------
         // special cases
         //----------------------------------------------------------------------
-        /* todo these special cases should be special MetaClasses maybe */
         if (theClass != Class.class && object instanceof Class) {
             return new MetaProperty(name, Object.class) {
                 @Override
@@ -2170,7 +2166,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
             return new MetaProperty(name, Object.class) {
                 @Override
                 public Object getProperty(Object object) {
-                    return DefaultGroovyMethods.getAt((Collection) object, name);
+                    return DefaultGroovyMethods.getAt((Collection<?>) object, name);
                 }
 
                 @Override
@@ -2194,7 +2190,6 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
         }
         MetaMethod addListenerMethod = listeners.get(name);
         if (addListenerMethod != null) {
-            //TODO: one day we could try return the previously registered Closure listener for easy removal
             return new MetaProperty(name, Object.class) {
                 @Override
                 public Object getProperty(Object object) {
@@ -2240,7 +2235,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
     private Tuple2<MetaMethod, MetaProperty> createMetaMethodAndMetaProperty(final Class senderForMP, final Class senderForCMG, final String name, final boolean useSuper, final boolean isStatic) {
         MetaMethod method = null;
         MetaProperty mp = getMetaProperty(senderForMP, name, useSuper, isStatic);
-        if ((mp == null || mp instanceof CachedField) && isUpperCase(name.charAt(0)) && (name.length() < 2 || !isUpperCase(name.charAt(1))) && !"Class".equals(name)) {
+        if ((mp == null || mp instanceof CachedField) && !name.isEmpty() && isUpperCase(name.charAt(0)) && (name.length() < 2 || !isUpperCase(name.charAt(1))) && !"Class".equals(name)) {
             // GROOVY-9618 adjust because capitalised properties aren't stored as meta bean props
             MetaProperty saved = mp;
             mp = getMetaProperty(senderForMP, BeanUtils.decapitalize(name), useSuper, isStatic);
@@ -2797,8 +2792,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
      * @param fromInsideClass Whether the call was invoked from the inside or the outside of the class.
      */
     @Override
-    public void setProperty(Class sender, Object object, String name, Object newValue, boolean useSuper, boolean fromInsideClass) {
-        checkInitalised();
+    public void setProperty(final Class sender, final Object object, final String name, Object newValue, final boolean useSuper, final boolean fromInsideClass) {
 
         //----------------------------------------------------------------------
         // handling of static
@@ -2810,6 +2804,8 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
             return;
         }
 
+        checkInitalised();
+
         //----------------------------------------------------------------------
         // Unwrap wrapped values fo now - the new MOP will handle them properly
         //----------------------------------------------------------------------
@@ -2876,16 +2872,16 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
         // field
         //----------------------------------------------------------------------
         if (method == null && field != null) {
+            boolean mapInstance = (isMap && !isStatic);
             int modifiers = field.getModifiers();
             if (Modifier.isFinal(modifiers)) {
-                // GROOVY-5985
-                if (!isStatic && this.isMap) {
+                if (mapInstance) { // GROOVY-8065
                     ((Map) object).put(name, newValue);
                     return;
                 }
-                throw new ReadOnlyPropertyException(name, theClass);
+                throw new ReadOnlyPropertyException(name, theClass); // GROOVY-5985
             }
-            if (!this.isMap || Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers)) {
+            if (!mapInstance || Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers)) {
                 field.setProperty(object, newValue);
                 return;
             }
@@ -2899,16 +2895,13 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
             method = getCategoryMethodSetter(sender, "set", true);
             if (method != null) arguments = new Object[]{name, newValue};
         }
-
-        // the generic method is valid, if available (!=null), if static or
-        // if it is not static and we do no static access
-        if (method == null && genericSetMethod != null && !(!genericSetMethod.isStatic() && isStatic)) {
+        if (method == null && genericSetMethod != null && (genericSetMethod.isStatic() || !isStatic)) {
             arguments = new Object[]{name, newValue};
             method = genericSetMethod;
         }
 
         //----------------------------------------------------------------------
-        // executing the setter method
+        // executing the method
         //----------------------------------------------------------------------
         if (method != null) {
             if (arguments.length == 1) {
@@ -2923,21 +2916,20 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
                 arguments[1] = newValue;
             }
 
-            MetaMethod transformedMetaMethod = VM_PLUGIN.transformMetaMethod(this, method);
-            transformedMetaMethod.doMethodInvoke(object, arguments);
+            VM_PLUGIN.transformMetaMethod(this, method).doMethodInvoke(object, arguments);
             return;
         }
 
-        //----------------------------------------------------------------------
-        // turn setProperty on a Map to put on the Map itself
-        //----------------------------------------------------------------------
-        if (method == null && !isStatic && this.isMap) {
+        //------------------------------------------------------------------
+        // java.util.Map put method
+        //------------------------------------------------------------------
+        if (isMap && !isStatic) {
             ((Map) object).put(name, newValue);
             return;
         }
 
         //----------------------------------------------------------------------
-        // error due to missing method/field
+        // missing property protocol
         //----------------------------------------------------------------------
         if (ambiguousListener) {
             throw new GroovyRuntimeException("There are multiple listeners for the property " + name + ". Please do not use the bean short form to access this listener.");
@@ -2945,7 +2937,6 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
         if (mp != null) {
             throw new ReadOnlyPropertyException(name, theClass);
         }
-
         if ((isStatic || object instanceof Class) && !"metaClass".equals(name)) {
             invokeStaticMissingProperty(object, name, newValue, false);
         } else {
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
index 016899f..03dbe44 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
@@ -177,15 +177,17 @@ public class StaticTypesCallSiteWriter extends CallSiteWriter {
             return;
         }
 
-        boolean isStaticProperty = receiver instanceof ClassExpression
-                && (receiverType.isDerivedFrom(receiver.getType()) || receiverType.implementsInterface(receiver.getType()));
+        if (makeGetPropertyWithGetter(receiver, receiverType, propertyName, safe, implicitThis)) return;
+
+        boolean isStaticProperty = (receiver instanceof ClassExpression
+                && (receiverType.isDerivedFrom(receiver.getType()) || receiverType.implementsInterface(receiver.getType())));
 
-        if (!isStaticProperty && isOrImplements(receiverType, MAP_TYPE)) {
-            // for maps, replace map.foo with map.get('foo')
+        // for maps, replace "map.foo" with "map.get('foo')" -- if no public field "foo" is declared (GROOVY-5001)
+        if (!isStaticProperty && isOrImplements(receiverType, MAP_TYPE)
+                && !java.util.Optional.ofNullable(getField(receiverType, propertyName)).filter(FieldNode::isPublic).isPresent()) {
             writeMapDotProperty(receiver, propertyName, safe);
             return;
         }
-        if (makeGetPropertyWithGetter(receiver, receiverType, propertyName, safe, implicitThis)) return;
         if (makeGetField(receiver, receiverType, propertyName, safe, implicitThis)) return;
         if (receiver instanceof ClassExpression) {
             if (makeGetField(receiver, receiver.getType(), propertyName, safe, implicitThis)) return;
diff --git a/src/test/groovy/MapTest.groovy b/src/test/groovy/MapTest.groovy
index 6f4aa7a..67e7532 100644
--- a/src/test/groovy/MapTest.groovy
+++ b/src/test/groovy/MapTest.groovy
@@ -20,10 +20,9 @@ package groovy
 
 import groovy.test.GroovyTestCase
 
-class MapTest extends GroovyTestCase {
+final class MapTest extends GroovyTestCase {
 
     void testMap() {
-
         def m = [1:'one', '2':'two', 3:'three']
 
         assert m.size() == 3
@@ -55,22 +54,22 @@ class MapTest extends GroovyTestCase {
 
         assert m.size() == 2
 
-        assert m.containsKey("cheese")
-        assert m.containsValue("cheddar")
+        assert m.containsKey('cheese')
+        assert m.containsValue('cheddar')
 
 
-        if ( m.containsKey("cheese") ) {
+        if ( m.containsKey('cheese') ) {
             // ignore
         }
         else {
-            assert false , "should contain cheese!"
+            assert false , 'should contain cheese!'
         }
 
         if ( m.containsKey(3) ) {
             // ignore
         }
         else {
-            assert false , "should contain 3!"
+            assert false , 'should contain 3!'
         }
     }
 
@@ -78,12 +77,49 @@ class MapTest extends GroovyTestCase {
         def m = [:]
 
         assert m.size() == 0
-        assert !m.containsKey("cheese")
+        assert !m.containsKey('cheese')
 
-        m.put("cheese", "cheddar")
+        m.put('cheese', 'cheddar')
 
         assert m.size() == 1
-        assert m.containsKey("cheese")
+        assert m.containsKey('cheese')
+    }
+
+    /**
+     * Map "empty" and "class" properties aren't Map#isEmpty or Object#getClass.
+     */
+    void testMapEmpty() {
+        def m = [empty: 'no', class: 'xx']
+
+        assert m.get('empty') == 'no'
+        assert m.get('class') == 'xx'
+        assert m['empty'] == 'no'
+        assert m['class'] == 'xx'
+        assert m.empty == 'no'
+        assert m.class == 'xx'
+        assert m.Class == null
+    }
+
+    // GROOVY-5001
+    void testMapDelegate() {
+        for (tag : ['','@TypeChecked','@CompileStatic']) {
+            assertScript """import groovy.transform.*
+                $tag class C {
+                    @Delegate Map m = [:]
+                    private def x = 'x'
+                    public  def y = 'y'
+                    def getZ() { 'z' }
+                }
+                def c = new C()
+
+                assert c.class == null
+                assert c.empty == null
+                assert c.m === c.@m
+                assert c.x == null
+                assert c.y == 'y'
+                assert c.z == 'z'
+            """
+        }
     }
 
     void testMapMutation() {
@@ -109,7 +145,34 @@ class MapTest extends GroovyTestCase {
         assert foo == 5
     }
 
-    void testMapLeftShift(){
+    // GROOVY-5001, GROOVY-5491
+    void testMapMutation2() {
+        for (tag : ['','@TypeChecked','@CompileStatic']) {
+            assertScript """import groovy.transform.*
+                    $tag class C extends HashMap { // just like GROOVY-662, GROOVY-8065, GROOVY-8074
+                    private boo
+                    def foo
+                }
+
+                def map = new C(foo:'bar')
+                assert map.@boo == null
+                assert map.boo  == null
+                assert map.foo == 'bar'
+
+                map.foo = 'baz' // set not put
+                assert map.foo == 'baz'
+                assert map.keySet().isEmpty()
+
+                map.boo = 'xx'
+                assert map.@boo == null
+                assert map.boo == 'xx'
+                assert map['boo'] == 'xx'
+                assert map.containsKey('boo')
+            """
+        }
+    }
+
+    void testMapLeftShift() {
         def map = [a:1, b:2]
         def other = [c:3]
         def entry = [d:4].iterator().toList()[0]
@@ -119,7 +182,7 @@ class MapTest extends GroovyTestCase {
         assert map == [a:1, b:2, c:3, d:4]
     }
 
-    void testFindAll(){
+    void testFindAll() {
         assert [a:1] == ['a':1, 'b':2].findAll {it.value == 1}
         assert [a:1] == ['a':1, 'b':2].findAll {it.key == 'a'}
         assert [a:1] == ['a':1, 'b':2].findAll {key,value -> key == 'a'}
@@ -208,7 +271,7 @@ class MapTest extends GroovyTestCase {
         assert map1 == [a:1, b:2]
     }
 
-    void testMapSort(){
+    void testMapSort() {
         def map = [a:100, c:20, b:3]
         def mapByValue = map.sort{ it.value }
         assert mapByValue.collect{ it.key } == ['b', 'c', 'a']
@@ -219,14 +282,14 @@ class MapTest extends GroovyTestCase {
     void testMapAdditionProducesCorrectValueAndPreservesOriginalMaps() {
         def left = [a:1, b:2]
         def right = [c:3]
-        assert left + right == [a:1, b:2, c:3], "should contain all entries from both maps"
-        assert left == [a:1, b:2] && right == [c:3], "LHS/RHS should not be modified"
+        assert left + right == [a:1, b:2, c:3], 'should contain all entries from both maps'
+        assert left == [a:1, b:2] && right == [c:3], 'LHS/RHS should not be modified'
     }
 
     void testMapAdditionGivesPrecedenceOfOverlappingValuesToRightMap() {
         def left = [a:1, b:1]
         def right = [a:2]
-        assert left + right == [a:2, b:1], "RHS should take precedence when entries have same key"
+        assert left + right == [a:2, b:1], 'RHS should take precedence when entries have same key'
     }
 
     void testMapAdditionPreservesOriginalTypeForCommonCases() {
@@ -246,11 +309,11 @@ class MapTest extends GroovyTestCase {
 
     void testTreeMapEach() {
         TreeMap map = [c:2, b:3, a:1]
-        String result1 = "", result2 = ""
+        String result1 = '', result2 = ''
         map.each{ k, v -> result1 += "$k$v " }
-        assert result1 == "a1 b3 c2 "
+        assert result1 == 'a1 b3 c2 '
         map.reverseEach{ e -> result2 += "$e.key$e.value " }
-        assert result2 == "c2 b3 a1 "
+        assert result2 == 'c2 b3 a1 '
     }
 
     void testMapWithDefault() {
@@ -264,8 +327,8 @@ class MapTest extends GroovyTestCase {
 
     void testMapIsCaseWithGrep() {
         def predicate = [apple:true, banana:true, lemon:false, orange:false, pear:true]
-        def fruitList = ["apple", "apple", "pear", "orange", "pear", "lemon", "banana"]
-        def expected = ["apple", "apple", "pear", "pear", "banana"]
+        def fruitList = ['apple', 'apple', 'pear', 'orange', 'pear', 'lemon', 'banana']
+        def expected = ['apple', 'apple', 'pear', 'pear', 'banana']
         assert fruitList.grep(predicate) == expected
     }
 
diff --git a/src/test/groovy/bugs/Groovy662.groovy b/src/test/groovy/bugs/Groovy662.groovy
new file mode 100644
index 0000000..5bc5ed0
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy662.groovy
@@ -0,0 +1,90 @@
+/*
+ *  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 groovy.transform.CompileStatic
+import groovy.transform.PackageScope
+import org.junit.Test
+
+import static groovy.test.GroovyAssert.assertScript
+
+final class Groovy662 {
+
+    @Test
+    void testJavaClass() {
+        def object = new Groovy662_JavaClass()
+        assert object.getMyProperty() == 'Hello'
+        assert object.@myProperty == 'Hello'
+        assert object.myProperty == 'Hello'
+    }
+
+    @Test @CompileStatic
+    void testJavaClassCS() {
+        def object = new Groovy662_JavaClass()
+        assert object.getMyProperty() == 'Hello'
+        assert object.@myProperty == 'Hello'
+        assert object.myProperty == 'Hello'
+    }
+
+    @Test
+    void testJavaClassAsScript() {
+        assertScript '''
+            def object = new groovy.bugs.Groovy662_JavaClass()
+            assert object.getMyProperty() == 'Hello'
+            assert object.@myProperty == 'Hello'
+            assert object.myProperty == 'Hello'
+        '''
+    }
+
+    //
+
+    @Test
+    void testGroovyClass() {
+        def object = new Groovy662_GroovyClass()
+        assert object.getMyProperty() == 'Hello'
+        assert object.@myProperty == 'Hello'
+        assert object.myProperty == 'Hello'
+    }
+
+    @Test @CompileStatic
+    void testGroovyClassCS() {
+        def object = new Groovy662_GroovyClass()
+        assert object.getMyProperty() == 'Hello'
+        assert object.@myProperty == 'Hello'
+        assert object.myProperty == 'Hello'
+    }
+
+    @Test
+    void testGroovyClassAsScript() {
+        assertScript '''
+            def object = new groovy.bugs.Groovy662_GroovyClass()
+            assert object.getMyProperty() == 'Hello'
+            assert object.@myProperty == 'Hello'
+            assert object.myProperty == 'Hello'
+        '''
+    }
+}
+
+class Groovy662_GroovyClass extends HashMap {
+    @PackageScope String myProperty = 'Hello'
+
+    String getMyProperty() {
+        return myProperty
+    }
+}
diff --git a/src/test/groovy/bugs/Groovy662Bug.groovy b/src/test/groovy/bugs/Groovy662Bug.groovy
deleted file mode 100644
index ccf47cb..0000000
--- a/src/test/groovy/bugs/Groovy662Bug.groovy
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- *  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 groovy.test.GroovyTestCase
-
-//  The order of the classes is crucial, the first must be the GroovyTestCase.  Its name doesn't
-//  matter it just has to be first.
-
-/**
- * Test class and support to realize the GROOVY-662 test.  There is a difference between
- * improper uses of properties between Groovy defined classes and Java defined classes.  There
- * is no difference between correct uses so this is not a problem just an anti-regression test.
- */
-class Groovy662 extends GroovyTestCase {
-    private String expected = "Hello"
-
-    private usePropertyCorrectly(def object) { return object.@myProperty }
-
-    private usePropertyIncorrectly(def object) { return object.myProperty }
-
-    private useMethod(def object) { return object.getMyProperty() }
-
-    private void doAssertions(def object) {
-        assertTrue(useMethod(object) == expected)
-        assertTrue(usePropertyCorrectly(object) == expected)
-    }
-
-    private String theTestScriptDefinitions = """
-        String expected = "Hello"
-        def usePropertyCorrectly ( def object ) { return object.@myProperty }
-        def usePropertyIncorrectly ( def object ) { return object.myProperty }
-        def useMethod ( def object ) { return object.getMyProperty ( ) }
-    """
-
-    private String theTestScriptAssertions = """
-        assert useMethod ( object ) == expected
-        assert usePropertyCorrectly ( object ) == expected
-    """
-
-    public void testJavaClass() {
-        def object = new groovy.bugs.Groovy662_JavaClass()
-        doAssertions(object)
-        assertTrue(usePropertyIncorrectly(object) == null)
-    }
-
-    public void testGroovyClass() {
-        def object = new Groovy662_GroovyClass()
-        doAssertions(object)
-        assertTrue(usePropertyIncorrectly(object) == null)
-    }
-
-    public void testJavaClassAsScript() {
-        assertScript(theTestScriptDefinitions + """
-            def object = new groovy.bugs.Groovy662_JavaClass ( )
-        """ + theTestScriptAssertions + """
-            assert usePropertyIncorrectly ( object ) == null
-        """)
-    }
-
-    public void testGroovyClassAsScript() {
-        assertScript(theTestScriptDefinitions + """
-            class Groovy662_GroovyClass extends HashMap {
-                String myProperty = "Hello"
-                public String getMyProperty ( ) { return myProperty }
-            }
-            def object = new Groovy662_GroovyClass ( )
-        """ + theTestScriptAssertions + """
-            assert usePropertyIncorrectly ( object ) == null
-        """)
-    }
-}
-
-class Groovy662_GroovyClass extends HashMap {
-    String myProperty = "Hello"
-
-    public String getMyProperty() {
-        return myProperty
-    }
-}
diff --git a/src/test/groovy/bugs/Groovy8065Bug.groovy b/src/test/groovy/bugs/Groovy8065.groovy
similarity index 79%
rename from src/test/groovy/bugs/Groovy8065Bug.groovy
rename to src/test/groovy/bugs/Groovy8065.groovy
index be18ca3..c1b8543 100644
--- a/src/test/groovy/bugs/Groovy8065Bug.groovy
+++ b/src/test/groovy/bugs/Groovy8065.groovy
@@ -18,9 +18,13 @@
  */
 package groovy.bugs
 
-import groovy.test.GroovyTestCase
+import org.junit.Test
 
-class Groovy8065Bug extends GroovyTestCase {
+import static groovy.test.GroovyAssert.assertScript
+
+final class Groovy8065 {
+
+    @Test
     void testMapWithCustomSetDuringAsTypeCast() {
         assertScript '''
             class MapWithSet extends LinkedHashMap {
@@ -33,18 +37,19 @@ class Groovy8065Bug extends GroovyTestCase {
         '''
     }
 
+    @Test
     void testMapWithPublicField() {
         assertScript '''
-            class A extends HashMap {
+            class C extends HashMap {
                 public foo
             }
-            def a = new A()
-            a.x = 1
-            assert a.x == 1
-            a.foo = 2
-            assert a.@foo == 2
-            assert a.foo == null
-            assert a == [x:1]
+            def c = new C()
+            c.x = 1
+            assert c.x == 1
+            c.foo = 2
+            assert c.@foo == 2
+            assert c.foo == 2
+            assert c == [x:1]
         '''
     }
 }
diff --git a/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy b/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
index 94f4ce4..0636c29 100644
--- a/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
@@ -733,11 +733,24 @@ class ArraysAndCollectionsSTCTest extends StaticTypeCheckingTestCase {
 
     // GROOVY-6266
     void testMapKeyGenerics() {
-        assertScript """
-            HashMap<String,List<List>> AR=new HashMap<String,List<List>>()
-            AR.get('key',[['val1'],['val2']])
-            assert AR.'key'[0] == ['val1']
-        """
+        assertScript '''
+            HashMap<String,List<List>> map = new HashMap<String,List<List>>()
+            map.get('key',[['val1'],['val2']])
+            assert map.'key'[0] == ['val1']
+        '''
+    }
+
+    // GROOVY-8074
+    void testMapSubclassPropertyStyleAccess() {
+        assertScript '''
+            class MyMap extends LinkedHashMap {
+                def foo = 1
+            }
+
+            def map = new MyMap()
+            map.put('foo', 42)
+            assert map.foo == 1
+        '''
     }
 
     // GROOVY-6311
diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/ArraysAndCollectionsStaticCompileTest.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/ArraysAndCollectionsStaticCompileTest.groovy
index 038bcd4..b0ca167 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/sc/ArraysAndCollectionsStaticCompileTest.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/sc/ArraysAndCollectionsStaticCompileTest.groovy
@@ -120,19 +120,6 @@ class ArraysAndCollectionsStaticCompileTest extends ArraysAndCollectionsSTCTest
         assert astTrees['Foo'][1].count('DefaultGroovyMethods.toList') == 1
     }
 
-    // GROOVY-8074
-    void testMapSubclassPropertyStyleAccess() {
-        assertScript '''
-            class MyMap extends LinkedHashMap {
-                def foo = 1
-            }
-
-            def map = new MyMap()
-            map.put('foo', 42)
-            assert map.foo == 42
-        '''
-    }
-
     // GROOVY-10029
     void testCollectionToArrayAssignmentSC() {
         assertScript '''