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/25 18:20:37 UTC
[groovy] branch master updated: GROOVY-5001, GROOVY-5491, GROOVY-6144: map property precedence
This is an automated email from the ASF dual-hosted git repository.
emilles 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 051d3cf3 GROOVY-5001, GROOVY-5491, GROOVY-6144: map property precedence
051d3cf3 is described below
commit 051d3cf3ef29a521137b9d6b71cb8097d077d46b
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue Jan 25 12:20:29 2022 -0600
GROOVY-5001, GROOVY-5491, GROOVY-6144: map property precedence
---
src/main/java/groovy/lang/MetaClassImpl.java | 228 ++++++++++-----------
.../classgen/asm/sc/StaticTypesCallSiteWriter.java | 12 +-
.../callsite/GetEffectivePojoPropertySite.java | 22 +-
src/test/groovy/MapTest.groovy | 118 +++++++++--
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 --
9 files changed, 345 insertions(+), 282 deletions(-)
diff --git a/src/main/java/groovy/lang/MetaClassImpl.java b/src/main/java/groovy/lang/MetaClassImpl.java
index 9cb38db..e6aea60 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -1960,100 +1960,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 || isSpecialProperty(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);
@@ -2066,13 +2071,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);
}
@@ -2086,73 +2090,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 || isSpecialProperty(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
@@ -2171,7 +2167,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
@@ -2195,7 +2191,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) {
@@ -2238,10 +2233,17 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
};
}
+ /**
+ * Object#getClass, Map#isEmpty, GroovyObject#getMetaClass
+ */
+ private boolean isSpecialProperty(final String name) {
+ return "class".equals(name) || (isMap && ("empty".equals(name) || "metaClass".equals(name)));
+ }
+
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) && name.length() > 0 && 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) && !"MetaClass".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);
@@ -2794,8 +2796,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
@@ -2807,6 +2808,8 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
return;
}
+ checkInitalised();
+
//----------------------------------------------------------------------
// Unwrap wrapped values fo now - the new MOP will handle them properly
//----------------------------------------------------------------------
@@ -2873,16 +2876,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;
}
@@ -2896,16 +2899,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) {
@@ -2920,21 +2920,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.");
@@ -2942,7 +2941,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 f6d53cc..ce65d11 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/main/java/org/codehaus/groovy/runtime/callsite/GetEffectivePojoPropertySite.java b/src/main/java/org/codehaus/groovy/runtime/callsite/GetEffectivePojoPropertySite.java
index 8eb022a..da2f73d 100644
--- a/src/main/java/org/codehaus/groovy/runtime/callsite/GetEffectivePojoPropertySite.java
+++ b/src/main/java/org/codehaus/groovy/runtime/callsite/GetEffectivePojoPropertySite.java
@@ -29,30 +29,18 @@ public class GetEffectivePojoPropertySite extends AbstractCallSite {
private final MetaProperty effective;
private final int version;
- public GetEffectivePojoPropertySite(CallSite site, MetaClassImpl metaClass, MetaProperty effective) {
+ public GetEffectivePojoPropertySite(final CallSite site, final MetaClassImpl metaClass, final MetaProperty effective) {
super(site);
this.metaClass = metaClass;
this.effective = effective;
version = metaClass.getVersion();
}
-// public final Object callGetProperty (Object receiver) throws Throwable {
-// if (GroovyCategorySupport.hasCategoryInCurrentThread() || receiver.getClass() != metaClass.getTheClass()) {
-// return createGetPropertySite(receiver).getProperty(receiver);
-// } else {
-// try {
-// return effective.getProperty(receiver);
-// } catch (GroovyRuntimeException gre) {
-// throw ScriptBytecodeAdapter.unwrap(gre);
-// }
-// }
-// }
-
@Override
- public final CallSite acceptGetProperty(Object receiver) {
-// if (GroovyCategorySupport.hasCategoryInCurrentThread() || !(receiver instanceof GroovyObject) || ((GroovyObject)receiver).getMetaClass() != metaClass) {
- if (GroovyCategorySupport.hasCategoryInCurrentThread() || receiver==null || receiver.getClass() != metaClass.getTheClass()
- || version != metaClass.getVersion()) { // metaClass is invalid
+ public final CallSite acceptGetProperty(final Object receiver) {
+ if (receiver == null || receiver.getClass() != metaClass.getTheClass()
+ || version != metaClass.getVersion() // metaClass is invalid
+ || GroovyCategorySupport.hasCategoryInCurrentThread()) {
return createGetPropertySite(receiver);
} else {
return this;
diff --git a/src/test/groovy/MapTest.groovy b/src/test/groovy/MapTest.groovy
index 6f4aa7a..4738099 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,62 @@ 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" property isn't Map#isEmpty.
+ */
+ void testMapEmpty() {
+ def m = [empty: 'no']
+
+ assert m.get('empty') == 'no'
+ assert m['empty'] == 'no'
+ assert m.empty == 'no'
+ }
+
+ /**
+ * Map "class" and "metaClass" properties aren't Object#getClass or GroovyObject#getMetaClass.
+ */
+ void testMapClass() {
+ def m = [class: 'xx', metaClass: 'yy']
+
+ assert m.get('class') == 'xx'
+ assert m['class'] == 'xx'
+ assert m.class == 'xx'
+ assert m.Class == null
+
+ assert m.get('metaClass') == 'yy'
+ assert m['metaClass'] == 'yy'
+ assert m.metaClass == 'yy'
+ assert m.MetaClass == 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 +158,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 +195,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 +284,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 +295,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 +322,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 +340,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 '''