Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Groovy 2.2.2

  • Loading branch information...
commit 11d48db2c717f2521c6802bd0f93bab04360d958 1 parent 6fee96e
@kdvolder kdvolder authored
Showing with 723 additions and 289 deletions.
  1. +1 −1  ...e.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/FailingErrorRecoveryTests.java
  2. +1 −1  ...rg.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GroovySimpleTest.java
  3. +1 −1  ...test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/SingleTest.java
  4. +1 −1  base/org.codehaus.groovy22/.classpath
  5. +2 −2 base/org.codehaus.groovy22/META-INF/MANIFEST.MF
  6. +7 −1 base/org.codehaus.groovy22/VERSION
  7. +5 −5 base/org.codehaus.groovy22/about.html
  8. +32 −0 base/org.codehaus.groovy22/git-differ.sh
  9. BIN  base/org.codehaus.groovy22/lib/{groovy-2.2.1-sources.jar → groovy-2.2.2-sources.jar}
  10. BIN  base/org.codehaus.groovy22/lib/{groovy-all-2.2.1.jar → groovy-all-2.2.2.jar}
  11. +43 −43 base/org.codehaus.groovy22/src/groovy/grape/GrabAnnotationTransformation.java
  12. +20 −10 base/org.codehaus.groovy22/src/groovy/grape/GrapeIvy.groovy
  13. +1 −1  base/org.codehaus.groovy22/src/org/codehaus/groovy/activator/GroovyActivator.java
  14. +42 −33 base/org.codehaus.groovy22/src/org/codehaus/groovy/ast/ClassHelper.java
  15. +10 −6 base/org.codehaus.groovy22/src/org/codehaus/groovy/ast/GenericsType.java
  16. +35 −6 base/org.codehaus.groovy22/src/org/codehaus/groovy/ast/ModuleNode.java
  17. +1 −6 base/org.codehaus.groovy22/src/org/codehaus/groovy/classgen/VariableScopeVisitor.java
  18. +12 −6 base/org.codehaus.groovy22/src/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
  19. +6 −4 base/org.codehaus.groovy22/src/org/codehaus/groovy/transform/DelegateASTTransformation.java
  20. +12 −4 base/org.codehaus.groovy22/src/org/codehaus/groovy/transform/ImmutableASTTransformation.java
  21. +490 −157 base/org.codehaus.groovy22/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
  22. +1 −1  ide/Feature org.codehaus.groovy22.feature/feature.xml
View
2  ...dt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/FailingErrorRecoveryTests.java
@@ -74,7 +74,7 @@ protected void tearDown() throws Exception {
String[] newcps = new String[cps.length+2];
System.arraycopy(cps,0,newcps,0,cps.length);
try {
- URL groovyJar = Platform.getBundle("org.codehaus.groovy").getEntry("lib/groovy-all-2.2.1.jar");
+ URL groovyJar = Platform.getBundle("org.codehaus.groovy").getEntry("lib/groovy-all-2.2.2.jar");
if (groovyJar == null) {
groovyJar = Platform.getBundle("org.codehaus.groovy").getEntry("lib/groovy-all-2.1.8.jar");
if (groovyJar==null) {
View
2  ...eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GroovySimpleTest.java
@@ -100,7 +100,7 @@ protected void tearDown() throws Exception {
String[] newcps = new String[cps.length+2];
System.arraycopy(cps,0,newcps,0,cps.length);
try {
- URL groovyJar = Platform.getBundle("org.codehaus.groovy").getEntry("lib/groovy-all-2.2.1.jar");
+ URL groovyJar = Platform.getBundle("org.codehaus.groovy").getEntry("lib/groovy-all-2.2.2.jar");
if (groovyJar==null) {
groovyJar = Platform.getBundle("org.codehaus.groovy").getEntry("lib/groovy-all-2.1.8.jar");
if (groovyJar==null) {
View
2  ...t/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/SingleTest.java
@@ -118,7 +118,7 @@ protected void tearDown() throws Exception {
String[] newcps = new String[cps.length+2];
System.arraycopy(cps,0,newcps,0,cps.length);
try {
- URL groovyJar = Platform.getBundle("org.codehaus.groovy").getEntry("lib/groovy-all-2.2.1.jar");
+ URL groovyJar = Platform.getBundle("org.codehaus.groovy").getEntry("lib/groovy-all-2.2.2.jar");
if (groovyJar==null) {
groovyJar = Platform.getBundle("org.codehaus.groovy").getEntry("lib/groovy-all-2.1.8.jar");
if (groovyJar==null) {
View
2  base/org.codehaus.groovy22/.classpath
@@ -6,6 +6,6 @@
<classpathentry kind="src" output="bin-trace" path="src-trace"/>
<classpathentry exported="true" kind="lib" path="lib/ivy-2.3.0.jar" sourcepath="lib/ivy-2.3.0-sources.jar"/>
<classpathentry exported="true" kind="lib" path="lib/bsf-2.4.0.jar"/>
- <classpathentry exported="true" kind="lib" path="lib/groovy-all-2.2.1.jar" sourcepath="lib/groovy-2.2.1-sources.jar"/>
+ <classpathentry exported="true" kind="lib" path="lib/groovy-all-2.2.2.jar" sourcepath="lib/groovy-2.2.2-sources.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
View
4 base/org.codehaus.groovy22/META-INF/MANIFEST.MF
@@ -2,11 +2,11 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Groovy Runtime Plug-in
Bundle-SymbolicName: org.codehaus.groovy
-Bundle-Version: 2.2.1.qualifier
+Bundle-Version: 2.2.2.qualifier
Bundle-ClassPath: groovy-eclipse.jar,
lib/ivy-2.3.0.jar,
lib/bsf-2.4.0.jar,
- lib/groovy-all-2.2.1.jar,
+ lib/groovy-all-2.2.2.jar,
eclipse-trace.jar
Groovy-Runtime-Jars: groovy.jar
Export-Package: groovy.beans,
View
8 base/org.codehaus.groovy22/VERSION
@@ -1,4 +1,10 @@
25-Sep-13: groovy-2.2.0-beta-2
30-Oct-13: groovy-2.2.0-rc-2
26-Nov-13: groovy-2.2.0
-20-Feb-13: groovy-2.2.1
+20-Feb-14: groovy-2.2.1
+24-Feb-14: groovy-2.2.2
+ Used GROOVY_2_2_X head rather than tagged version.
+ Hash = 9d9ae56a35b9b24384cae12cb10dacad809bdee9.
+ Manually edited gradle.properties to a release version.
+ When 2.2.2 is officially released / tagged check for last minute changes.
+ Then delete this comment :-)
View
10 base/org.codehaus.groovy22/about.html
@@ -34,21 +34,21 @@
<li>License text: <a href="about_files/asl-v20.txt">asl-v20.txt</a></li>
</ul>
-<h4>groovy-all-2.2.1-sources.jar</h4>
+<h4>groovy-all-2.2.2-sources.jar</h4>
<ul>
-<li>Obtained from: <a href="http://dist.groovy.codehaus.org/distributions/groovy-src-2.2.1.zip">http://dist.groovy.codehaus.org/distributions/groovy-src-2.2.1.zip</a></li>
+<li>Obtained from: <a href="http://dist.groovy.codehaus.org/distributions/groovy-src-2.2.2.zip">http://dist.groovy.codehaus.org/distributions/groovy-src-2.2.2.zip</a></li>
<li>Sources available at: This jar is a source jar</li>
<li>License kind: ASL</li>
<li>License URL: <a href="http://www.apache.org/licenses/LICENSE-2.0.html">http://www.apache.org/licenses/LICENSE-2.0.html</a></li>
<li>License text: <a href="about_files/asl-v20.txt">asl-v20.txt</a></li>
</ul>
-<h4>groovy-all-2.2.1.jar</h4>
+<h4>groovy-all-2.2.2.jar</h4>
<ul>
-<li>Obtained from: <a href="http://dist.groovy.codehaus.org/distributions/groovy-binary-2.2.1.zip">http://dist.groovy.codehaus.org/distributions/groovy-binary-2.2.1.zip</a></li>
-<li>Sources available at: <a href="http://dist.groovy.codehaus.org/distributions/groovy-src-2.2.1.zip">http://dist.groovy.codehaus.org/distributions/groovy-src-2.2.1.zip</a></li>
+<li>Obtained from: <a href="http://dist.groovy.codehaus.org/distributions/groovy-binary-2.2.2.zip">http://dist.groovy.codehaus.org/distributions/groovy-binary-2.2.2.zip</a></li>
+<li>Sources available at: <a href="http://dist.groovy.codehaus.org/distributions/groovy-src-2.2.2.zip">http://dist.groovy.codehaus.org/distributions/groovy-src-2.2.2.zip</a></li>
<li>License kind: ASL</li>
<li>License URL: <a href="http://www.apache.org/licenses/LICENSE-2.0.html">http://www.apache.org/licenses/LICENSE-2.0.html</a></li>
<li>License text: <a href="about_files/asl-v20.txt">asl-v20.txt</a></li>
View
32 base/org.codehaus.groovy22/git-differ.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+# Simple script to quickly check for changes between groovy versions, limited to just those changes that are
+# in files patched by us.
+
+# To use this script... define the following variables according to your environment
+
+#The tag of the previous new groovy version
+TAG_OLD=GROOVY_2_2_1
+TAG_NEW=GRECLIPS_2_2_2
+
+#Path to the root of groovy-core git clone
+GROOVY_CORE_REPO=/home/kdvolder/git/groovy-core
+
+# Run the script from the dir you found it in with
+# bash <script-name>
+
+### Shouldn't need to change anything below this line
+cd src
+INTERESTING_FILES=`find -print | sort`
+
+cd $GROOVY_CORE_REPO/src/main
+
+for f in $INTERESTING_FILES
+do
+ # Filter further: Only files that exist in both trees are actually interesting.
+ if [ -f $f ]
+ then
+ echo "================" $f
+ git diff $TAG_OLD $TAG_NEW -- $f
+ fi
+done
View
BIN  ...rg.codehaus.groovy22/lib/groovy-2.2.1-sources.jar → ...rg.codehaus.groovy22/lib/groovy-2.2.2-sources.jar
Binary file not shown
View
BIN  base/org.codehaus.groovy22/lib/groovy-all-2.2.1.jar → base/org.codehaus.groovy22/lib/groovy-all-2.2.2.jar
Binary file not shown
View
86 base/org.codehaus.groovy22/src/groovy/grape/GrabAnnotationTransformation.java
@@ -183,6 +183,7 @@ public void visit(ASTNode[] nodes, SourceUnit source) {
// better fix but this is easy. If there are duplicates it will work but we are calling
// grab with unnecessary dup info.
Collection<Map<String,Object>> grabMaps = new LinkedHashSet<Map<String,Object>>();
+ List<Map<String,Object>> grabMapsInit = new ArrayList<Map<String,Object>>();
List<Map<String,Object>> grabExcludeMaps = new ArrayList<Map<String,Object>>();
for (ClassNode classNode : sourceUnit.getAST().getClasses()) {
@@ -302,12 +303,17 @@ public void visit(ASTNode[] nodes, SourceUnit source) {
addError("Attribute \"" + s + "\" has value " + member.getText() + " but should be an inline constant in @" + node.getClassNode().getNameWithoutPackage() + " annotations", node);
continue grabAnnotationLoop;
}
- if (node.getMember(s) != null)
+ if (node.getMember(s) != null) {
grabMap.put(s, ((ConstantExpression)member).getValue());
+ }
}
grabMaps.add(grabMap);
- callGrabAsStaticInitIfNeeded(classNode, grapeClassNode, node, grabExcludeMaps);
+ if ((node.getMember("initClass") == null)
+ || (node.getMember("initClass") == ConstantExpression.TRUE)) {
+ grabMapsInit.add(grabMap);
+ }
}
+ callGrabAsStaticInitIfNeeded(classNode, grapeClassNode, grabMapsInit, grabExcludeMaps);
}
if (!grabResolverInitializers.isEmpty()) {
@@ -362,55 +368,49 @@ public void visit(ASTNode[] nodes, SourceUnit source) {
}
}
- private void callGrabAsStaticInitIfNeeded(ClassNode classNode, ClassNode grapeClassNode, AnnotationNode node, List<Map<String, Object>> grabExcludeMaps) {
- if ((node.getMember("initClass") == null)
- || (node.getMember("initClass") == ConstantExpression.TRUE))
- {
- List<Statement> grabInitializers = new ArrayList<Statement>();
+ private void callGrabAsStaticInitIfNeeded(ClassNode classNode, ClassNode grapeClassNode, List<Map<String,Object>> grabMapsInit, List<Map<String, Object>> grabExcludeMaps) {
+ List<Statement> grabInitializers = new ArrayList<Statement>();
+ MapExpression basicArgs = new MapExpression();
+ if (autoDownload != null) {
+ basicArgs.addMapEntryExpression(new ConstantExpression(AUTO_DOWNLOAD_SETTING), new ConstantExpression(autoDownload));
+ }
+ if (disableChecksums != null) {
+ basicArgs.addMapEntryExpression(new ConstantExpression(DISABLE_CHECKSUMS_SETTING), new ConstantExpression(disableChecksums));
+ }
+ if (!grabExcludeMaps.isEmpty()) {
+ ListExpression list = new ListExpression();
+ for (Map<String, Object> map : grabExcludeMaps) {
+ Set<Map.Entry<String, Object>> entries = map.entrySet();
+ MapExpression inner = new MapExpression();
+ for (Map.Entry<String, Object> entry : entries) {
+ inner.addMapEntryExpression(new ConstantExpression(entry.getKey()), new ConstantExpression(entry.getValue()));
+ }
+ list.addExpression(inner);
+ }
+ basicArgs.addMapEntryExpression(new ConstantExpression("excludes"), list);
+ }
+
+ List<Expression> argList = new ArrayList<Expression>();
+ argList.add(basicArgs);
+ for (Map<String, Object> grabMap : grabMapsInit) {
// add Grape.grab(excludeArgs, [group:group, module:module, version:version, classifier:classifier])
// or Grape.grab([group:group, module:module, version:version, classifier:classifier])
- MapExpression me = new MapExpression();
+ MapExpression dependencyArg = new MapExpression();
for (String s : GRAB_REQUIRED) {
- me.addMapEntryExpression(new ConstantExpression(s),node.getMember(s));
+ dependencyArg.addMapEntryExpression(new ConstantExpression(s), new ConstantExpression(grabMap.get(s)));
}
-
for (String s : GRAB_OPTIONAL) {
- if (node.getMember(s) != null)
- me.addMapEntryExpression(new ConstantExpression(s),node.getMember(s));
+ if (grabMap.containsKey(s))
+ dependencyArg.addMapEntryExpression(new ConstantExpression(s), new ConstantExpression(grabMap.get(s)));
}
-
- if (autoDownload != null) {
- me.addMapEntryExpression(new ConstantExpression(AUTO_DOWNLOAD_SETTING), new ConstantExpression(autoDownload));
- }
-
- if (disableChecksums != null) {
- me.addMapEntryExpression(new ConstantExpression(DISABLE_CHECKSUMS_SETTING), new ConstantExpression(disableChecksums));
- }
-
- ArgumentListExpression grabArgs;
- if (grabExcludeMaps.isEmpty()) {
- grabArgs = new ArgumentListExpression(me);
- } else {
- MapExpression args = new MapExpression();
- ListExpression list = new ListExpression();
- for (Map<String, Object> map : grabExcludeMaps) {
- Set<Map.Entry<String, Object>> entries = map.entrySet();
- MapExpression inner = new MapExpression();
- for (Map.Entry<String, Object> entry : entries) {
- inner.addMapEntryExpression(new ConstantExpression(entry.getKey()), new ConstantExpression(entry.getValue()));
- }
- list.addExpression(inner);
- }
- args.addMapEntryExpression(new ConstantExpression("excludes"), list);
- grabArgs = new ArgumentListExpression(args, me);
- }
- grabInitializers.add(new ExpressionStatement(
- new StaticMethodCallExpression(grapeClassNode, "grab", grabArgs)));
-
- // insert at beginning so we have the classloader set up before the class is called
- classNode.addStaticInitializerStatements(grabInitializers, true);
+ argList.add(dependencyArg);
}
+ ArgumentListExpression grabArgs = new ArgumentListExpression(argList);
+ grabInitializers.add(new ExpressionStatement(new StaticMethodCallExpression(grapeClassNode, "grab", grabArgs)));
+
+ // insert at beginning so we have the classloader set up before the class is called
+ classNode.addStaticInitializerStatements(grabInitializers, true);
}
private void addGrabResolverAsStaticInitIfNeeded(ClassNode grapeClassNode, AnnotationNode node,
View
30 base/org.codehaus.groovy22/src/groovy/grape/GrapeIvy.groovy
@@ -1,5 +1,5 @@
/*
- * Copyright 2003-2012 the original author or authors.
+ * Copyright 2003-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -354,17 +354,17 @@ class GrapeIvy implements GrapeEngine {
addExcludesIfNeeded(args, md)
for (IvyGrabRecord grabRecord : grabRecords) {
- DefaultDependencyDescriptor dd = new DefaultDependencyDescriptor(md,
- grabRecord.mrid, grabRecord.force, grabRecord.changing, grabRecord.transitive)
def conf = grabRecord.conf ?: ['*']
- conf.each {dd.addDependencyConfiguration('default', it)}
- if (grabRecord.classifier || grabRecord.ext) {
- def dad = new DefaultDependencyArtifactDescriptor(dd,
- grabRecord.mrid.name, grabRecord.type ?: 'jar', grabRecord.ext ?: 'jar', null, grabRecord.classifier ? [classifier:grabRecord.classifier] : null)
- conf.each { dad.addConfiguration(it) }
- dd.addDependencyArtifact('default', dad)
+ DefaultDependencyDescriptor dd = md.dependencies.find {it.dependencyRevisionId.equals(grabRecord.mrid)}
+ if (dd) {
+ createAndAddDependencyArtifactDescriptor(dd, grabRecord, conf)
+ } else {
+ dd = new DefaultDependencyDescriptor(md, grabRecord.mrid, grabRecord.force,
+ grabRecord.changing, grabRecord.transitive)
+ conf.each {dd.addDependencyConfiguration('default', it)}
+ createAndAddDependencyArtifactDescriptor(dd, grabRecord, conf)
+ md.addDependency(dd)
}
- md.addDependency(dd)
}
// resolve grab and dependencies
@@ -434,6 +434,16 @@ class GrapeIvy implements GrapeEngine {
return report
}
+ private void createAndAddDependencyArtifactDescriptor(DefaultDependencyDescriptor dd, IvyGrabRecord grabRecord, List<String> conf) {
+ // TODO: find out "unknown" reason and change comment below - also, confirm conf[0] check vs conf.contains('optional')
+ if (conf[0]!="optional" || grabRecord.classifier) { // for some unknown reason optional dependencies should not have an artifactDescriptor
+ def dad = new DefaultDependencyArtifactDescriptor(dd,
+ grabRecord.mrid.name, grabRecord.type ?: 'jar', grabRecord.ext ?: 'jar', null, grabRecord.classifier ? [classifier: grabRecord.classifier] : null)
+ conf.each { dad.addConfiguration(it) }
+ dd.addDependencyArtifact('default', dad)
+ }
+ }
+
public void uninstallArtifact(String group, String module, String rev) {
// TODO consider transitive uninstall as an option
Pattern ivyFilePattern = ~/ivy-(.*)\.xml/ //TODO get pattern from ivy conf
View
2  base/org.codehaus.groovy22/src/org/codehaus/groovy/activator/GroovyActivator.java
@@ -30,7 +30,7 @@
public static final String PLUGIN_ID = "org.codehaus.groovy"; //$NON-NLS-1$
- public static final String GROOVY_ALL_JAR = "lib/groovy-all-2.2.1.jar"; //$NON-NLS-1$
+ public static final String GROOVY_ALL_JAR = "lib/groovy-all-2.2.2.jar"; //$NON-NLS-1$
public static URL GROOVY_ALL_JAR_URL;
View
75 base/org.codehaus.groovy22/src/org/codehaus/groovy/ast/ClassHelper.java
@@ -415,39 +415,48 @@ public static boolean isCachedType(ClassNode type) {
static ManagedConcurrentMap<Class, SoftReference<ClassNode>> classCache = new ManagedConcurrentMap<Class, SoftReference<ClassNode>>(ReferenceBundle.getWeakBundle());
}
- public static boolean isSAMType(ClassNode type) {
- if (!Modifier.isAbstract(type.getModifiers())) return false;
- if (type.isInterface()) {
- List<MethodNode> methods = type.getMethods();
- boolean found=false;
- for (MethodNode mi : methods) {
- // ignore methods, that are not abstract and from Object
- if (!Modifier.isAbstract(mi.getModifiers())) continue;
- if (mi.getDeclaringClass().equals(OBJECT_TYPE)) continue;
- if (OBJECT_TYPE.getDeclaredMethod(mi.getName(), mi.getParameters())!=null) continue;
-
- // we have two methods, so no SAM
- if (found) return false;
- found = true;
- }
- return found;
-
- } else {
-
- List<MethodNode> methods = type.getAbstractMethods();
- boolean found = false;
- if (methods!=null) {
- for (MethodNode mi : methods) {
- if (!hasUsableImplementation(type, mi)) {
- if (found) return false;
- found = true;
- }
- }
- }
- return found;
- }
- }
-
+ 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
+ */
+ public static MethodNode findSAM(ClassNode type) {
+ if (!Modifier.isAbstract(type.getModifiers())) return null;
+ if (type.isInterface()) {
+ List<MethodNode> methods = type.getMethods();
+ MethodNode found=null;
+ for (MethodNode mi : methods) {
+ // ignore methods, that are not abstract and from Object
+ if (!Modifier.isAbstract(mi.getModifiers())) continue;
+ if (mi.getDeclaringClass().equals(OBJECT_TYPE)) continue;
+ if (OBJECT_TYPE.getDeclaredMethod(mi.getName(), mi.getParameters())!=null) continue;
+
+ // we have two methods, so no SAM
+ if (found!=null) return null;
+ found = mi;
+ }
+ return found;
+
+ } else {
+
+ List<MethodNode> methods = type.getAbstractMethods();
+ MethodNode found = null;
+ if (methods!=null) {
+ for (MethodNode mi : methods) {
+ if (!hasUsableImplementation(type, mi)) {
+ if (found!=null) return null;
+ found = mi;
+ }
+ }
+ }
+ return found;
+ }
+ }
+
private static boolean hasUsableImplementation(ClassNode c, MethodNode m) {
if (c==m.getDeclaringClass()) return false;
MethodNode found = c.getDeclaredMethod(m.getName(), m.getParameters());
View
16 base/org.codehaus.groovy22/src/org/codehaus/groovy/ast/GenericsType.java
@@ -98,12 +98,16 @@ public String toString() {
private String toString(Set<String> visited) {
if (placeholder) visited.add(name);
- String ret = wildcard?"?":((type == null || placeholder) ? name : genericsBounds(type, visited));
+ String ret = wildcard?"?":((type == null || placeholder) ? name : genericsBounds(type, visited));
if (upperBounds != null) {
- ret += " extends ";
- for (int i = 0; i < upperBounds.length; i++) {
- ret += genericsBounds(upperBounds[i], visited);
- if (i + 1 < upperBounds.length) ret += " & ";
+ if (placeholder && upperBounds.length==1 && !upperBounds[0].isGenericsPlaceHolder() && upperBounds[0].getName().equals("java.lang.Object")) {
+ // T extends Object should just be printed as T
+ } else {
+ ret += " extends ";
+ for (int i = 0; i < upperBounds.length; i++) {
+ ret += genericsBounds(upperBounds[i], visited);
+ if (i + 1 < upperBounds.length) ret += " & ";
+ }
}
} else if (lowerBound != null) {
ret += " super " + genericsBounds(lowerBound, visited);
@@ -327,7 +331,7 @@ private boolean checkGenerics(final ClassNode classNode) {
*/
private boolean compareGenericsWithBound(final ClassNode classNode, final ClassNode bound) {
if (classNode==null) return false;
- if (!bound.isUsingGenerics()) {
+ if (!bound.isUsingGenerics() || (classNode.getGenericsTypes()==null && classNode.redirect().getGenericsTypes()!=null)) {
// if the bound is not using generics, there's nothing to compare with
return true;
}
View
41 base/org.codehaus.groovy22/src/org/codehaus/groovy/ast/ModuleNode.java
@@ -17,6 +17,17 @@
import groovy.lang.Binding;
+import groovyjarjarasm.asm.Opcodes;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.ast.expr.ClassExpression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
@@ -24,12 +35,10 @@
import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.control.ErrorCollector;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.runtime.InvokerHelper;
-import groovyjarjarasm.asm.Opcodes;
-
-import java.io.File;
-import java.util.*;
+import org.codehaus.groovy.syntax.SyntaxException;
/**
* Represents a module, which consists typically of a class declaration
@@ -327,8 +336,17 @@ protected ClassNode createStatementsClass() {
new ClassExpression(classNode),
new VariableExpression("args"))))));
- classNode.addMethod(
- new MethodNode("run", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, statementBlock));
+ MethodNode methodNode = hasRunMethod();
+ if (methodNode!=null) {
+ ErrorCollector ec = context.getErrorCollector();
+ ec.addError(new SyntaxException("You cannot define a 'run()' method in a script because it is used to wrap the script body. Please choose another name.",
+ methodNode.getLineNumber(),
+ methodNode.getLineNumber()),
+ context);
+ } else {
+ classNode.addMethod(
+ new MethodNode("run", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, statementBlock));
+ }
classNode.addConstructor(ACC_PUBLIC, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, new BlockStatement());
Statement stmt = new ExpressionStatement(
@@ -360,6 +378,17 @@ protected ClassNode createStatementsClass() {
return classNode;
}
+ private MethodNode hasRunMethod() {
+ MethodNode methodNode = null;
+ for (MethodNode method : methods) {
+ if ("run".equals(method.getName()) && method.getParameters().length==0) {
+ methodNode = method;
+ break;
+ }
+ }
+ return methodNode;
+ }
+
/*
* If a main method is provided by user, account for it under run() as scripts generate their own 'main' so they can run.
*/
View
7 base/org.codehaus.groovy22/src/org/codehaus/groovy/classgen/VariableScopeVisitor.java
@@ -44,7 +44,6 @@
private VariableScope headScope = new VariableScope();
private ClassNode currentClass = null;
private SourceUnit source;
- private boolean inPropertyExpression = false;
private boolean isSpecialConstructorCall = false;
private boolean inConstructor = false;
@@ -290,7 +289,7 @@ private void checkPropertyOnExplicitThis(PropertyExpression pe) {
}
private void checkVariableContextAccess(Variable v, Expression expr) {
- if (inPropertyExpression || v.isInStaticContext() || !currentScope.isInStaticContext()) return;
+ if (v.isInStaticContext() || !currentScope.isInStaticContext()) return;
String msg = v.getName() +
" is declared in a dynamic context, but you tried to" +
@@ -405,13 +404,9 @@ public void visitVariableExpression(VariableExpression expression) {
}
public void visitPropertyExpression(PropertyExpression expression) {
- boolean ipe = inPropertyExpression;
- inPropertyExpression = true;
expression.getObjectExpression().visit(this);
- inPropertyExpression = false;
expression.getProperty().visit(this);
checkPropertyOnExplicitThis(expression);
- inPropertyExpression = ipe;
}
public void visitClosureExpression(ClosureExpression expression) {
View
18 base/org.codehaus.groovy22/src/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java
@@ -34,9 +34,7 @@
import java.util.*;
import static org.codehaus.groovy.ast.ClassHelper.*;
-import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.chooseBestMethod;
-import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.findDGMMethodsByNameAndArguments;
-import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf;
+import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.*;
/**
* A call site writer which replaces call site caching with static calls. This means that the generated code
@@ -93,9 +91,7 @@ public void makeGetPropertySite(Expression receiver, final String methodName, fi
receiverType = (ClassNode) type;
}
boolean isClassReceiver = false;
- if (receiverType.equals(CLASS_Type)
- && receiverType.getGenericsTypes()!=null
- && !receiverType.getGenericsTypes()[0].isPlaceholder()) {
+ if (isClassClassNodeWrappingConcreteType(receiverType)) {
isClassReceiver = true;
receiverType = receiverType.getGenericsTypes()[0].getType();
}
@@ -551,6 +547,11 @@ public void makeSingleArgumentCall(final Expression receiver, final String messa
MethodNode getAtNode = null;
while (current!=null && getAtNode==null) {
getAtNode = current.getMethod("getAt", new Parameter[]{new Parameter(aType, "index")});
+ if (getAtNode==null && isPrimitiveType(aType)) {
+ getAtNode = current.getMethod("getAt", new Parameter[]{new Parameter(getWrapper(aType), "index")});
+ } else if (getAtNode==null && aType.isDerivedFrom(Number_TYPE)) {
+ getAtNode = current.getMethod("getAt", new Parameter[]{new Parameter(getUnwrapper(aType), "index")});
+ }
current = current.getSuperClass();
}
if (getAtNode!=null) {
@@ -572,6 +573,11 @@ public void makeSingleArgumentCall(final Expression receiver, final String messa
MAP_TYPE.equals(rType) || rType.implementsInterface(MAP_TYPE)
|| LIST_TYPE.equals(rType) || rType.implementsInterface(LIST_TYPE);
List<MethodNode> nodes = StaticTypeCheckingSupport.findDGMMethodsByNameAndArguments(controller.getSourceUnit().getClassLoader(), rType, message, args);
+ if (nodes.isEmpty()) {
+ // retry with raw types
+ rType = rType.getPlainNodeReference();
+ nodes = StaticTypeCheckingSupport.findDGMMethodsByNameAndArguments(controller.getSourceUnit().getClassLoader(), rType, message, args);
+ }
nodes = StaticTypeCheckingSupport.chooseBestMethod(rType, nodes, args);
if (nodes.size()==1 || nodes.size()>1 && acceptAnyMethod) {
MethodNode methodNode = nodes.get(0);
View
10 base/org.codehaus.groovy22/src/org/codehaus/groovy/transform/DelegateASTTransformation.java
@@ -254,16 +254,18 @@ private void addDelegateMethod(AnnotationNode node, FieldNode fieldNode, ClassNo
args.addExpression(new VariableExpression(newParam));
}
// addMethod will ignore attempts to override abstract or static methods with same signature on self
+ MethodCallExpression mce = new MethodCallExpression(
+ new VariableExpression(fieldNode.getName(), nonGeneric(fieldNode.getOriginType())),
+ candidate.getName(),
+ args);
+ mce.setSourcePosition(fieldNode);
MethodNode newMethod = owner.addMethod(candidate.getName(),
candidate.getModifiers() & (~ACC_ABSTRACT) & (~ACC_NATIVE),
nonGeneric(candidate.getReturnType()),
newParams,
candidate.getExceptions(),
new ExpressionStatement(
- new MethodCallExpression(
- new VariableExpression(fieldNode),
- candidate.getName(),
- args)));
+ mce));
newMethod.setGenericsTypes(candidate.getGenericsTypes());
if (hasBooleanValue(node.getMember(MEMBER_METHOD_ANNOTATIONS), true)) {
View
16 base/org.codehaus.groovy22/src/org/codehaus/groovy/transform/ImmutableASTTransformation.java
@@ -323,7 +323,18 @@ private void createConstructorMap(ClassNode cNode, List<PropertyNode> list, List
// check for missing properties
Expression checkArgs = new ArgumentListExpression(new VariableExpression("this"), new VariableExpression("args"));
body.addStatement(new ExpressionStatement(new StaticMethodCallExpression(SELF_TYPE, "checkPropNames", checkArgs)));
+ body.addStatement(new IfStatement(equalsNullExpr(new VariableExpression("args")),
+ assignStatement(new VariableExpression("args"), new MapExpression()),
+ EmptyStatement.INSTANCE));
createConstructorMapCommon(cNode, body);
+ if (list.size() > 0) {
+ createNoArgConstructor(cNode);
+ }
+ }
+
+ private void createNoArgConstructor(ClassNode cNode) {
+ Statement body = new ExpressionStatement(new ConstructorCallExpression(ClassNode.THIS, new ArgumentListExpression(new MapExpression())));
+ doAddConstructor(cNode, new ConstructorNode(ACC_PUBLIC, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body));
}
private void createConstructorMapCommon(ClassNode cNode, BlockStatement body) {
@@ -338,10 +349,7 @@ private void createConstructorMapCommon(ClassNode cNode, BlockStatement body) {
body.addStatement(createConstructorStatementDefault(fNode));
}
final Parameter[] params = new Parameter[]{new Parameter(HASHMAP_TYPE, "args")};
- doAddConstructor(cNode,new ConstructorNode(ACC_PUBLIC, params, ClassNode.EMPTY_ARRAY, new IfStatement(
- equalsNullExpr(new VariableExpression("args")),
- EmptyStatement.INSTANCE,
- body)));
+ doAddConstructor(cNode, new ConstructorNode(ACC_PUBLIC, params, ClassNode.EMPTY_ARRAY, body));
}
private Statement checkFinalArgNotOverridden(ClassNode cNode, FieldNode fNode) {
View
647 base/org.codehaus.groovy22/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -890,7 +890,7 @@ public static boolean implementsInterfaceOrIsSubclassOf(ClassNode type, ClassNod
}
static int getPrimitiveDistance(ClassNode primA, ClassNode primB) {
- return Math.abs(NUMBER_TYPES.get(primA)-NUMBER_TYPES.get(primB));
+ return Math.abs(NUMBER_TYPES.get(primA) - NUMBER_TYPES.get(primB));
}
static int getDistance(final ClassNode receiver, final ClassNode compare) {
@@ -977,97 +977,38 @@ private static int getMaximumInterfaceDistance(ClassNode c, ClassNode interfaceC
public static List<MethodNode> findDGMMethodsByNameAndArguments(final ClassLoader loader, final ClassNode receiver, final String name, final ClassNode[] args, final List<MethodNode> methods) {
final List<MethodNode> chosen;
methods.addAll(findDGMMethodsForClassNode(loader, receiver, name));
+ if (methods.isEmpty()) return methods;
chosen = chooseBestMethod(receiver, methods, args);
- // specifically for DGM-like methods, we may have a generic type as the first argument of the DGM method
- // for example: DGM#getAt(T[], int) or DGM#putAt(T[], int, U)
- // in that case, we must verify that the chosen method match generic type information
- Iterator<MethodNode> iterator = chosen.iterator();
- while (iterator.hasNext()) {
- ExtensionMethodNode emn = (ExtensionMethodNode) iterator.next();
- MethodNode dgmMethod = emn.getExtensionMethodNode(); // this is the method from DGM
- GenericsType[] methodGenericTypes = dgmMethod.getGenericsTypes();
- if (methodGenericTypes !=null && methodGenericTypes.length>0) {
- Parameter[] parameters = dgmMethod.getParameters();
- ClassNode dgmOwnerType = parameters[0].getOriginType();
- if (dgmOwnerType.isGenericsPlaceHolder() || dgmOwnerType.isArray() && dgmOwnerType.getComponentType().isGenericsPlaceHolder()) {
- // first parameter of DGM method is a generic type or an array of generic type
-
- ClassNode receiverBase = receiver.isArray() ? receiver.getComponentType() : receiver;
- ClassNode receiverBaseRedirect = dgmOwnerType.isArray()?dgmOwnerType.getComponentType():dgmOwnerType;
- boolean mismatch = false;
- // ex: <T, U extends T> void putAt(T[], int, U)
- for (int i = 1; i < parameters.length && !mismatch; i++) {
- final int k = i - 1; // index of the actual parameter because of the extra receiver parameter in DGM
- ClassNode type = parameters[i].getOriginType();
- if (isUsingGenericsOrIsArrayUsingGenerics(type)) {
- // in a DGM-like method, the first parameter is the receiver. Because of type erasure,
- // it can only be T or T[]
- String receiverPlaceholder = receiverBaseRedirect.getGenericsTypes()[0].getName();
- ClassNode parameterBaseType = args[k].isArray() ? args[k].getComponentType() : args[k];
- ClassNode parameterBaseTypeRedirect = type.isArray() ? type.getComponentType() : type;
- GenericsType[] paramRedirectGenericsTypes = parameterBaseTypeRedirect.getGenericsTypes();
- GenericsType[] paramGenericTypes = parameterBaseType.getGenericsTypes();
- if (paramGenericTypes==null) {
- paramGenericTypes = new GenericsType[paramRedirectGenericsTypes.length];
- Arrays.fill(paramGenericTypes, new GenericsType(OBJECT_TYPE));
- } else {
- for (int j = 0; j < paramGenericTypes.length; j++) {
- GenericsType paramGenericType = paramGenericTypes[j];
- if (paramGenericType.isWildcard() || paramGenericType.isPlaceholder()) {
- // this may happen if an argument has been used without specifying a generic type
- // for example, foo(List) instead of foo(List<Object>)
- paramGenericTypes[j] = new GenericsType(OBJECT_TYPE);
- }
- }
- }
- for (int j = 0, genericsTypesLength = paramRedirectGenericsTypes.length; j < genericsTypesLength && !mismatch; j++) {
- final GenericsType gt = paramRedirectGenericsTypes[j];
- if (gt.isPlaceholder()) {
- List<GenericsType> fromMethodGenerics = new LinkedList<GenericsType>();
- for (GenericsType methodGenericType : methodGenericTypes) {
- if (methodGenericType.getName().equals(gt.getName())) {
- fromMethodGenerics.add(methodGenericType);
- break;
- }
- }
- while (!fromMethodGenerics.isEmpty()) {
- // type must either be T or a derived type from T (ex: U extends T)
- GenericsType test = fromMethodGenerics.remove(0);
- if (test.getName().equals(receiverPlaceholder)) {
- if (!implementsInterfaceOrIsSubclassOf(getWrapper(args[k]), getWrapper(receiverBase))) {
- mismatch = true;
- break;
- }
- } else if (test.getUpperBounds()!=null) {
- for (ClassNode classNode : test.getUpperBounds()) {
- GenericsType[] genericsTypes = classNode.getGenericsTypes();
- if (genericsTypes!=null) {
- for (GenericsType genericsType : genericsTypes) {
- if (genericsType.isPlaceholder()) {
- for (GenericsType methodGenericType : methodGenericTypes) {
- if (methodGenericType.getName().equals(genericsType.getName())) {
- fromMethodGenerics.add(methodGenericType);
- break;
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- if (mismatch) {
- iterator.remove();
- }
+ return chosen;
+ }
+
+ /**
+ * Returns true if the provided class node, when considered as a receiver of a message or as a parameter,
+ * is using a placeholder in its generics type. In this case, we're facing unchecked generics and type
+ * checking is limited (ex: void foo(Set s) { s.keySet() }
+ * @param node the node to test
+ * @return true if it is using any placeholder in generics types
+ */
+ public static boolean isUsingUncheckedGenerics(ClassNode node) {
+ if (node.isArray()) return isUsingUncheckedGenerics(node.getComponentType());
+ if (node.isUsingGenerics()) {
+ GenericsType[] genericsTypes = node.getGenericsTypes();
+ if (genericsTypes!=null) {
+ for (GenericsType genericsType : genericsTypes) {
+ if (genericsType.isPlaceholder()) {
+ return true;
+ } else {
+ if (isUsingUncheckedGenerics(genericsType.getType())) {
+ return true;
}
}
}
}
+ } else {
+ return false;
}
- return chosen;
+ return false;
}
/**
@@ -1081,13 +1022,29 @@ private static int getMaximumInterfaceDistance(ClassNode c, ClassNode interfaceC
*/
public static List<MethodNode> chooseBestMethod(final ClassNode receiver, Collection<MethodNode> methods, ClassNode... args) {
if (methods.isEmpty()) return Collections.emptyList();
+ if (isUsingUncheckedGenerics(receiver)) {
+ ClassNode raw = makeRawType(receiver);
+ return chooseBestMethod(raw, methods, args);
+ }
List<MethodNode> bestChoices = new LinkedList<MethodNode>();
int bestDist = Integer.MAX_VALUE;
- ClassNode actualReceiver;
Collection<MethodNode> choicesLeft = removeCovariants(methods);
- for (MethodNode m : choicesLeft) {
- final ClassNode declaringClass = m.getDeclaringClass();
- actualReceiver = receiver!=null?receiver: declaringClass;
+ for (MethodNode candidateNode : choicesLeft) {
+ ClassNode declaringClass = candidateNode.getDeclaringClass();
+ ClassNode actualReceiver = receiver!=null?receiver: declaringClass;
+ final ClassNode declaringClassForDistance = declaringClass;
+ final ClassNode actualReceiverForDistance = actualReceiver;
+ MethodNode safeNode = candidateNode;
+ ClassNode[] safeArgs = args;
+ if (candidateNode instanceof ExtensionMethodNode) {
+ safeArgs = new ClassNode[args.length+1];
+ System.arraycopy(args, 0, safeArgs, 1, args.length);
+ safeArgs[0] = receiver;
+ safeNode = ((ExtensionMethodNode) candidateNode).getExtensionMethodNode();
+ declaringClass = safeNode.getDeclaringClass();
+ actualReceiver = declaringClass;
+ }
+
// todo : corner case
/*
class B extends A {}
@@ -1099,37 +1056,37 @@ Person foo(B i){...}
Person p = foo(b)
*/
- Parameter[] params = parameterizeArguments(actualReceiver, m);
- if (params.length == args.length) {
- int allPMatch = allParametersAndArgumentsMatch(params, args);
+ Parameter[] params = parameterizeArguments(actualReceiver, safeNode);
+ if (params.length == safeArgs.length) {
+ int allPMatch = allParametersAndArgumentsMatch(params, safeArgs);
boolean firstParamMatches = true;
// check first parameters
- if (args.length > 0) {
+ if (safeArgs.length > 0) {
Parameter[] firstParams = new Parameter[params.length - 1];
System.arraycopy(params, 0, firstParams, 0, firstParams.length);
- firstParamMatches = allParametersAndArgumentsMatch(firstParams, args) >= 0;
+ firstParamMatches = allParametersAndArgumentsMatch(firstParams, safeArgs) >= 0;
}
- int lastArgMatch = isVargs(params) && firstParamMatches?lastArgMatchesVarg(params, args):-1;
+ int lastArgMatch = isVargs(params) && firstParamMatches?lastArgMatchesVarg(params, safeArgs):-1;
if (lastArgMatch>=0) {
lastArgMatch += 256-params.length; // ensure exact matches are preferred over vargs
}
int dist = allPMatch>=0?Math.max(allPMatch, lastArgMatch):lastArgMatch;
- if (dist>=0 && !actualReceiver.equals(declaringClass)) dist+=getDistance(actualReceiver, declaringClass);
+ if (dist>=0 && !actualReceiverForDistance.equals(declaringClassForDistance)) dist+=getDistance(actualReceiverForDistance, declaringClassForDistance);
if (dist>=0 && dist<bestDist) {
bestChoices.clear();
- bestChoices.add(m);
+ bestChoices.add(candidateNode);
bestDist = dist;
} else if (dist>=0 && dist==bestDist) {
- bestChoices.add(m);
+ bestChoices.add(candidateNode);
}
} else if (isVargs(params)) {
boolean firstParamMatches = true;
int dist = -1;
// check first parameters
- if (args.length > 0) {
+ if (safeArgs.length > 0) {
Parameter[] firstParams = new Parameter[params.length - 1];
System.arraycopy(params, 0, firstParams, 0, firstParams.length);
- dist = allParametersAndArgumentsMatch(firstParams, args);
+ dist = allParametersAndArgumentsMatch(firstParams, safeArgs);
firstParamMatches = dist >= 0;
} else {
dist = 0;
@@ -1137,32 +1094,32 @@ Person foo(B i){...}
if (firstParamMatches) {
// there are three case for vargs
// (1) varg part is left out
- if (params.length == args.length + 1) {
+ if (params.length == safeArgs.length + 1) {
if (dist>=0) {
dist += 256-params.length; // ensure exact matches are preferred over vargs
}
if (bestDist > 1+dist) {
bestChoices.clear();
- bestChoices.add(m);
+ bestChoices.add(candidateNode);
bestDist = 1+dist; // 1+dist to discriminate foo(Object,String) vs foo(Object,String, Object...)
}
} else {
// (2) last argument is put in the vargs array
// that case is handled above already
// (3) there is more than one argument for the vargs array
- dist += excessArgumentsMatchesVargsParameter(params, args);
- if (dist >= 0 && !actualReceiver.equals(declaringClass)) dist+=getDistance(actualReceiver, declaringClass);
+ dist += excessArgumentsMatchesVargsParameter(params, safeArgs);
+ if (dist >= 0 && !actualReceiverForDistance.equals(declaringClassForDistance)) dist+=getDistance(actualReceiverForDistance, declaringClassForDistance);
// varargs methods must not be preferred to methods without varargs
// for example :
// int sum(int x) should be preferred to int sum(int x, int... y)
dist+=256-params.length;
- if (params.length < args.length && dist >= 0) {
+ if (params.length < safeArgs.length && dist >= 0) {
if (dist >= 0 && dist < bestDist) {
bestChoices.clear();
- bestChoices.add(m);
+ bestChoices.add(candidateNode);
bestDist = dist;
} else if (dist >= 0 && dist == bestDist) {
- bestChoices.add(m);
+ bestChoices.add(candidateNode);
}
}
}
@@ -1172,6 +1129,16 @@ Person foo(B i){...}
return bestChoices;
}
+ private static ClassNode makeRawType(final ClassNode receiver) {
+ if (receiver.isArray()) {
+ return makeRawType(receiver.getComponentType()).makeArray();
+ }
+ ClassNode raw = receiver.getPlainNodeReference();
+ raw.setUsingGenerics(false);
+ raw.setGenericsTypes(null);
+ return raw;
+ }
+
private static Collection<MethodNode> removeCovariants(Collection<MethodNode> collection) {
if (collection.size()<=1) return collection;
List<MethodNode> toBeRemoved = new LinkedList<MethodNode>();
@@ -1232,69 +1199,418 @@ Person foo(B i){...}
* @return the parameterized arguments
*/
public static Parameter[] parameterizeArguments(final ClassNode receiver, final MethodNode m) {
- MethodNode mn = m;
- ClassNode actualReceiver = receiver;
- /*if (m instanceof ExtensionMethodNode) {
- ExtensionMethodNode emn = (ExtensionMethodNode) m;
- mn = emn.getExtensionMethodNode();
- actualReceiver = emn.getDeclaringClass();
- }*/
- List<GenericsType> redirectTypes = new ArrayList<GenericsType>();
-// if (mn.getGenericsTypes()!=null) Collections.addAll(redirectTypes,mn.getGenericsTypes());
- if (actualReceiver.redirect().getGenericsTypes()!=null) {
- Collections.addAll(redirectTypes,actualReceiver.redirect().getGenericsTypes());
+ Map<String, GenericsType> genericFromReceiver = GenericsUtils.extractPlaceholders(receiver);
+ Map<String, GenericsType> contextPlaceholders = extractGenericsParameterMapOfThis(m);
+ Parameter[] methodParameters = m.getParameters();
+ Parameter[] params = new Parameter[methodParameters.length];
+ for (int i = 0; i < methodParameters.length; i++) {
+ Parameter methodParameter = methodParameters[i];
+ ClassNode paramType = methodParameter.getType();
+ params[i] = buildParameter(genericFromReceiver, contextPlaceholders, methodParameter, paramType);
}
+ return params;
+ }
- if (redirectTypes.isEmpty()) {
- return m.getParameters();
+ /**
+ * Given a parameter, builds a new parameter for which the known generics placeholders are resolved.
+ * @param genericFromReceiver resolved generics from the receiver of the message
+ * @param placeholdersFromContext, resolved generics from the method context
+ * @param methodParameter the method parameter for which we want to resolve generic types
+ * @param paramType the (unresolved) type of the method parameter
+ * @return a new parameter with the same name and type as the original one, but with resolved generic types
+ */
+ private static Parameter buildParameter(final Map<String, GenericsType> genericFromReceiver, final Map<String, GenericsType> placeholdersFromContext, final Parameter methodParameter, final ClassNode paramType) {
+ if (genericFromReceiver.isEmpty() && (placeholdersFromContext==null||placeholdersFromContext.isEmpty())) {
+ return methodParameter;
}
- GenericsType[] redirectReceiverTypes = redirectTypes.toArray(new GenericsType[redirectTypes.size()]);
+ if (paramType.isArray()) {
+ ClassNode componentType = paramType.getComponentType();
+ Parameter subMethodParameter = new Parameter(componentType, methodParameter.getName());
+ Parameter component = buildParameter(genericFromReceiver, placeholdersFromContext, subMethodParameter, componentType);
+ return new Parameter(component.getType().makeArray(), component.getName());
+ }
+ ClassNode resolved = resolveClassNodeGenerics(genericFromReceiver, placeholdersFromContext, paramType);
- Parameter[] methodParameters = mn.getParameters();
- Parameter[] params = new Parameter[methodParameters.length];
- GenericsType[] receiverParameterizedTypes = actualReceiver.getGenericsTypes();
- if (receiverParameterizedTypes==null) {
- receiverParameterizedTypes = redirectReceiverTypes;
+ return new Parameter(resolved, methodParameter.getName());
+ }
+
+ /**
+ * Returns true if a class node makes use of generic types. If the class node represents an
+ * array type, then checks if the component type is using generics.
+ * @param cn a class node for which to check if it is using generics
+ * @return true if the type (or component type) is using generics
+ */
+ public static boolean isUsingGenericsOrIsArrayUsingGenerics(ClassNode cn) {
+ if (cn.isArray()) {
+ return isUsingGenericsOrIsArrayUsingGenerics(cn.getComponentType());
}
- for (int i = 0; i < methodParameters.length; i++) {
- Parameter methodParameter = methodParameters[i];
- ClassNode paramType = methodParameter.getType();
- if (paramType.isUsingGenerics()) {
- GenericsType[] alignmentTypes = paramType.getGenericsTypes();
- GenericsType[] genericsTypes = GenericsUtils.alignGenericTypes(redirectReceiverTypes, receiverParameterizedTypes, alignmentTypes);
- if (genericsTypes.length==1) {
- ClassNode parameterizedCN;
- if (paramType.equals(OBJECT_TYPE)) {
- parameterizedCN = genericsTypes[0].getType();
+ return (cn.isUsingGenerics() && cn.getGenericsTypes()!=null);
+ }
+
+ /**
+ * Given a generics type representing SomeClass&lt;T,V&gt; and a resolved placeholder map, returns a new generics type
+ * for which placeholders are resolved recursively.
+ */
+ protected static GenericsType fullyResolve(GenericsType gt, Map<String, GenericsType> placeholders) {
+ GenericsType fromMap = placeholders.get(gt.getName());
+ if (gt.isPlaceholder() && fromMap!=null) {
+ gt = fromMap;
+ }
+
+ ClassNode type = fullyResolveType(gt.getType(), placeholders);
+ ClassNode lowerBound = gt.getLowerBound();
+ if (lowerBound != null) lowerBound = fullyResolveType(lowerBound, placeholders);
+ ClassNode[] upperBounds = gt.getUpperBounds();
+ if (upperBounds != null) {
+ ClassNode[] copy = new ClassNode[upperBounds.length];
+ for (int i = 0, upperBoundsLength = upperBounds.length; i < upperBoundsLength; i++) {
+ final ClassNode upperBound = upperBounds[i];
+ copy[i] = fullyResolveType(upperBound, placeholders);
+ }
+ upperBounds = copy;
+ }
+ GenericsType genericsType = new GenericsType(type, upperBounds, lowerBound);
+ genericsType.setWildcard(gt.isWildcard());
+ return genericsType;
+ }
+
+ protected static ClassNode fullyResolveType(final ClassNode type, final Map<String, GenericsType> placeholders) {
+ if (type.isUsingGenerics() && !type.isGenericsPlaceHolder()) {
+ GenericsType[] gts = type.getGenericsTypes();
+ if (gts != null) {
+ GenericsType[] copy = new GenericsType[gts.length];
+ for (int i = 0; i < gts.length; i++) {
+ GenericsType genericsType = gts[i];
+ if (genericsType.isPlaceholder() && placeholders.containsKey(genericsType.getName())) {
+ copy[i] = placeholders.get(genericsType.getName());
} else {
- parameterizedCN= paramType.getPlainNodeReference();
- parameterizedCN.setGenericsTypes(genericsTypes);
+ copy[i] = fullyResolve(genericsType, placeholders);
}
- params[i] = new Parameter(
- parameterizedCN,
- methodParameter.getName()
- );
- } else {
- params[i] = methodParameter;
}
+ gts = copy;
+ }
+ ClassNode result = type.getPlainNodeReference();
+ result.setGenericsTypes(gts);
+ return result;
+ } else if (type.isUsingGenerics() && OBJECT_TYPE.equals(type) && type.getGenericsTypes() != null) {
+ // Object<T>
+ GenericsType genericsType = placeholders.get(type.getGenericsTypes()[0].getName());
+ if (genericsType != null) {
+ return genericsType.getType();
+ }
+ } else if (type.isArray()) {
+ return fullyResolveType(type.getComponentType(), placeholders).makeArray();
+ }
+ return type;
+ }
+
+ /**
+ * Checks that the parameterized generics of an argument are compatible with the generics of the parameter.
+ *
+ * @param parameterType the parameter type of a method
+ * @param argumentType the type of the argument passed to the method
+ */
+ protected static boolean typeCheckMethodArgumentWithGenerics(ClassNode parameterType, ClassNode argumentType, boolean lastArg) {
+ if (UNKNOWN_PARAMETER_TYPE == argumentType) {
+ // called with null
+ return true;
+ }
+ if (!isAssignableTo(argumentType, parameterType) && !lastArg) {
+ // incompatible assignment
+ return false;
+ }
+ if (!isAssignableTo(argumentType, parameterType) && lastArg) {
+ if (parameterType.isArray()) {
+ if (!isAssignableTo(argumentType, parameterType.getComponentType())) {
+ return false;
+ }
+ }
+ }
+ if (parameterType.isUsingGenerics() && argumentType.isUsingGenerics()) {
+ GenericsType gt = GenericsUtils.buildWildcardType(parameterType);
+ if (!gt.isCompatibleWith(argumentType)) {
+ return false;
+ }
+ } else if (parameterType.isArray() && argumentType.isArray()) {
+ // verify component type
+ return typeCheckMethodArgumentWithGenerics(parameterType.getComponentType(), argumentType.getComponentType(), lastArg);
+ } else if (lastArg && parameterType.isArray()) {
+ // verify component type, but if we reach that point, the only possibility is that the argument is
+ // the last one of the call, so we're in the cast of a vargs call
+ // (otherwise, we face a type checker bug)
+ return typeCheckMethodArgumentWithGenerics(parameterType.getComponentType(), argumentType, lastArg);
+ }
+ return true;
+ }
+
+ protected static boolean typeCheckMethodsWithGenerics(ClassNode receiver, ClassNode[] arguments, MethodNode candidateMethod) {
+ if (isUsingUncheckedGenerics(receiver)) {
+ return true;
+ }
+ if (CLASS_Type.equals(receiver)
+ && receiver.isUsingGenerics()
+ && candidateMethod.getDeclaringClass() != receiver
+ && !(candidateMethod instanceof ExtensionMethodNode)) {
+ return typeCheckMethodsWithGenerics(receiver.getGenericsTypes()[0].getType(), arguments, candidateMethod);
+ }
+ boolean failure = false;
+ // both candidate method and receiver have generic information so a check is possible
+ Parameter[] parameters = candidateMethod.getParameters();
+ GenericsType[] genericsTypes = candidateMethod.getGenericsTypes();
+ boolean methodUsesGenerics = (genericsTypes != null && genericsTypes.length > 0);
+ boolean isExtensionMethod = candidateMethod instanceof ExtensionMethodNode;
+ if (isExtensionMethod && methodUsesGenerics) {
+ ClassNode[] dgmArgs = new ClassNode[arguments.length + 1];
+ dgmArgs[0] = receiver;
+ System.arraycopy(arguments, 0, dgmArgs, 1, arguments.length);
+ MethodNode extensionMethodNode = ((ExtensionMethodNode) candidateMethod).getExtensionMethodNode();
+ return typeCheckMethodsWithGenerics(extensionMethodNode.getDeclaringClass(), dgmArgs, extensionMethodNode);
+
+
+ }
+ Map<String, GenericsType> classGTs = GenericsUtils.extractPlaceholders(receiver);
+ if (parameters.length > arguments.length || parameters.length==0) {
+ // this is a limitation that must be removed in a future version
+ // we cannot check generic type arguments if there are default parameters!
+ return true;
+ }
+ Map<String, ClassNode> resolvedMethodGenerics = new HashMap<String, ClassNode>();
+ final GenericsType[] methodNodeGenericsTypes = candidateMethod.getGenericsTypes();
+ final boolean shouldCheckMethodGenericTypes = methodNodeGenericsTypes!=null && methodNodeGenericsTypes.length>0;
+ for (int i = 0; i < arguments.length; i++) {
+ int pindex = Math.min(i, parameters.length - 1);
+ ClassNode type = parameters[pindex].getType();
+ type = fullyResolveType(type, classGTs);
+ failure |= !typeCheckMethodArgumentWithGenerics(type, arguments[i], i >= parameters.length - 1);
+ if (shouldCheckMethodGenericTypes && !failure) {
+ // GROOVY-5692
+ // for example: public <T> foo(T arg0, List<T> arg1)
+ // we must check that T for arg0 and arg1 are the same
+ // so that if you call foo(String, List<Integer>) the compiler fails
+
+ // For that, we store the information for each argument, and for a new argument, we will
+ // check that is is the same as the previous one
+ while (type.isArray()) {
+ type = type.getComponentType();
+ }
+ GenericsType[] typeGenericsTypes = type.getGenericsTypes();
+ if (type.isUsingGenerics() && typeGenericsTypes !=null) {
+ for (int gtIndex = 0, typeGenericsTypesLength = typeGenericsTypes.length; gtIndex < typeGenericsTypesLength; gtIndex++) {
+ final GenericsType typeGenericsType = typeGenericsTypes[gtIndex];
+ if (typeGenericsType.isPlaceholder()) {
+ for (GenericsType methodNodeGenericsType : methodNodeGenericsTypes) {
+ String placeholderName = methodNodeGenericsType.getName();
+ if (methodNodeGenericsType.isPlaceholder() && placeholderName.equals(typeGenericsType.getName())) {
+ // match!
+ ClassNode argument = arguments[i];
+ if (argument==UNKNOWN_PARAMETER_TYPE) {
+ continue;
+ }
+ while (argument.isArray()) {
+ argument = argument.getComponentType();
+ }
+ ClassNode parameterized = GenericsUtils.parameterizeType(argument, type);
+ // retrieve the type of the generics placeholder we're looking for
+ // For example, if we have List<T> in the signature and List<String> as an argument
+ // we want to align T with String
+ // but first test is for Object<T> -> String which explains we don't use the generics types
+
+ if (type.isGenericsPlaceHolder()) {
+ String name = type.getGenericsTypes()[0].getName();
+ if (name.equals(placeholderName)) {
+ if (resolvedMethodGenerics.containsKey(name)) {
+ failure |= !GenericsUtils.buildWildcardType(resolvedMethodGenerics.get(name)).isCompatibleWith(parameterized);
+ } else {
+ resolvedMethodGenerics.put(name, parameterized);
+ }
+ }
+ } else {
+ if (type.isUsingGenerics() && type.getGenericsTypes()!=null) {
+ // we have a method parameter type which is for example List<T>
+ // and an actual argument which is FooList
+ // which has been aligned to List<E> thanks to parameterizeType
+ // then in theory both the parameterized type and the method parameter type
+ // are the same type but with different type arguments
+ // that we need to align
+ GenericsType[] gtInParameter = type.getGenericsTypes();
+ GenericsType[] gtInArgument = parameterized.getGenericsTypes();
+ if (gtInArgument!=null && gtInArgument.length==gtInParameter.length) {
+ for (int j = 0; j < gtInParameter.length; j++) {
+ GenericsType genericsType = gtInParameter[j];
+ if (genericsType.getName().equals(placeholderName)) {
+ ClassNode actualType = gtInArgument[j].getType();
+ if (gtInArgument[j].isPlaceholder()
+ && gtInArgument[j].getName().equals(placeholderName)
+ && resolvedMethodGenerics.containsKey(placeholderName)) {
+ // GROOVY-5724
+ actualType = resolvedMethodGenerics.get(placeholderName);
+ }
+ if (resolvedMethodGenerics.containsKey(placeholderName)) {
+ failure |= !GenericsUtils.buildWildcardType(resolvedMethodGenerics.get(placeholderName)).isCompatibleWith(actualType);
+ } else if (!actualType.isGenericsPlaceHolder()) {
+ resolvedMethodGenerics.put(placeholderName, actualType);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ }
+ }
+ if (!failure && genericsTypes!=null) {
+ // last check, verify generic type constraints!
+ for (GenericsType type : genericsTypes) {
+ ClassNode node = resolvedMethodGenerics.get(type.getName());
+ if (node!=null && type.getUpperBounds()!=null) {
+ // U extends T
+ for (ClassNode classNode : type.getUpperBounds()) {
+ if (classNode.isGenericsPlaceHolder()) {
+ ClassNode resolved = resolvedMethodGenerics.get(classNode.getGenericsTypes()[0].getName());
+ if (resolved!=null) {
+ failure |= !GenericsUtils.buildWildcardType(resolved).isCompatibleWith(node);
+ }
+ }
+ }
+ }
+ if (type.getLowerBound()!=null) {
+ ClassNode resolved = resolvedMethodGenerics.get(type.getLowerBound().getGenericsTypes()[0].getName());
+ if (resolved!=null) {
+ failure = !GenericsUtils.buildWildcardType(node).isCompatibleWith(resolved);
+ }
+ }
+ }
+ }
+ return !failure;
+ }
+
+ public static ClassNode resolveClassNodeGenerics(final Map<String, GenericsType> resolvedPlaceholders, final Map<String, GenericsType> placeholdersFromContext, ClassNode currentType) {
+ applyContextGenerics(resolvedPlaceholders,placeholdersFromContext);
+ currentType = applyGenerics(currentType, resolvedPlaceholders);
+
+ // GROOVY-5748
+ if (currentType.isGenericsPlaceHolder()) {
+ GenericsType resolved = resolvedPlaceholders.get(currentType.getUnresolvedName());
+ if (resolved!=null && !resolved.isPlaceholder() && !resolved.isWildcard()) {
+ return resolved.getType();
+ }
+ }
+
+ GenericsType[] returnTypeGenerics = getGenericsWithoutArray(currentType);
+ if (returnTypeGenerics==null || returnTypeGenerics.length==0) return currentType;
+ GenericsType[] copy = new GenericsType[returnTypeGenerics.length];
+ for (int i = 0; i < copy.length; i++) {
+ GenericsType returnTypeGeneric = returnTypeGenerics[i];
+ if (returnTypeGeneric.isPlaceholder() || returnTypeGeneric.isWildcard()) {
+ GenericsType resolved = resolvedPlaceholders.get(returnTypeGeneric.getName());
+ if (resolved == null) resolved = returnTypeGeneric;
+ copy[i] = fullyResolve(resolved, resolvedPlaceholders);
} else {
- params[i] = methodParameter;
+ copy[i] = fullyResolve(returnTypeGeneric, resolvedPlaceholders);
}
}
- /*if (m instanceof ExtensionMethodNode) {
- // the parameter array we're using is the one from the extension
- // but we want to return an array for the regular method
- Parameter[] result = new Parameter[params.length-1];
- // 0 is the receiver
- // 1..n are what we want to return
- System.arraycopy(params, 1, result, 0, result.length);
- return result;
- }*/
- return params;
+ GenericsType firstGenericsType = copy[0];
+ if (currentType.equals(OBJECT_TYPE)) {
+ if (firstGenericsType.getType().isGenericsPlaceHolder()) return OBJECT_TYPE;
+
+ if (firstGenericsType.isWildcard()) {
+ // ? extends Foo
+ // ? super Foo
+ // ?
+ if (firstGenericsType.getLowerBound() != null) return firstGenericsType.getLowerBound();
+ ClassNode[] upperBounds = firstGenericsType.getUpperBounds();
+ if (upperBounds==null) { // case "?"
+ return OBJECT_TYPE;
+ }
+ if (upperBounds.length == 1) return upperBounds[0];
+ return new UnionTypeClassNode(upperBounds);
+ }
+ return firstGenericsType.getType();
+ }
+ if (currentType.isArray()) {
+ currentType = currentType.getComponentType().getPlainNodeReference();
+ currentType.setGenericsTypes(copy);
+ if (OBJECT_TYPE.equals(currentType)) {
+ // replace Object<Component> with Component
+ currentType = firstGenericsType.getType();
+ }
+ currentType = currentType.makeArray();
+ } else {
+ currentType = currentType.getPlainNodeReference();
+ currentType.setGenericsTypes(copy);
+ }
+ if (currentType.equals(Annotation_TYPE) && currentType.getGenericsTypes() != null && !currentType.getGenericsTypes()[0].isPlaceholder()) {
+ return currentType.getGenericsTypes()[0].getType();
+ }
+ return currentType;
+ }
+
+ static GenericsType[] getGenericsWithoutArray(ClassNode type) {
+ if (type.isArray()) return getGenericsWithoutArray(type.getComponentType());
+ return type.getGenericsTypes();
}
- static boolean isUsingGenericsOrIsArrayUsingGenerics(ClassNode cn) {
- return (cn.isUsingGenerics() && cn.getGenericsTypes()!=null) || cn.isArray() && cn.getComponentType().isUsingGenerics();
+ private static ClassNode applyGenerics(ClassNode type, Map<String, GenericsType> resolvedPlaceholders) {
+ if (type.isGenericsPlaceHolder()) {
+ String name = type.getUnresolvedName();
+ GenericsType gt = resolvedPlaceholders.get(name);
+ if (gt!=null && gt.isPlaceholder()) {
+ //TODO: have to handle more cases here
+ if (gt.getUpperBounds()!=null) return gt.getUpperBounds()[0];
+ return type;
+ }
+ }
+ return type;
+ }
+
+ private static void applyContextGenerics(Map<String, GenericsType> resolvedPlaceholders, Map<String, GenericsType> placeholdersFromContext) {
+ if (placeholdersFromContext==null) return;
+ for (Map.Entry<String, GenericsType> entry : resolvedPlaceholders.entrySet()) {
+ GenericsType gt = entry.getValue();
+ if (gt.isPlaceholder()) {
+ String name = gt.getName();
+ GenericsType outer = placeholdersFromContext.get(name);
+ if (outer==null) continue;
+ entry.setValue(outer);
+ }
+ }
+ }
+
+ private static Map<String, GenericsType> getGenericsParameterMapOfThis(ClassNode cn) {
+ if (cn==null) return null;
+ Map<String, GenericsType> map = null;
+ if (cn.getEnclosingMethod()!=null) {
+ map = extractGenericsParameterMapOfThis(cn.getEnclosingMethod());
+ } else if (cn.getOuterClass()!=null) {
+ map = getGenericsParameterMapOfThis(cn.getOuterClass());
+ }
+ map = mergeGenerics(map, cn.getGenericsTypes());
+ return map;
+ }
+
+ static Map<String, GenericsType> extractGenericsParameterMapOfThis(MethodNode mn) {
+ if (mn==null) return null;
+ Map<String, GenericsType> map = getGenericsParameterMapOfThis(mn.getDeclaringClass());
+ map = mergeGenerics(map, mn.getGenericsTypes());
+ return map;
+ }
+
+ private static Map<String, GenericsType> mergeGenerics(Map<String, GenericsType> current, GenericsType[] newGenerics) {
+ if (newGenerics == null || newGenerics.length == 0) return null;
+ if (current==null) current = new HashMap<String, GenericsType>();
+ for (int i = 0; i < newGenerics.length; i++) {
+ GenericsType gt = newGenerics[i];
+ if (!gt.isPlaceholder()) continue;
+ String name = gt.getName();
+ if (!current.containsKey(name)) current.put(name, newGenerics[i]);
+ }
+ return current;
}
/**
@@ -1526,4 +1842,21 @@ private static void collectAllInterfaces(final ClassNode node, final Set<ClassNo
out.addAll(allInterfaces);
collectAllInterfaces(node.getSuperClass(), out);
}
+
+ /**
+ * Returns true if the class node represents a the class node for the Class class
+ * and if the parametrized type is a neither a placeholder or a wildcard. For example,
+ * the class node Class&lt;Foo&gt; where Foo is a class would return true, but the class
+ * node for Class&lt;?&gt; would return false.
+ * @param classNode a class node to be tested
+ * @return true if it is the class node for Class and its generic type is a real class
+ */
+ public static boolean isClassClassNodeWrappingConcreteType(ClassNode classNode) {
+ GenericsType[] genericsTypes = classNode.getGenericsTypes();
+ return ClassHelper.CLASS_Type.equals(classNode)
+ && classNode.isUsingGenerics()
+ && genericsTypes!=null
+ && !genericsTypes[0].isPlaceholder()
+ && !genericsTypes[0].isWildcard();
+ }
}
View
2  ide/Feature org.codehaus.groovy22.feature/feature.xml
@@ -35,7 +35,7 @@
id="org.codehaus.groovy"
download-size="0"
install-size="0"
- version="2.2.1.qualifier"/>
+ version="2.2.2.qualifier"/>
<plugin
id="org.codehaus.groovy.eclipse"
Please sign in to comment.
Something went wrong with that request. Please try again.