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 2017/05/26 02:04:35 UTC
groovy git commit: GROOVY-8204: @Delegate on arrays causes NPE during
compilation (closes #551)
Repository: groovy
Updated Branches:
refs/heads/master 401b3a4c4 -> 86be867e7
GROOVY-8204: @Delegate on arrays causes NPE during compilation (closes #551)
Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/86be867e
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/86be867e
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/86be867e
Branch: refs/heads/master
Commit: 86be867e71eceb3b493773ac567d1cee6b42c05c
Parents: 401b3a4
Author: paulk <pa...@asert.com.au>
Authored: Thu May 25 23:37:14 2017 +1000
Committer: paulk <pa...@asert.com.au>
Committed: Fri May 26 12:03:21 2017 +1000
----------------------------------------------------------------------
.../org/codehaus/groovy/ast/ClassHelper.java | 323 ++++++++++---------
.../groovy/ast/tools/GenericsUtils.java | 2 +
.../transform/DelegateASTTransformation.java | 11 +
.../transform/DelegateTransformTest.groovy | 18 +-
4 files changed, 192 insertions(+), 162 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/groovy/blob/86be867e/src/main/org/codehaus/groovy/ast/ClassHelper.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/ast/ClassHelper.java b/src/main/org/codehaus/groovy/ast/ClassHelper.java
index a52085d..6fc6a22 100644
--- a/src/main/org/codehaus/groovy/ast/ClassHelper.java
+++ b/src/main/org/codehaus/groovy/ast/ClassHelper.java
@@ -41,90 +41,87 @@ import java.lang.reflect.Modifier;
/**
* This class is a Helper for ClassNode and classes handling ClassNodes.
- * It does contain a set of predefined ClassNodes for the most used
- * types and some code for cached ClassNode creation and basic
- * ClassNode handling
- *
- * @author Jochen Theodorou
+ * It does contain a set of predefined ClassNodes for the most used
+ * types and some code for cached ClassNode creation and basic
+ * ClassNode handling
*/
public class ClassHelper {
- private static final Class[] classes = new Class[] {
- Object.class, Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE,
- Integer.TYPE, Long.TYPE, Double.TYPE, Float.TYPE, Void.TYPE,
- Closure.class, GString.class, List.class, Map.class, Range.class,
- Pattern.class, Script.class, String.class, Boolean.class,
- Character.class, Byte.class, Short.class, Integer.class, Long.class,
- Double.class, Float.class, BigDecimal.class, BigInteger.class,
- Number.class, Void.class, Reference.class, Class.class, MetaClass.class,
- Iterator.class, GeneratedClosure.class, GroovyObjectSupport.class
+ private static final Class[] classes = new Class[]{
+ Object.class, Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE,
+ Integer.TYPE, Long.TYPE, Double.TYPE, Float.TYPE, Void.TYPE,
+ Closure.class, GString.class, List.class, Map.class, Range.class,
+ Pattern.class, Script.class, String.class, Boolean.class,
+ Character.class, Byte.class, Short.class, Integer.class, Long.class,
+ Double.class, Float.class, BigDecimal.class, BigInteger.class,
+ Number.class, Void.class, Reference.class, Class.class, MetaClass.class,
+ Iterator.class, GeneratedClosure.class, GroovyObjectSupport.class
};
- private static final String[] primitiveClassNames = new String[] {
- "", "boolean", "char", "byte", "short",
- "int", "long", "double", "float", "void"
+ private static final String[] primitiveClassNames = new String[]{
+ "", "boolean", "char", "byte", "short",
+ "int", "long", "double", "float", "void"
};
-
- public static final ClassNode
- DYNAMIC_TYPE = makeCached(Object.class), OBJECT_TYPE = DYNAMIC_TYPE,
- VOID_TYPE = makeCached(Void.TYPE), CLOSURE_TYPE = makeCached(Closure.class),
- GSTRING_TYPE = makeCached(GString.class), LIST_TYPE = makeWithoutCaching(List.class),
- MAP_TYPE = makeWithoutCaching(Map.class), RANGE_TYPE = makeCached(Range.class),
- PATTERN_TYPE = makeCached(Pattern.class), STRING_TYPE = makeCached(String.class),
- SCRIPT_TYPE = makeCached(Script.class), REFERENCE_TYPE = makeWithoutCaching(Reference.class),
- BINDING_TYPE = makeCached(Binding.class),
-
- boolean_TYPE = makeCached(boolean.class), char_TYPE = makeCached(char.class),
- byte_TYPE = makeCached(byte.class), int_TYPE = makeCached(int.class),
- long_TYPE = makeCached(long.class), short_TYPE = makeCached(short.class),
- double_TYPE = makeCached(double.class), float_TYPE = makeCached(float.class),
- Byte_TYPE = makeCached(Byte.class), Short_TYPE = makeCached(Short.class),
- Integer_TYPE = makeCached(Integer.class), Long_TYPE = makeCached(Long.class),
- Character_TYPE = makeCached(Character.class), Float_TYPE = makeCached(Float.class),
- Double_TYPE = makeCached(Double.class), Boolean_TYPE = makeCached(Boolean.class),
- BigInteger_TYPE = makeCached(java.math.BigInteger.class),
- BigDecimal_TYPE = makeCached(java.math.BigDecimal.class),
- Number_TYPE = makeCached(Number.class),
-
- void_WRAPPER_TYPE = makeCached(Void.class), METACLASS_TYPE = makeCached(MetaClass.class),
- Iterator_TYPE = makeCached(Iterator.class),
-
- Enum_Type = makeWithoutCaching(Enum.class),
- Annotation_TYPE = makeCached(Annotation.class),
- ELEMENT_TYPE_TYPE = makeCached(ElementType.class),
-
- // uncached constants.
- CLASS_Type = makeWithoutCaching(Class.class), COMPARABLE_TYPE = makeWithoutCaching(Comparable.class),
- GENERATED_CLOSURE_Type = makeWithoutCaching(GeneratedClosure.class),
- GROOVY_OBJECT_SUPPORT_TYPE = makeWithoutCaching(GroovyObjectSupport.class),
- GROOVY_OBJECT_TYPE = makeWithoutCaching(GroovyObject.class),
- GROOVY_INTERCEPTABLE_TYPE = makeWithoutCaching(GroovyInterceptable.class)
- ;
-
- private static final ClassNode[] types = new ClassNode[] {
- OBJECT_TYPE,
- boolean_TYPE, char_TYPE, byte_TYPE, short_TYPE,
- int_TYPE, long_TYPE, double_TYPE, float_TYPE,
- VOID_TYPE, CLOSURE_TYPE, GSTRING_TYPE,
- LIST_TYPE, MAP_TYPE, RANGE_TYPE, PATTERN_TYPE,
- SCRIPT_TYPE, STRING_TYPE, Boolean_TYPE, Character_TYPE,
- Byte_TYPE, Short_TYPE, Integer_TYPE, Long_TYPE,
- Double_TYPE, Float_TYPE, BigDecimal_TYPE, BigInteger_TYPE,
- Number_TYPE,
- void_WRAPPER_TYPE, REFERENCE_TYPE, CLASS_Type, METACLASS_TYPE,
- Iterator_TYPE, GENERATED_CLOSURE_Type, GROOVY_OBJECT_SUPPORT_TYPE,
- GROOVY_OBJECT_TYPE, GROOVY_INTERCEPTABLE_TYPE, Enum_Type, Annotation_TYPE
+
+ public static final ClassNode
+ DYNAMIC_TYPE = makeCached(Object.class), OBJECT_TYPE = DYNAMIC_TYPE,
+ VOID_TYPE = makeCached(Void.TYPE), CLOSURE_TYPE = makeCached(Closure.class),
+ GSTRING_TYPE = makeCached(GString.class), LIST_TYPE = makeWithoutCaching(List.class),
+ MAP_TYPE = makeWithoutCaching(Map.class), RANGE_TYPE = makeCached(Range.class),
+ PATTERN_TYPE = makeCached(Pattern.class), STRING_TYPE = makeCached(String.class),
+ SCRIPT_TYPE = makeCached(Script.class), REFERENCE_TYPE = makeWithoutCaching(Reference.class),
+ BINDING_TYPE = makeCached(Binding.class),
+
+ boolean_TYPE = makeCached(boolean.class), char_TYPE = makeCached(char.class),
+ byte_TYPE = makeCached(byte.class), int_TYPE = makeCached(int.class),
+ long_TYPE = makeCached(long.class), short_TYPE = makeCached(short.class),
+ double_TYPE = makeCached(double.class), float_TYPE = makeCached(float.class),
+ Byte_TYPE = makeCached(Byte.class), Short_TYPE = makeCached(Short.class),
+ Integer_TYPE = makeCached(Integer.class), Long_TYPE = makeCached(Long.class),
+ Character_TYPE = makeCached(Character.class), Float_TYPE = makeCached(Float.class),
+ Double_TYPE = makeCached(Double.class), Boolean_TYPE = makeCached(Boolean.class),
+ BigInteger_TYPE = makeCached(java.math.BigInteger.class),
+ BigDecimal_TYPE = makeCached(java.math.BigDecimal.class),
+ Number_TYPE = makeCached(Number.class),
+
+ void_WRAPPER_TYPE = makeCached(Void.class), METACLASS_TYPE = makeCached(MetaClass.class),
+ Iterator_TYPE = makeCached(Iterator.class),
+
+ Enum_Type = makeWithoutCaching(Enum.class),
+ Annotation_TYPE = makeCached(Annotation.class),
+ ELEMENT_TYPE_TYPE = makeCached(ElementType.class),
+
+ // uncached constants.
+ CLASS_Type = makeWithoutCaching(Class.class), COMPARABLE_TYPE = makeWithoutCaching(Comparable.class),
+ GENERATED_CLOSURE_Type = makeWithoutCaching(GeneratedClosure.class),
+ GROOVY_OBJECT_SUPPORT_TYPE = makeWithoutCaching(GroovyObjectSupport.class),
+ GROOVY_OBJECT_TYPE = makeWithoutCaching(GroovyObject.class),
+ GROOVY_INTERCEPTABLE_TYPE = makeWithoutCaching(GroovyInterceptable.class);
+
+ private static final ClassNode[] types = new ClassNode[]{
+ OBJECT_TYPE,
+ boolean_TYPE, char_TYPE, byte_TYPE, short_TYPE,
+ int_TYPE, long_TYPE, double_TYPE, float_TYPE,
+ VOID_TYPE, CLOSURE_TYPE, GSTRING_TYPE,
+ LIST_TYPE, MAP_TYPE, RANGE_TYPE, PATTERN_TYPE,
+ SCRIPT_TYPE, STRING_TYPE, Boolean_TYPE, Character_TYPE,
+ Byte_TYPE, Short_TYPE, Integer_TYPE, Long_TYPE,
+ Double_TYPE, Float_TYPE, BigDecimal_TYPE, BigInteger_TYPE,
+ Number_TYPE,
+ void_WRAPPER_TYPE, REFERENCE_TYPE, CLASS_Type, METACLASS_TYPE,
+ Iterator_TYPE, GENERATED_CLOSURE_Type, GROOVY_OBJECT_SUPPORT_TYPE,
+ GROOVY_OBJECT_TYPE, GROOVY_INTERCEPTABLE_TYPE, Enum_Type, Annotation_TYPE
};
- private static final int ABSTRACT_STATIC_PRIVATE =
- Modifier.ABSTRACT|Modifier.PRIVATE|Modifier.STATIC;
+ private static final int ABSTRACT_STATIC_PRIVATE =
+ Modifier.ABSTRACT | Modifier.PRIVATE | Modifier.STATIC;
private static final int VISIBILITY = 5; // public|protected
-
+
protected static final ClassNode[] EMPTY_TYPE_ARRAY = {};
-
+
public static final String OBJECT = "java.lang.Object";
- public static ClassNode makeCached(Class c){
+ public static ClassNode makeCached(Class c) {
final SoftReference<ClassNode> classNodeSoftReference = ClassHelperCache.classCache.get(c);
ClassNode classNode;
if (classNodeSoftReference == null || (classNode = classNodeSoftReference.get()) == null) {
@@ -136,146 +133,146 @@ public class ClassHelper {
return classNode;
}
-
+
/**
* Creates an array of ClassNodes using an array of classes.
- * For each of the given classes a new ClassNode will be
+ * For each of the given classes a new ClassNode will be
* created
- * @see #make(Class)
+ *
* @param classes an array of classes used to create the ClassNodes
* @return an array of ClassNodes
+ * @see #make(Class)
*/
public static ClassNode[] make(Class[] classes) {
ClassNode[] cns = new ClassNode[classes.length];
- for (int i=0; i<cns.length; i++) {
+ for (int i = 0; i < cns.length; i++) {
cns[i] = make(classes[i]);
}
-
+
return cns;
}
-
+
/**
* Creates a ClassNode using a given class.
* A new ClassNode object is only created if the class
* is not one of the predefined ones
- *
+ *
* @param c class used to created the ClassNode
* @return ClassNode instance created from the given class
*/
public static ClassNode make(Class c) {
- return make(c,true);
+ return make(c, true);
}
-
+
public static ClassNode make(Class c, boolean includeGenerics) {
- for (int i=0; i<classes.length; i++) {
- if (c==classes[i]) return types[i];
+ for (int i = 0; i < classes.length; i++) {
+ if (c == classes[i]) return types[i];
}
if (c.isArray()) {
- ClassNode cn = make(c.getComponentType(),includeGenerics);
+ ClassNode cn = make(c.getComponentType(), includeGenerics);
return cn.makeArray();
}
- return makeWithoutCaching(c,includeGenerics);
+ return makeWithoutCaching(c, includeGenerics);
}
-
- public static ClassNode makeWithoutCaching(Class c){
- return makeWithoutCaching(c,true);
+
+ public static ClassNode makeWithoutCaching(Class c) {
+ return makeWithoutCaching(c, true);
}
-
- public static ClassNode makeWithoutCaching(Class c, boolean includeGenerics){
+
+ public static ClassNode makeWithoutCaching(Class c, boolean includeGenerics) {
if (c.isArray()) {
- ClassNode cn = makeWithoutCaching(c.getComponentType(),includeGenerics);
+ ClassNode cn = makeWithoutCaching(c.getComponentType(), includeGenerics);
return cn.makeArray();
}
final ClassNode cached = makeCached(c);
if (includeGenerics) {
return cached;
- }
- else {
+ } else {
ClassNode t = makeWithoutCaching(c.getName());
t.setRedirect(cached);
return t;
}
}
-
-
+
+
/**
* Creates a ClassNode using a given class.
* Unlike make(String) this method will not use the cache
* to create the ClassNode. This means the ClassNode created
* from this method using the same name will have a different
* reference
- *
- * @see #make(String)
+ *
* @param name of the class the ClassNode is representing
+ * @see #make(String)
*/
- public static ClassNode makeWithoutCaching(String name) {
- ClassNode cn = new ClassNode(name,Opcodes.ACC_PUBLIC,OBJECT_TYPE);
+ public static ClassNode makeWithoutCaching(String name) {
+ ClassNode cn = new ClassNode(name, Opcodes.ACC_PUBLIC, OBJECT_TYPE);
cn.isPrimaryNode = false;
return cn;
}
-
- /** * Creates a ClassNode using a given class.
- * If the name is one of the predefined ClassNodes then the
+
+ /**
+ * Creates a ClassNode using a given class.
+ * If the name is one of the predefined ClassNodes then the
* corresponding ClassNode instance will be returned. If the
* name is null or of length 0 the dynamic type is returned
- *
+ *
* @param name of the class the ClassNode is representing
*/
public static ClassNode make(String name) {
if (name == null || name.length() == 0) return DYNAMIC_TYPE;
-
- for (int i=0; i<primitiveClassNames.length; i++) {
+
+ for (int i = 0; i < primitiveClassNames.length; i++) {
if (primitiveClassNames[i].equals(name)) return types[i];
}
-
- for (int i=0; i<classes.length; i++) {
+
+ for (int i = 0; i < classes.length; i++) {
String cname = classes[i].getName();
if (name.equals(cname)) return types[i];
- }
+ }
return makeWithoutCaching(name);
}
-
+
/**
- * Creates a ClassNode containing the wrapper of a ClassNode
+ * Creates a ClassNode containing the wrapper of a ClassNode
* of primitive type. Any ClassNode representing a primitive
* type should be created using the predefined types used in
- * class. The method will check the parameter for known
+ * class. The method will check the parameter for known
* references of ClassNode representing a primitive type. If
* Reference is found, then a ClassNode will be contained that
* represents the wrapper class. For example for boolean, the
* wrapper class is java.lang.Boolean.
- *
- * If the parameter is no primitive type, the redirected
- * ClassNode will be returned
- *
+ * <p>
+ * If the parameter is no primitive type, the redirected
+ * ClassNode will be returned
+ *
+ * @param cn the ClassNode containing a possible primitive type
* @see #make(Class)
* @see #make(String)
- * @param cn the ClassNode containing a possible primitive type
*/
public static ClassNode getWrapper(ClassNode cn) {
cn = cn.redirect();
if (!isPrimitiveType(cn)) return cn;
- if (cn==boolean_TYPE) {
+ if (cn == boolean_TYPE) {
return Boolean_TYPE;
- } else if (cn==byte_TYPE) {
+ } else if (cn == byte_TYPE) {
return Byte_TYPE;
- } else if (cn==char_TYPE) {
+ } else if (cn == char_TYPE) {
return Character_TYPE;
- } else if (cn==short_TYPE) {
+ } else if (cn == short_TYPE) {
return Short_TYPE;
- } else if (cn==int_TYPE) {
+ } else if (cn == int_TYPE) {
return Integer_TYPE;
- } else if (cn==long_TYPE) {
+ } else if (cn == long_TYPE) {
return Long_TYPE;
- } else if (cn==float_TYPE) {
+ } else if (cn == float_TYPE) {
return Float_TYPE;
- } else if (cn==double_TYPE) {
+ } else if (cn == double_TYPE) {
return Double_TYPE;
- } else if (cn==VOID_TYPE) {
+ } else if (cn == VOID_TYPE) {
return void_WRAPPER_TYPE;
- }
- else {
+ } else {
return cn;
}
}
@@ -283,24 +280,23 @@ public class ClassHelper {
public static ClassNode getUnwrapper(ClassNode cn) {
cn = cn.redirect();
if (isPrimitiveType(cn)) return cn;
- if (cn==Boolean_TYPE) {
+ if (cn == Boolean_TYPE) {
return boolean_TYPE;
- } else if (cn==Byte_TYPE) {
+ } else if (cn == Byte_TYPE) {
return byte_TYPE;
- } else if (cn==Character_TYPE) {
+ } else if (cn == Character_TYPE) {
return char_TYPE;
- } else if (cn==Short_TYPE) {
+ } else if (cn == Short_TYPE) {
return short_TYPE;
- } else if (cn==Integer_TYPE) {
+ } else if (cn == Integer_TYPE) {
return int_TYPE;
- } else if (cn==Long_TYPE) {
+ } else if (cn == Long_TYPE) {
return long_TYPE;
- } else if (cn==Float_TYPE) {
+ } else if (cn == Float_TYPE) {
return float_TYPE;
- } else if (cn==Double_TYPE) {
+ } else if (cn == Double_TYPE) {
return double_TYPE;
- }
- else {
+ } else {
return cn;
}
}
@@ -310,14 +306,14 @@ public class ClassHelper {
* Test to determine if a ClassNode is a primitive type.
* Note: this only works for ClassNodes created using a
* predefined ClassNode
- *
- * @see #make(Class)
- * @see #make(String)
+ *
* @param cn the ClassNode containing a possible primitive type
* @return true if the ClassNode is a primitive type
+ * @see #make(Class)
+ * @see #make(String)
*/
public static boolean isPrimitiveType(ClassNode cn) {
- return cn == boolean_TYPE ||
+ return cn == boolean_TYPE ||
cn == char_TYPE ||
cn == byte_TYPE ||
cn == short_TYPE ||
@@ -331,17 +327,17 @@ public class ClassHelper {
/**
* Test to determine if a ClassNode is a type belongs to the list of types which
* are allowed to initialize constants directly in bytecode instead of using <cinit>
- *
+ * <p>
* Note: this only works for ClassNodes created using a
* predefined ClassNode
*
- * @see #make(Class)
- * @see #make(String)
* @param cn the ClassNode to be tested
* @return true if the ClassNode is of int, float, long, double or String type
+ * @see #make(Class)
+ * @see #make(String)
*/
public static boolean isStaticConstantInitializerType(ClassNode cn) {
- return cn == int_TYPE ||
+ return cn == int_TYPE ||
cn == float_TYPE ||
cn == long_TYPE ||
cn == double_TYPE ||
@@ -353,7 +349,7 @@ public class ClassHelper {
}
public static boolean isNumberType(ClassNode cn) {
- return cn == Byte_TYPE ||
+ return cn == Byte_TYPE ||
cn == Short_TYPE ||
cn == Integer_TYPE ||
cn == Long_TYPE ||
@@ -381,13 +377,14 @@ public class ClassHelper {
static class ClassHelperCache {
static ManagedConcurrentMap<Class, SoftReference<ClassNode>> classCache = new ManagedConcurrentMap<Class, SoftReference<ClassNode>>(ReferenceBundle.getWeakBundle());
}
-
+
public static boolean isSAMType(ClassNode type) {
return findSAM(type) != null;
}
/**
* Returns the single abstract method of a class node, if it is a SAM type, or null otherwise.
+ *
* @param type a type for which to search for a single abstract method
* @return the method node if type is a SAM type, null otherwise
*/
@@ -395,17 +392,17 @@ public class ClassHelper {
if (!Modifier.isAbstract(type.getModifiers())) return null;
if (type.isInterface()) {
List<MethodNode> methods = type.getMethods();
- MethodNode found=null;
+ MethodNode found = null;
for (MethodNode mi : methods) {
// ignore methods, that are not abstract and from Object
if (!Modifier.isAbstract(mi.getModifiers())) continue;
// ignore trait methods which have a default implementation
if (Traits.hasDefaultImplementation(mi)) continue;
if (mi.getDeclaringClass().equals(OBJECT_TYPE)) continue;
- if (OBJECT_TYPE.getDeclaredMethod(mi.getName(), mi.getParameters())!=null) continue;
+ if (OBJECT_TYPE.getDeclaredMethod(mi.getName(), mi.getParameters()) != null) continue;
// we have two methods, so no SAM
- if (found!=null) return null;
+ if (found != null) return null;
found = mi;
}
return found;
@@ -414,10 +411,10 @@ public class ClassHelper {
List<MethodNode> methods = type.getAbstractMethods();
MethodNode found = null;
- if (methods!=null) {
+ if (methods != null) {
for (MethodNode mi : methods) {
if (!hasUsableImplementation(type, mi)) {
- if (found!=null) return null;
+ if (found != null) return null;
found = mi;
}
}
@@ -427,12 +424,12 @@ public class ClassHelper {
}
private static boolean hasUsableImplementation(ClassNode c, MethodNode m) {
- if (c==m.getDeclaringClass()) return false;
+ if (c == m.getDeclaringClass()) return false;
MethodNode found = c.getDeclaredMethod(m.getName(), m.getParameters());
- if (found==null) return false;
+ if (found == null) return false;
int asp = found.getModifiers() & ABSTRACT_STATIC_PRIVATE;
int visible = found.getModifiers() & VISIBILITY;
- if (visible !=0 && asp == 0) return true;
+ if (visible != 0 && asp == 0) return true;
if (c.equals(OBJECT_TYPE)) return false;
return hasUsableImplementation(c.getSuperClass(), m);
}
@@ -440,14 +437,18 @@ public class ClassHelper {
/**
* Returns a super class or interface for a given class depending on a given target.
* If the target is no super class or interface, then null will be returned.
- * @param clazz the start class
+ * For a non-primitive array type, returns an array of the componentType's super class
+ * or interface if the target is also an array.
+ *
+ * @param clazz the start class
* @param goalClazz the goal class
* @return the next super class or interface
*/
public static ClassNode getNextSuperClass(ClassNode clazz, ClassNode goalClazz) {
if (clazz.isArray()) {
- ClassNode cn = getNextSuperClass(clazz.getComponentType(),goalClazz.getComponentType());
- if (cn!=null) cn = cn.makeArray();
+ if (!goalClazz.isArray()) return null;
+ ClassNode cn = getNextSuperClass(clazz.getComponentType(), goalClazz.getComponentType());
+ if (cn != null) cn = cn.makeArray();
return cn;
}
@@ -461,9 +462,9 @@ public class ClassHelper {
}
ClassNode[] interfaces = clazz.getUnresolvedInterfaces();
- for (int i=0; i<interfaces.length; i++) {
- if (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(interfaces[i],goalClazz)) {
- return interfaces[i];
+ for (ClassNode anInterface : interfaces) {
+ if (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(anInterface, goalClazz)) {
+ return anInterface;
}
}
//none of the interfaces here match, so continue with super class
http://git-wip-us.apache.org/repos/asf/groovy/blob/86be867e/src/main/org/codehaus/groovy/ast/tools/GenericsUtils.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/ast/tools/GenericsUtils.java b/src/main/org/codehaus/groovy/ast/tools/GenericsUtils.java
index 79449e4..653f327 100644
--- a/src/main/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -431,6 +431,8 @@ public class GenericsUtils {
if (target==null || type==target) return;
if (type.isArray() && target.isArray()) {
extractSuperClassGenerics(type.getComponentType(), target.getComponentType(), spec);
+ } else if (type.isArray() && target.getName().equals("java.lang.Object")) {
+ // Object is superclass of arrays but no generics involved
} else if (target.isGenericsPlaceHolder() || type.equals(target) || !StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(type, target)) {
// structural match route
if (target.isGenericsPlaceHolder()) {
http://git-wip-us.apache.org/repos/asf/groovy/blob/86be867e/src/main/org/codehaus/groovy/transform/DelegateASTTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/transform/DelegateASTTransformation.java b/src/main/org/codehaus/groovy/transform/DelegateASTTransformation.java
index 8e1daa5..d56da3d 100644
--- a/src/main/org/codehaus/groovy/transform/DelegateASTTransformation.java
+++ b/src/main/org/codehaus/groovy/transform/DelegateASTTransformation.java
@@ -164,6 +164,17 @@ public class DelegateASTTransformation extends AbstractASTTransformation {
addGetterIfNeeded(delegate, prop, name, allNames);
addSetterIfNeeded(delegate, prop, name, allNames);
}
+ if (delegate.type.isArray()) {
+ boolean skipLength = delegate.excludes != null && (delegate.excludes.contains("length") || delegate.excludes.contains("getLength"));
+ if (!skipLength) {
+ delegate.owner.addMethod("getLength",
+ ACC_PUBLIC,
+ ClassHelper.int_TYPE,
+ Parameter.EMPTY_ARRAY,
+ null,
+ returnS(propX(delegate.getOp, "length")));
+ }
+ }
if (skipInterfaces) return;
http://git-wip-us.apache.org/repos/asf/groovy/blob/86be867e/src/test/org/codehaus/groovy/transform/DelegateTransformTest.groovy
----------------------------------------------------------------------
diff --git a/src/test/org/codehaus/groovy/transform/DelegateTransformTest.groovy b/src/test/org/codehaus/groovy/transform/DelegateTransformTest.groovy
index 5dd6930..3321344 100644
--- a/src/test/org/codehaus/groovy/transform/DelegateTransformTest.groovy
+++ b/src/test/org/codehaus/groovy/transform/DelegateTransformTest.groovy
@@ -21,7 +21,7 @@ package org.codehaus.groovy.transform
import gls.CompilableTestSupport
/**
- * Tests for the @{code @Delegate} AST transform.
+ * Tests for the {@code @Delegate} AST transform.
*/
class DelegateTransformTest extends CompilableTestSupport {
@@ -811,6 +811,22 @@ assert foo.dm.x == '123'
assert new Foo(bar: new Bar()).pls() == 'foo pls'
'''
}
+
+ // GROOVY-8204
+ void testDelegateToArray() {
+ assertScript '''
+ import groovy.lang.Delegate
+
+ class BugsMe {
+ @Delegate
+ String[] content = ['foo', 'bar']
+ }
+
+ assert new BugsMe().content.join() == 'foobar'
+ assert new BugsMe().content.length == 2
+ assert new BugsMe().length == 2
+ '''
+ }
}
interface DelegateFoo {