Skip to content

Commit ee8e9e4

Browse files
committed
GROOVY-10071, GROOVY-10072
1 parent aa1935a commit ee8e9e4

File tree

19 files changed

+1086
-48
lines changed

19 files changed

+1086
-48
lines changed

base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/StaticCompilationTests.java

+43-3
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ public void testCompileStatic12() {
333333
"1. ERROR in Main.groovy (at line 3)\n" +
334334
"\tdef list = new LinkedList<String>([1,2,3])\n" +
335335
"\t ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
336-
"Groovy:[Static type checking] - Cannot call java.util.LinkedList#<init>(java.util.Collection<? extends java.lang.String>) with arguments [java.util.List<java.lang.Integer>] \n" +
336+
"Groovy:[Static type checking] - Cannot call java.util.LinkedList#<init>(java.util.Collection<? extends java.lang.String>) with arguments [java.util.List<java.lang.Integer>]\n" +
337337
"----------\n");
338338
}
339339

@@ -4551,7 +4551,7 @@ public void testCompileStatic9338() {
45514551
"1. ERROR in Main.groovy (at line 7)\n" +
45524552
"\tmeth(c)\n" +
45534553
"\t^^^^^^^\n" +
4554-
"Groovy:[Static type checking] - Cannot call Main#meth(java.lang.Class<? extends java.lang.CharSequence>) with arguments [java.lang.Class<?>] \n" +
4554+
"Groovy:[Static type checking] - Cannot call Main#meth(java.lang.Class<? extends java.lang.CharSequence>) with arguments [java.lang.Class<?>]\n" +
45554555
"----------\n");
45564556
}
45574557

@@ -4577,7 +4577,7 @@ public void testCompileStatic9338a() {
45774577
"1. ERROR in Main.groovy (at line 7)\n" +
45784578
"\tmeth(c)\n" +
45794579
"\t^^^^^^^\n" +
4580-
"Groovy:[Static type checking] - Cannot call Main#meth(java.lang.Class<? super java.lang.CharSequence>) with arguments [java.lang.Class<?>] \n" +
4580+
"Groovy:[Static type checking] - Cannot call Main#meth(java.lang.Class<? super java.lang.CharSequence>) with arguments [java.lang.Class<?>]\n" +
45814581
"----------\n");
45824582
}
45834583

@@ -6166,4 +6166,44 @@ public void testCompileStatic10047() {
61666166

61676167
runConformTest(sources, "[a:1, bc:2, def:3]");
61686168
}
6169+
6170+
@Test
6171+
public void testCompileStatic10071() {
6172+
//@formatter:off
6173+
String[] sources = {
6174+
"Main.groovy",
6175+
"@groovy.transform.CompileStatic\n" +
6176+
"void test() {\n" +
6177+
" def c = { ... zeroOrMore -> 'foo' + zeroOrMore }\n" +
6178+
" assert c('bar', 'baz') == 'foo[bar, baz]'\n" +
6179+
" assert c('bar') == 'foo[bar]'\n" +
6180+
" assert c() == 'foo[]'\n" +
6181+
"}\n" +
6182+
"test()\n",
6183+
};
6184+
//@formatter:on
6185+
6186+
runConformTest(sources);
6187+
}
6188+
6189+
@Test
6190+
public void testCompileStatic10072() {
6191+
//@formatter:off
6192+
String[] sources = {
6193+
"Main.groovy",
6194+
"@groovy.transform.CompileStatic\n" +
6195+
"void test() {\n" +
6196+
" def c = { p = 'foo' -> return p }\n" +
6197+
" assert c('bar') == 'bar'\n" +
6198+
" assert c() == 'foo'\n" +
6199+
" c = { p, q = 'baz' -> '' + p + q }\n" +
6200+
" assert c('foo', 'bar') == 'foobar'\n" +
6201+
" assert c('foo') == 'foobaz'\n" +
6202+
"}\n" +
6203+
"test()\n",
6204+
};
6205+
//@formatter:on
6206+
6207+
runConformTest(sources);
6208+
}
61696209
}

base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/TypeCheckedTests.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -624,7 +624,7 @@ public void testTypeChecked7945() {
624624
"1. ERROR in Test.groovy (at line 12)\n" +
625625
"\tsuper(Integer, String)\n" +
626626
"\t^^^^^^^^^^^^^^^^^^^^^^\n" +
627-
"Groovy:[Static type checking] - Cannot call A#<init>(java.lang.Class<java.lang.String>, java.lang.Class<java.lang.Integer>) with arguments [java.lang.Class<java.lang.Integer>, java.lang.Class<java.lang.String>] \n" +
627+
"Groovy:[Static type checking] - Cannot call A#<init>(java.lang.Class<java.lang.String>, java.lang.Class<java.lang.Integer>) with arguments [java.lang.Class<java.lang.Integer>, java.lang.Class<java.lang.String>]\n" +
628628
"----------\n");
629629
}
630630

@@ -1375,17 +1375,17 @@ public void testTypeChecked9902() {
13751375
"1. ERROR in Main.groovy (at line 19)\n" +
13761376
"\th.stringProperty.eq(\"${0}\")\n" +
13771377
"\t^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
1378-
"Groovy:[Static type checking] - Cannot call TypedProperty#eq(java.lang.String) with arguments [groovy.lang.GString] \n" +
1378+
"Groovy:[Static type checking] - Cannot call TypedProperty#eq(java.lang.String) with arguments [groovy.lang.GString]\n" +
13791379
"----------\n" +
13801380
"2. ERROR in Main.groovy (at line 21)\n" +
13811381
"\tstringProperty.eq(1234)\n" +
13821382
"\t^^^^^^^^^^^^^^^^^^^^^^^\n" +
1383-
"Groovy:[Static type checking] - Cannot call TypedProperty#eq(java.lang.String) with arguments [int] \n" +
1383+
"Groovy:[Static type checking] - Cannot call TypedProperty#eq(java.lang.String) with arguments [int]\n" +
13841384
"----------\n" +
13851385
"3. ERROR in Main.groovy (at line 22)\n" +
13861386
"\tnumberProperty.eq('xx')\n" +
13871387
"\t^^^^^^^^^^^^^^^^^^^^^^^\n" +
1388-
"Groovy:[Static type checking] - Cannot call TypedProperty#eq(java.lang.Number) with arguments [java.lang.String] \n" +
1388+
"Groovy:[Static type checking] - Cannot call TypedProperty#eq(java.lang.Number) with arguments [java.lang.String]\n" +
13891389
"----------\n");
13901390
}
13911391

base/org.codehaus.groovy25/.checkstyle

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
<file-match-pattern match-pattern="groovy/classgen/asm/sc/StaticInvocationWriter.java" include-pattern="false" />
5555
<file-match-pattern match-pattern="groovy/classgen/asm/sc/StaticPropertyAccessHelper.java" include-pattern="false" />
5656
<file-match-pattern match-pattern="groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java" include-pattern="false" />
57-
<file-match-pattern match-pattern="groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java" include-pattern="false" />
57+
<file-match-pattern match-pattern="groovy/classgen/asm/sc/StaticTypes(CallSite|Closure)Writer.java" include-pattern="false" />
5858
<file-match-pattern match-pattern="groovy/classgen/asm/sc/StaticTypesTypeChooser.java" include-pattern="false" />
5959
<file-match-pattern match-pattern="groovy/classgen/asm/sc/StaticTypesUnaryExpressionHelper.java" include-pattern="false" />
6060
<file-match-pattern match-pattern="groovy/control/CompilationUnit.java" include-pattern="false" />

base/org.codehaus.groovy25/src/org/codehaus/groovy/classgen/asm/ClosureWriter.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ protected ClassNode createClosureClass(ClosureExpression expression, int mods) {
198198
parameters = Parameter.EMPTY_ARRAY;
199199
} else if (parameters.length == 0) {
200200
// let's create a default 'it' parameter
201-
Parameter it = new Parameter(ClassHelper.OBJECT_TYPE, "it", ConstantExpression.NULL);
201+
Parameter it = new Parameter(ClassHelper.OBJECT_TYPE, "it", new ConstantExpression(null));
202202
parameters = new Parameter[]{it};
203203
Variable ref = expression.getVariableScope().getDeclaredVariable("it");
204204
if (ref!=null) it.setClosureSharedVariable(ref.isClosureSharedVariable());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.codehaus.groovy.classgen.asm.sc;
20+
21+
import org.codehaus.groovy.GroovyBugError;
22+
import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
23+
import org.codehaus.groovy.ast.ClassHelper;
24+
import org.codehaus.groovy.ast.ClassNode;
25+
import org.codehaus.groovy.ast.MethodNode;
26+
import org.codehaus.groovy.ast.Parameter;
27+
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
28+
import org.codehaus.groovy.ast.expr.ArrayExpression;
29+
import org.codehaus.groovy.ast.expr.ClosureExpression;
30+
import org.codehaus.groovy.ast.expr.ConstantExpression;
31+
import org.codehaus.groovy.ast.expr.Expression;
32+
import org.codehaus.groovy.ast.expr.MethodCallExpression;
33+
import org.codehaus.groovy.ast.expr.VariableExpression;
34+
import org.codehaus.groovy.ast.stmt.ReturnStatement;
35+
import org.codehaus.groovy.classgen.asm.ClosureWriter;
36+
import org.codehaus.groovy.classgen.asm.WriterController;
37+
import org.codehaus.groovy.control.SourceUnit;
38+
import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys;
39+
import org.codehaus.groovy.transform.stc.StaticTypesMarker;
40+
import groovyjarjarasm.asm.Opcodes;
41+
42+
import java.util.Collections;
43+
import java.util.List;
44+
45+
/**
46+
* Writer responsible for generating closure classes in statically compiled mode.
47+
*/
48+
public class StaticTypesClosureWriter extends ClosureWriter {
49+
public StaticTypesClosureWriter(WriterController wc) {
50+
super(wc);
51+
}
52+
53+
@Override
54+
protected ClassNode createClosureClass(final ClosureExpression expression, final int mods) {
55+
ClassNode closureClass = super.createClosureClass(expression, mods);
56+
List<MethodNode> methods = closureClass.getDeclaredMethods("call");
57+
List<MethodNode> doCall = closureClass.getMethods("doCall");
58+
if (doCall.size() != 1) {
59+
throw new GroovyBugError("Expected to find one (1) doCall method on generated closure, but found " + doCall.size());
60+
}
61+
MethodNode doCallMethod = doCall.get(0);
62+
if (methods.isEmpty() && doCallMethod.getParameters().length == 1) {
63+
createDirectCallMethod(closureClass, doCallMethod);
64+
}
65+
MethodTargetCompletionVisitor visitor = new MethodTargetCompletionVisitor(doCallMethod);
66+
Object dynamic = expression.getNodeMetaData(StaticTypesMarker.DYNAMIC_RESOLUTION);
67+
if (dynamic != null) {
68+
doCallMethod.putNodeMetaData(StaticTypesMarker.DYNAMIC_RESOLUTION, dynamic);
69+
}
70+
for (MethodNode method : methods) {
71+
visitor.visitMethod(method);
72+
}
73+
closureClass.putNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE, Boolean.TRUE);
74+
return closureClass;
75+
}
76+
77+
private static void createDirectCallMethod(final ClassNode closureClass, final MethodNode doCallMethod) {
78+
// in case there is no "call" method on the closure, we can create a "fast invocation" paths
79+
// to avoid going through ClosureMetaClass by call(Object...) method
80+
81+
// we can't have a specialized version of call(Object...) because the dispatch logic in ClosureMetaClass
82+
// is too complex!
83+
84+
// call(Object)
85+
/* GRECLIPSE edit -- GROOVY-10071
86+
Parameter args = new Parameter(ClassHelper.OBJECT_TYPE, "args");
87+
*/
88+
Parameter doCallParam = doCallMethod.getParameters()[0];
89+
Parameter args = new Parameter(doCallParam.getType(), "args");
90+
// GRECLIPSE end
91+
MethodCallExpression doCall1arg = new MethodCallExpression(
92+
new VariableExpression("this", closureClass),
93+
"doCall",
94+
new ArgumentListExpression(new VariableExpression(args))
95+
);
96+
doCall1arg.setImplicitThis(true);
97+
doCall1arg.setMethodTarget(doCallMethod);
98+
closureClass.addMethod(
99+
new MethodNode("call",
100+
Opcodes.ACC_PUBLIC,
101+
ClassHelper.OBJECT_TYPE,
102+
new Parameter[]{args},
103+
ClassNode.EMPTY_ARRAY,
104+
new ReturnStatement(doCall1arg)));
105+
106+
// call()
107+
/* GRECLIPSE edit -- GROOVY-10071, GROOVY-10072
108+
MethodCallExpression doCallNoArgs = new MethodCallExpression(new VariableExpression("this", closureClass), "doCall", new ArgumentListExpression(new ConstantExpression(null)));
109+
*/
110+
Expression argument;
111+
if (doCallParam.hasInitialExpression()) {
112+
argument = doCallParam.getInitialExpression();
113+
} else if (doCallParam.getType().isArray()) {
114+
ClassNode elementType = doCallParam.getType().getComponentType();
115+
argument = new ArrayExpression(elementType, null, Collections.singletonList(new ConstantExpression(0, true)));
116+
} else {
117+
argument = new ConstantExpression(null);
118+
}
119+
MethodCallExpression doCallNoArgs = new MethodCallExpression(new VariableExpression("this", closureClass), "doCall", new ArgumentListExpression(argument));
120+
// GRECLIPSE end
121+
doCallNoArgs.setImplicitThis(true);
122+
doCallNoArgs.setMethodTarget(doCallMethod);
123+
closureClass.addMethod(
124+
new MethodNode("call",
125+
Opcodes.ACC_PUBLIC,
126+
ClassHelper.OBJECT_TYPE,
127+
Parameter.EMPTY_ARRAY,
128+
ClassNode.EMPTY_ARRAY,
129+
new ReturnStatement(doCallNoArgs)));
130+
}
131+
132+
private static final class MethodTargetCompletionVisitor extends ClassCodeVisitorSupport {
133+
134+
private final MethodNode doCallMethod;
135+
136+
private MethodTargetCompletionVisitor(final MethodNode doCallMethod) {
137+
this.doCallMethod = doCallMethod;
138+
}
139+
140+
@Override
141+
protected SourceUnit getSourceUnit() {
142+
return null;
143+
}
144+
145+
@Override
146+
public void visitMethodCallExpression(final MethodCallExpression call) {
147+
super.visitMethodCallExpression(call);
148+
MethodNode mn = call.getMethodTarget();
149+
if (mn == null) {
150+
call.setMethodTarget(doCallMethod);
151+
}
152+
}
153+
}
154+
}

base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -443,12 +443,15 @@ static int excessArgumentsMatchesVargsParameter(Parameter[] params, ClassNode[]
443443
*/
444444
static int lastArgMatchesVarg(Parameter[] params, ClassNode... args) {
445445
if (!isVargs(params)) return -1;
446-
// case length ==0 handled already
446+
// GRECLIPSE add -- GROOVY-10071
447+
int lastParamIndex = params.length - 1;
448+
if (lastParamIndex == args.length) return 0;
449+
// GRECLIPSE end
447450
// we have now two cases,
448451
// the argument is wrapped in the vargs array or
449452
// the argument is an array that can be used for the vargs part directly
450453
// we test only the wrapping part, since the non wrapping is done already
451-
ClassNode lastParamType = params[params.length - 1].getType();
454+
ClassNode lastParamType = params[lastParamIndex].getType();
452455
ClassNode ptype = lastParamType.getComponentType();
453456
ClassNode arg = args[args.length - 1];
454457
if (isNumberType(ptype) && isNumberType(arg) && !getWrapper(ptype).equals(getWrapper(arg))) return -1;

0 commit comments

Comments
 (0)