Skip to content

Commit

Permalink
GROOVY-10071, GROOVY-10072
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-milles committed May 3, 2021
1 parent aa1935a commit ee8e9e4
Show file tree
Hide file tree
Showing 19 changed files with 1,086 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ public void testCompileStatic12() {
"1. ERROR in Main.groovy (at line 3)\n" +
"\tdef list = new LinkedList<String>([1,2,3])\n" +
"\t ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
"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" +
"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" +
"----------\n");
}

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

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

Expand Down Expand Up @@ -6166,4 +6166,44 @@ public void testCompileStatic10047() {

runConformTest(sources, "[a:1, bc:2, def:3]");
}

@Test
public void testCompileStatic10071() {
//@formatter:off
String[] sources = {
"Main.groovy",
"@groovy.transform.CompileStatic\n" +
"void test() {\n" +
" def c = { ... zeroOrMore -> 'foo' + zeroOrMore }\n" +
" assert c('bar', 'baz') == 'foo[bar, baz]'\n" +
" assert c('bar') == 'foo[bar]'\n" +
" assert c() == 'foo[]'\n" +
"}\n" +
"test()\n",
};
//@formatter:on

runConformTest(sources);
}

@Test
public void testCompileStatic10072() {
//@formatter:off
String[] sources = {
"Main.groovy",
"@groovy.transform.CompileStatic\n" +
"void test() {\n" +
" def c = { p = 'foo' -> return p }\n" +
" assert c('bar') == 'bar'\n" +
" assert c() == 'foo'\n" +
" c = { p, q = 'baz' -> '' + p + q }\n" +
" assert c('foo', 'bar') == 'foobar'\n" +
" assert c('foo') == 'foobaz'\n" +
"}\n" +
"test()\n",
};
//@formatter:on

runConformTest(sources);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ public void testTypeChecked7945() {
"1. ERROR in Test.groovy (at line 12)\n" +
"\tsuper(Integer, String)\n" +
"\t^^^^^^^^^^^^^^^^^^^^^^\n" +
"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" +
"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" +
"----------\n");
}

Expand Down Expand Up @@ -1375,17 +1375,17 @@ public void testTypeChecked9902() {
"1. ERROR in Main.groovy (at line 19)\n" +
"\th.stringProperty.eq(\"${0}\")\n" +
"\t^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
"Groovy:[Static type checking] - Cannot call TypedProperty#eq(java.lang.String) with arguments [groovy.lang.GString] \n" +
"Groovy:[Static type checking] - Cannot call TypedProperty#eq(java.lang.String) with arguments [groovy.lang.GString]\n" +
"----------\n" +
"2. ERROR in Main.groovy (at line 21)\n" +
"\tstringProperty.eq(1234)\n" +
"\t^^^^^^^^^^^^^^^^^^^^^^^\n" +
"Groovy:[Static type checking] - Cannot call TypedProperty#eq(java.lang.String) with arguments [int] \n" +
"Groovy:[Static type checking] - Cannot call TypedProperty#eq(java.lang.String) with arguments [int]\n" +
"----------\n" +
"3. ERROR in Main.groovy (at line 22)\n" +
"\tnumberProperty.eq('xx')\n" +
"\t^^^^^^^^^^^^^^^^^^^^^^^\n" +
"Groovy:[Static type checking] - Cannot call TypedProperty#eq(java.lang.Number) with arguments [java.lang.String] \n" +
"Groovy:[Static type checking] - Cannot call TypedProperty#eq(java.lang.Number) with arguments [java.lang.String]\n" +
"----------\n");
}

Expand Down
2 changes: 1 addition & 1 deletion base/org.codehaus.groovy25/.checkstyle
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
<file-match-pattern match-pattern="groovy/classgen/asm/sc/StaticInvocationWriter.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/classgen/asm/sc/StaticPropertyAccessHelper.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/classgen/asm/sc/StaticTypes(CallSite|Closure)Writer.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/classgen/asm/sc/StaticTypesTypeChooser.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/classgen/asm/sc/StaticTypesUnaryExpressionHelper.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/control/CompilationUnit.java" include-pattern="false" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ protected ClassNode createClosureClass(ClosureExpression expression, int mods) {
parameters = Parameter.EMPTY_ARRAY;
} else if (parameters.length == 0) {
// let's create a default 'it' parameter
Parameter it = new Parameter(ClassHelper.OBJECT_TYPE, "it", ConstantExpression.NULL);
Parameter it = new Parameter(ClassHelper.OBJECT_TYPE, "it", new ConstantExpression(null));
parameters = new Parameter[]{it};
Variable ref = expression.getVariableScope().getDeclaredVariable("it");
if (ref!=null) it.setClosureSharedVariable(ref.isClosureSharedVariable());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.codehaus.groovy.classgen.asm.sc;

import org.codehaus.groovy.GroovyBugError;
import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.ast.expr.ArrayExpression;
import org.codehaus.groovy.ast.expr.ClosureExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.ast.stmt.ReturnStatement;
import org.codehaus.groovy.classgen.asm.ClosureWriter;
import org.codehaus.groovy.classgen.asm.WriterController;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys;
import org.codehaus.groovy.transform.stc.StaticTypesMarker;
import groovyjarjarasm.asm.Opcodes;

import java.util.Collections;
import java.util.List;

/**
* Writer responsible for generating closure classes in statically compiled mode.
*/
public class StaticTypesClosureWriter extends ClosureWriter {
public StaticTypesClosureWriter(WriterController wc) {
super(wc);
}

@Override
protected ClassNode createClosureClass(final ClosureExpression expression, final int mods) {
ClassNode closureClass = super.createClosureClass(expression, mods);
List<MethodNode> methods = closureClass.getDeclaredMethods("call");
List<MethodNode> doCall = closureClass.getMethods("doCall");
if (doCall.size() != 1) {
throw new GroovyBugError("Expected to find one (1) doCall method on generated closure, but found " + doCall.size());
}
MethodNode doCallMethod = doCall.get(0);
if (methods.isEmpty() && doCallMethod.getParameters().length == 1) {
createDirectCallMethod(closureClass, doCallMethod);
}
MethodTargetCompletionVisitor visitor = new MethodTargetCompletionVisitor(doCallMethod);
Object dynamic = expression.getNodeMetaData(StaticTypesMarker.DYNAMIC_RESOLUTION);
if (dynamic != null) {
doCallMethod.putNodeMetaData(StaticTypesMarker.DYNAMIC_RESOLUTION, dynamic);
}
for (MethodNode method : methods) {
visitor.visitMethod(method);
}
closureClass.putNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE, Boolean.TRUE);
return closureClass;
}

private static void createDirectCallMethod(final ClassNode closureClass, final MethodNode doCallMethod) {
// in case there is no "call" method on the closure, we can create a "fast invocation" paths
// to avoid going through ClosureMetaClass by call(Object...) method

// we can't have a specialized version of call(Object...) because the dispatch logic in ClosureMetaClass
// is too complex!

// call(Object)
/* GRECLIPSE edit -- GROOVY-10071
Parameter args = new Parameter(ClassHelper.OBJECT_TYPE, "args");
*/
Parameter doCallParam = doCallMethod.getParameters()[0];
Parameter args = new Parameter(doCallParam.getType(), "args");
// GRECLIPSE end
MethodCallExpression doCall1arg = new MethodCallExpression(
new VariableExpression("this", closureClass),
"doCall",
new ArgumentListExpression(new VariableExpression(args))
);
doCall1arg.setImplicitThis(true);
doCall1arg.setMethodTarget(doCallMethod);
closureClass.addMethod(
new MethodNode("call",
Opcodes.ACC_PUBLIC,
ClassHelper.OBJECT_TYPE,
new Parameter[]{args},
ClassNode.EMPTY_ARRAY,
new ReturnStatement(doCall1arg)));

// call()
/* GRECLIPSE edit -- GROOVY-10071, GROOVY-10072
MethodCallExpression doCallNoArgs = new MethodCallExpression(new VariableExpression("this", closureClass), "doCall", new ArgumentListExpression(new ConstantExpression(null)));
*/
Expression argument;
if (doCallParam.hasInitialExpression()) {
argument = doCallParam.getInitialExpression();
} else if (doCallParam.getType().isArray()) {
ClassNode elementType = doCallParam.getType().getComponentType();
argument = new ArrayExpression(elementType, null, Collections.singletonList(new ConstantExpression(0, true)));
} else {
argument = new ConstantExpression(null);
}
MethodCallExpression doCallNoArgs = new MethodCallExpression(new VariableExpression("this", closureClass), "doCall", new ArgumentListExpression(argument));
// GRECLIPSE end
doCallNoArgs.setImplicitThis(true);
doCallNoArgs.setMethodTarget(doCallMethod);
closureClass.addMethod(
new MethodNode("call",
Opcodes.ACC_PUBLIC,
ClassHelper.OBJECT_TYPE,
Parameter.EMPTY_ARRAY,
ClassNode.EMPTY_ARRAY,
new ReturnStatement(doCallNoArgs)));
}

private static final class MethodTargetCompletionVisitor extends ClassCodeVisitorSupport {

private final MethodNode doCallMethod;

private MethodTargetCompletionVisitor(final MethodNode doCallMethod) {
this.doCallMethod = doCallMethod;
}

@Override
protected SourceUnit getSourceUnit() {
return null;
}

@Override
public void visitMethodCallExpression(final MethodCallExpression call) {
super.visitMethodCallExpression(call);
MethodNode mn = call.getMethodTarget();
if (mn == null) {
call.setMethodTarget(doCallMethod);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -443,12 +443,15 @@ static int excessArgumentsMatchesVargsParameter(Parameter[] params, ClassNode[]
*/
static int lastArgMatchesVarg(Parameter[] params, ClassNode... args) {
if (!isVargs(params)) return -1;
// case length ==0 handled already
// GRECLIPSE add -- GROOVY-10071
int lastParamIndex = params.length - 1;
if (lastParamIndex == args.length) return 0;
// GRECLIPSE end
// we have now two cases,
// the argument is wrapped in the vargs array or
// the argument is an array that can be used for the vargs part directly
// we test only the wrapping part, since the non wrapping is done already
ClassNode lastParamType = params[params.length - 1].getType();
ClassNode lastParamType = params[lastParamIndex].getType();
ClassNode ptype = lastParamType.getComponentType();
ClassNode arg = args[args.length - 1];
if (isNumberType(ptype) && isNumberType(arg) && !getWrapper(ptype).equals(getWrapper(arg))) return -1;
Expand Down
Loading

0 comments on commit ee8e9e4

Please sign in to comment.