From 3e8474d89042123672c8daff7f5b2691f2046123 Mon Sep 17 00:00:00 2001 From: Eric Milles Date: Fri, 29 Jan 2021 15:58:17 -0600 Subject: [PATCH] GROOVY-9918 --- .../tests/xform/StaticCompilationTests.java | 18 +++++ .../asm/sc/StaticInvocationWriter.java | 14 ++-- .../asm/sc/StaticInvocationWriter.java | 14 ++-- .../asm/sc/StaticInvocationWriter.java | 79 ++++++++++++++++++- 4 files changed, 114 insertions(+), 11 deletions(-) diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/StaticCompilationTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/StaticCompilationTests.java index f220ede8f7..c6d94206ef 100644 --- a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/StaticCompilationTests.java +++ b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/StaticCompilationTests.java @@ -5756,4 +5756,22 @@ public void testCompileStatic9893a() { runConformTest(sources, "String"); } + + @Test + public void testCompileStatic9918() { + //@formatter:off + String[] sources = { + "Main.groovy", + "def m(one, ... zeroOrMore) { }\n" + + "@groovy.transform.CompileStatic\n" + + "void test() {\n" + + " Object[] array = ['a', 'b']\n" + + " m(array)\n" + // ouch! + "}\n" + + "test()\n", + }; + //@formatter:on + + runConformTest(sources, ""); + } } diff --git a/base/org.codehaus.groovy25/src/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java b/base/org.codehaus.groovy25/src/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java index dd53220867..e6d66566f7 100644 --- a/base/org.codehaus.groovy25/src/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java +++ b/base/org.codehaus.groovy25/src/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java @@ -448,12 +448,16 @@ protected void loadArguments(List argumentList, Parameter[] para) { ClassNode lastArgType = argumentListSize > 0 ? typeChooser.resolveType(argumentList.get(argumentListSize -1), controller.getClassNode()):null; if (lastParaType.isArray() - && ((argumentListSize > para.length) + && (argumentListSize > para.length + /* GRECLIPSE edit -- GROOVY-9918 || ((argumentListSize == (para.length - 1)) && !lastParaType.equals(lastArgType)) - || ((argumentListSize == para.length && lastArgType!=null && !lastArgType.isArray()) - && (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(lastArgType,lastParaType.getComponentType()))) - || ClassHelper.GSTRING_TYPE.equals(lastArgType) && ClassHelper.STRING_TYPE.equals(lastParaType.getComponentType())) - ) { + */ + || argumentListSize == para.length - 1 + // GRECLIPSE end + || (argumentListSize == para.length && lastArgType!=null && !lastArgType.isArray() + && (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(lastArgType,lastParaType.getComponentType()) + || ClassHelper.GSTRING_TYPE.equals(lastArgType) && ClassHelper.STRING_TYPE.equals(lastParaType.getComponentType()))) + )) { int stackLen = operandStack.getStackLength() + argumentListSize; MethodVisitor mv = controller.getMethodVisitor(); controller.setMethodVisitor(mv); diff --git a/base/org.codehaus.groovy30/src/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java b/base/org.codehaus.groovy30/src/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java index a29a8f3e05..75f74dfcec 100644 --- a/base/org.codehaus.groovy30/src/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java +++ b/base/org.codehaus.groovy30/src/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java @@ -432,12 +432,16 @@ protected void loadArguments(final List argumentList, final Paramete ClassNode lastArgType = argumentListSize > 0 ? typeChooser.resolveType(argumentList.get(argumentListSize -1), controller.getClassNode()) : null; if (lastParaType.isArray() - && ((argumentListSize > para.length) + && (argumentListSize > para.length + /* GRECLIPSE edit -- GROOVY-9918 || ((argumentListSize == (para.length - 1)) && !lastParaType.equals(lastArgType)) - || ((argumentListSize == para.length && lastArgType!=null && !lastArgType.isArray()) - && (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(lastArgType,lastParaType.getComponentType()))) - || ClassHelper.GSTRING_TYPE.equals(lastArgType) && ClassHelper.STRING_TYPE.equals(lastParaType.getComponentType())) - ) { + */ + || argumentListSize == para.length - 1 + // GRECLIPSE end + || (argumentListSize == para.length && lastArgType!=null && !lastArgType.isArray() + && (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(lastArgType,lastParaType.getComponentType()) + || ClassHelper.GSTRING_TYPE.equals(lastArgType) && ClassHelper.STRING_TYPE.equals(lastParaType.getComponentType()))) + )) { int stackLen = operandStack.getStackLength() + argumentListSize; MethodVisitor mv = controller.getMethodVisitor(); controller.setMethodVisitor(mv); diff --git a/base/org.codehaus.groovy40/src/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java b/base/org.codehaus.groovy40/src/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java index a29a8f3e05..4e7fd6c89c 100644 --- a/base/org.codehaus.groovy40/src/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java +++ b/base/org.codehaus.groovy40/src/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java @@ -65,9 +65,11 @@ import groovyjarjarasm.asm.MethodVisitor; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import static java.util.stream.Collectors.joining; import static org.apache.groovy.ast.tools.ClassNodeUtils.samePackageName; import static org.apache.groovy.ast.tools.ExpressionUtils.isNullConstant; @@ -422,7 +424,8 @@ protected static boolean isPrivateBridgeMethodsCallAllowed(final ClassNode recei } @Override - protected void loadArguments(final List argumentList, final Parameter[] para) { + protected void loadArguments(final List argumentList, final Parameter[] parameters) { + /* GRECLIPSE edit -- GROOVY-9918 if (para.length == 0) return; ClassNode lastParaType = para[para.length - 1].getOriginType(); AsmClassGenerator acg = controller.getAcg(); @@ -492,6 +495,80 @@ protected void loadArguments(final List argumentList, final Paramete visitArgument(arguments[i], para[i].getType()); } } + */ + final int nArgs = argumentList.size(), nPrms = parameters.length; if (nPrms == 0) return; + + ClassNode classNode = controller.getClassNode(); + TypeChooser typeChooser = controller.getTypeChooser(); + ClassNode lastArgType = nArgs == 0 ? null : typeChooser.resolveType(argumentList.get(nArgs - 1), classNode); + ClassNode lastPrmType = parameters[nPrms - 1].getOriginType(); + + // target is variadic and args are too many or one short or just enough with array compatibility + if (lastPrmType.isArray() && (nArgs > nPrms || nArgs == nPrms - 1 + || (nArgs == nPrms && !lastArgType.isArray() + && (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(lastArgType, lastPrmType.getComponentType()) + || ClassHelper.GSTRING_TYPE.equals(lastArgType) && ClassHelper.STRING_TYPE.equals(lastPrmType.getComponentType()))) + )) { + OperandStack operandStack = controller.getOperandStack(); + int stackLength = operandStack.getStackLength() + nArgs; + // first arguments/parameters as usual + for (int i = 0; i < nPrms - 1; i += 1) { + visitArgument(argumentList.get(i), parameters[i].getType()); + } + // wrap remaining arguments in an array for last parameter + List lastArgs = new ArrayList<>(); + for (int i = nPrms - 1; i < nArgs; i += 1) { + lastArgs.add(argumentList.get(i)); + } + ArrayExpression array = new ArrayExpression(lastPrmType.getComponentType(), lastArgs); + array.visit(controller.getAcg()); + // adjust stack length + while (operandStack.getStackLength() < stackLength) { + operandStack.push(ClassHelper.OBJECT_TYPE); + } + if (nArgs == nPrms - 1) { + operandStack.remove(1); + } + } else if (nArgs == nPrms) { + for (int i = 0; i < nArgs; i += 1) { + visitArgument(argumentList.get(i), parameters[i].getType()); + } + } else { // call with default arguments + Expression[] arguments = new Expression[nPrms]; + for (int i = 0, j = 0; i < nPrms; i += 1) { + Parameter p = parameters[i]; + ClassNode pType = p.getType(); + Expression a = (j < nArgs ? argumentList.get(j) : null); + ClassNode aType = (a == null ? null : typeChooser.resolveType(a, classNode)); + + Expression expression = getInitialExpression(p); // default argument + if (expression != null && !compatibleArgumentType(aType, pType)) { + arguments[i] = expression; + } else if (a != null) { + arguments[i] = a; + j += 1; + } else { + controller.getSourceUnit().addFatalError("Binding failed" + + " for arguments [" + argumentList.stream().map(arg -> typeChooser.resolveType(arg, classNode).toString(false)).collect(joining(", ")) + "]" + + " and parameters [" + Arrays.stream(parameters).map(prm -> prm.getType().toString(false)).collect(joining(", ")) + "]", getCurrentCall()); + } + } + for (int i = 0; i < nArgs; i += 1) { + visitArgument(arguments[i], parameters[i].getType()); + } + } + // GRECLIPSE end + } + + private static Expression getInitialExpression(final Parameter parameter) { + Expression initialExpression = parameter.getNodeMetaData(StaticTypesMarker.INITIAL_EXPRESSION); + if (initialExpression == null && parameter.hasInitialExpression()) { + initialExpression = parameter.getInitialExpression(); + } + if (initialExpression == null && parameter.getNodeMetaData(Verifier.INITIAL_EXPRESSION) != null) { + initialExpression = parameter.getNodeMetaData(Verifier.INITIAL_EXPRESSION); + } + return initialExpression; } private void visitArgument(final Expression argumentExpr, final ClassNode parameterType) {