From 0fcd98359200e87abb7a8dabdf4811024e87b266 Mon Sep 17 00:00:00 2001 From: Eric Milles Date: Mon, 6 Dec 2021 11:29:08 -0600 Subject: [PATCH] GROOVY-1736, GROOVY-6097, GROOVY-7300, GROOVY-7924 --- .../core/tests/basic/GroovySimpleTests.java | 70 +++++++ base/org.codehaus.groovy25/.checkstyle | 1 + .../asm/sc/StaticInvocationWriter.java | 11 +- .../StaticCompilationTransformer.java | 196 ++++++++++++++++++ base/org.codehaus.groovy30/.checkstyle | 1 + .../asm/sc/StaticInvocationWriter.java | 11 +- .../StaticCompilationTransformer.java | 188 +++++++++++++++++ base/org.codehaus.groovy40/.checkstyle | 1 + .../asm/sc/StaticInvocationWriter.java | 12 +- .../StaticCompilationTransformer.java | 187 +++++++++++++++++ 10 files changed, 670 insertions(+), 8 deletions(-) create mode 100644 base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/sc/transformers/StaticCompilationTransformer.java create mode 100644 base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/sc/transformers/StaticCompilationTransformer.java create mode 100644 base/org.codehaus.groovy40/src/org/codehaus/groovy/transform/sc/transformers/StaticCompilationTransformer.java diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GroovySimpleTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GroovySimpleTests.java index 410c789c83..5e1353a8c0 100644 --- a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GroovySimpleTests.java +++ b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GroovySimpleTests.java @@ -5226,6 +5226,31 @@ public void testGroovyBug2() { runConformTest(sources, "a"); } + @Test // https://issues.apache.org/jira/browse/GROOVY-1736 + public void testGroovy1736() { + for (String vis : new String[] {"", "public", "protected", "@groovy.transform.PackageScope"}) { + //@formatter:off + String[] sources = { + "Script.groovy", + "abstract class A {\n" + + vis + " def getX() { 'A' }\n" + + "}\n" + + "class C extends A {\n" + + " def getX() { super.x + 'C' }\n" + // no stack overflow + " void m() {\n" + + " print x\n" + + " print this.x\n" + + " print super.x\n" + // TODO: test safe and spread + " }\n" + + "}\n" + + "new C().m()\n", + }; + //@formatter:on + + runConformTest(sources, "ACACA"); + } + } + @Test // https://issues.apache.org/jira/browse/GROOVY-3311 public void testGroovy3311() { //@formatter:off @@ -5274,6 +5299,51 @@ public void testGroovy6045() { runConformTest(sources, ""); } + @Test // https://issues.apache.org/jira/browse/GROOVY-6097 + public void testGroovy6097() { + for (String vis : new String[] {"", "public", "protected", "@groovy.transform.PackageScope"}) { + //@formatter:off + String[] sources = { + "Script.groovy", + "abstract class A {\n" + + vis + " boolean isX() { true }\n" + + vis + " boolean getX() { false }\n" + + "}\n" + + "class C extends A {\n" + + " void m() {\n" + + " print x\n" + + " print this.x\n" + + " print super.x\n" + // hardwired to "super.getX()" + " }\n" + + "}\n" + + "new C().m()\n", + }; + //@formatter:on + + runConformTest(sources, "truetruefalse"); + } + } + + @Test // https://issues.apache.org/jira/browse/GROOVY-7924 + public void testGroovy7924() { + //@formatter:off + String[] sources = { + "Script.groovy", + "abstract class A {\n" + + " def getFoo() { 'works' }\n" + + "}\n" + + "class C extends A {\n" + + " void m(String name) {\n" + + " print super.\"$name\"\n" + + " }\n" + + "}\n" + + "new C().m('foo')\n", + }; + //@formatter:on + + runConformTest(sources, "works"); + } + @Test // https://issues.apache.org/jira/browse/GROOVY-8311 public void testGroovy8311() { //@formatter:off diff --git a/base/org.codehaus.groovy25/.checkstyle b/base/org.codehaus.groovy25/.checkstyle index d8f8456433..43d5d421eb 100644 --- a/base/org.codehaus.groovy25/.checkstyle +++ b/base/org.codehaus.groovy25/.checkstyle @@ -83,6 +83,7 @@ + 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 f615c407da..a141c14b1b 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 @@ -119,13 +119,22 @@ protected boolean makeDirectCall(final Expression origin, final Expression recei if (origin instanceof MethodCallExpression && receiver instanceof VariableExpression && ((VariableExpression) receiver).isSuperExpression()) { + /* GRECLIPSE edit -- GROOVY-6097, GROOVY-7300, et al. ClassNode superClass = receiver.getNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER); if (superClass!=null && !controller.getCompileStack().isLHS()) { - // GROOVY-7300 MethodCallExpression mce = (MethodCallExpression) origin; MethodNode node = superClass.getDeclaredMethod(mce.getMethodAsString(), Parameter.EMPTY_ARRAY); mce.setMethodTarget(node); } + */ + MethodCallExpression mce = (MethodCallExpression) origin; + if (mce.getMethodTarget() == null && !controller.getCompileStack().isLHS()) { + ClassNode owner = receiver.getNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER); + if (owner != null) { + mce.setMethodTarget(owner.getDeclaredMethod(mce.getMethodAsString(), Parameter.EMPTY_ARRAY)); + } + } + // GRECLIPSE end } return super.makeDirectCall(origin, receiver, message, arguments, adapter, implicitThis, containsSpreadExpression); } diff --git a/base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/sc/transformers/StaticCompilationTransformer.java b/base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/sc/transformers/StaticCompilationTransformer.java new file mode 100644 index 0000000000..e0a36bed75 --- /dev/null +++ b/base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/sc/transformers/StaticCompilationTransformer.java @@ -0,0 +1,196 @@ +/* + * 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.transform.sc.transformers; + +import org.codehaus.groovy.ast.ClassCodeExpressionTransformer; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.InnerClassNode; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.expr.BinaryExpression; +import org.codehaus.groovy.ast.expr.BooleanExpression; +import org.codehaus.groovy.ast.expr.CastExpression; +import org.codehaus.groovy.ast.expr.ClosureExpression; +import org.codehaus.groovy.ast.expr.ConstructorCallExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.ListExpression; +import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.ast.expr.PropertyExpression; +import org.codehaus.groovy.ast.expr.RangeExpression; +import org.codehaus.groovy.ast.expr.StaticMethodCallExpression; +import org.codehaus.groovy.ast.expr.VariableExpression; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.classgen.asm.sc.StaticTypesTypeChooser; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.runtime.ScriptBytecodeAdapter; +import org.codehaus.groovy.syntax.Types; +import org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * Some expressions use symbols as aliases to method calls (<<, +=, ...). In static compilation, + * if such a method call is found, we transform the original binary expression into a method + * call expression so that the call gets statically compiled. + */ +public class StaticCompilationTransformer extends ClassCodeExpressionTransformer { + + protected static final ClassNode BYTECODE_ADAPTER_CLASS = ClassHelper.make(ScriptBytecodeAdapter.class); + protected static final Map BYTECODE_BINARY_ADAPTERS = Collections.unmodifiableMap(new HashMap() { + private static final long serialVersionUID = -9117028399464862605L; + + { + put(Types.COMPARE_EQUAL, BYTECODE_ADAPTER_CLASS.getMethods("compareEqual").get(0)); + put(Types.COMPARE_GREATER_THAN, BYTECODE_ADAPTER_CLASS.getMethods("compareGreaterThan").get(0)); + put(Types.COMPARE_GREATER_THAN_EQUAL, BYTECODE_ADAPTER_CLASS.getMethods("compareGreaterThanEqual").get(0)); + put(Types.COMPARE_LESS_THAN, BYTECODE_ADAPTER_CLASS.getMethods("compareLessThan").get(0)); + put(Types.COMPARE_LESS_THAN_EQUAL, BYTECODE_ADAPTER_CLASS.getMethods("compareLessThanEqual").get(0)); + put(Types.COMPARE_NOT_EQUAL, BYTECODE_ADAPTER_CLASS.getMethods("compareNotEqual").get(0)); + put(Types.COMPARE_TO, BYTECODE_ADAPTER_CLASS.getMethods("compareTo").get(0)); + + }}); + + + private ClassNode classNode; + private final SourceUnit unit; + + private final StaticTypesTypeChooser typeChooser = new StaticTypesTypeChooser(); + private final StaticTypeCheckingVisitor staticCompilationVisitor; + + // various helpers in order to avoid a potential very big class + private final StaticMethodCallExpressionTransformer staticMethodCallExpressionTransformer = new StaticMethodCallExpressionTransformer(this); + private final ConstructorCallTransformer constructorCallTransformer = new ConstructorCallTransformer(this); + private final MethodCallExpressionTransformer methodCallExpressionTransformer = new MethodCallExpressionTransformer(this); + private final BinaryExpressionTransformer binaryExpressionTransformer = new BinaryExpressionTransformer(this); + private final ClosureExpressionTransformer closureExpressionTransformer = new ClosureExpressionTransformer(this); + private final BooleanExpressionTransformer booleanExpressionTransformer = new BooleanExpressionTransformer(this); + private final VariableExpressionTransformer variableExpressionTransformer = new VariableExpressionTransformer(); + private final RangeExpressionTransformer rangeExpressionTransformer = new RangeExpressionTransformer(this); + private final ListExpressionTransformer listExpressionTransformer = new ListExpressionTransformer(this); + private final CastExpressionOptimizer castExpressionTransformer = new CastExpressionOptimizer(this); + + public StaticCompilationTransformer(final SourceUnit unit, final StaticTypeCheckingVisitor visitor) { + this.unit = unit; + this.staticCompilationVisitor = visitor; + } + + @Override + protected SourceUnit getSourceUnit() { + return unit; + } + + public StaticTypesTypeChooser getTypeChooser() { + return typeChooser; + } + + public ClassNode getClassNode() { + return classNode; + } + + @Override + public void visitClassCodeContainer(final Statement code) { + super.visitClassCodeContainer(code); + } + + @Override + public Expression transform(Expression expr) { + // GRECLIPSE add -- GROOVY-6097, GROOVY-7300, et al. + if (expr instanceof PropertyExpression) { + MethodNode dmct = expr.getNodeMetaData(org.codehaus.groovy.transform.stc.StaticTypesMarker.DIRECT_METHOD_CALL_TARGET); + // NOTE: BinaryExpressionTransformer handles the setter + if (dmct != null && dmct.getParameters().length == 0) { + PropertyExpression pe = (PropertyExpression) expr; + + MethodCallExpression mce = new MethodCallExpression(transform(pe.getObjectExpression()), dmct.getName(), MethodCallExpression.NO_ARGUMENTS); + mce.setImplicitThis(pe.isImplicitThis()); + mce.setSpreadSafe(pe.isSpreadSafe()); + mce.setMethodTarget(dmct); + mce.setSourcePosition(pe); + mce.copyNodeMetaData(pe); + mce.setSafe(pe.isSafe()); + return mce; + } + return super.transform(expr); + } + // GRECLIPSE end + if (expr instanceof StaticMethodCallExpression) { + return staticMethodCallExpressionTransformer.transformStaticMethodCallExpression((StaticMethodCallExpression) expr); + } + if (expr instanceof BinaryExpression) { + return binaryExpressionTransformer.transformBinaryExpression((BinaryExpression)expr); + } + if (expr instanceof MethodCallExpression) { + return methodCallExpressionTransformer.transformMethodCallExpression((MethodCallExpression) expr); + } + if (expr instanceof ClosureExpression) { + return closureExpressionTransformer.transformClosureExpression((ClosureExpression) expr); + } + if (expr instanceof ConstructorCallExpression) { + return constructorCallTransformer.transformConstructorCall((ConstructorCallExpression) expr); + } + if (expr instanceof BooleanExpression) { + return booleanExpressionTransformer.transformBooleanExpression((BooleanExpression)expr); + } + if (expr instanceof VariableExpression) { + return variableExpressionTransformer.transformVariableExpression((VariableExpression)expr); + } + if (expr instanceof RangeExpression) { + return rangeExpressionTransformer.transformRangeExpression(((RangeExpression)expr)); + } + if (expr instanceof ListExpression) { + return listExpressionTransformer.transformListExpression((ListExpression) expr); + } + if (expr instanceof CastExpression) { + return castExpressionTransformer.transformCastExpression(((CastExpression)expr)); + } + return super.transform(expr); + } + + /** + * Called by helpers when super.transform() is needed. + */ + final Expression superTransform(Expression expr) { + return super.transform(expr); + } + + @Override + public void visitClass(final ClassNode node) { + ClassNode prec = classNode; + classNode = node; + super.visitClass(node); + Iterator innerClasses = classNode.getInnerClasses(); + while (innerClasses.hasNext()) { + InnerClassNode innerClassNode = innerClasses.next(); + visitClass(innerClassNode); + } + classNode = prec; + } + + @Override + protected void visitConstructorOrMethod(final MethodNode node, final boolean isConstructor) { + if (staticCompilationVisitor.isSkipMode(node)) { + // method has already been visited by a static type checking visitor + return; + } + super.visitConstructorOrMethod(node, isConstructor); + } +} diff --git a/base/org.codehaus.groovy30/.checkstyle b/base/org.codehaus.groovy30/.checkstyle index 2837f3b05c..87a4624842 100644 --- a/base/org.codehaus.groovy30/.checkstyle +++ b/base/org.codehaus.groovy30/.checkstyle @@ -70,6 +70,7 @@ + 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 e014ad2421..b7268be839 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 @@ -122,13 +122,22 @@ public StaticInvocationWriter(final WriterController wc) { @Override protected boolean makeDirectCall(final Expression origin, final Expression receiver, final Expression message, final Expression arguments, final MethodCallerMultiAdapter adapter, final boolean implicitThis, final boolean containsSpreadExpression) { if (origin instanceof MethodCallExpression && isSuperExpression(receiver)) { + /* GRECLIPSE edit -- GROOVY-6097, GROOVY-7300, et al. ClassNode superClass = receiver.getNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER); if (superClass != null && !controller.getCompileStack().isLHS()) { - // GROOVY-7300 MethodCallExpression mce = (MethodCallExpression) origin; MethodNode node = superClass.getDeclaredMethod(mce.getMethodAsString(), Parameter.EMPTY_ARRAY); mce.setMethodTarget(node); } + */ + MethodCallExpression mce = (MethodCallExpression) origin; + if (mce.getMethodTarget() == null && !controller.getCompileStack().isLHS()) { + ClassNode owner = receiver.getNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER); + if (owner != null) { + mce.setMethodTarget(owner.getDeclaredMethod(mce.getMethodAsString(), Parameter.EMPTY_ARRAY)); + } + } + // GRECLIPSE end } return super.makeDirectCall(origin, receiver, message, arguments, adapter, implicitThis, containsSpreadExpression); } diff --git a/base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/sc/transformers/StaticCompilationTransformer.java b/base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/sc/transformers/StaticCompilationTransformer.java new file mode 100644 index 0000000000..ffc003c200 --- /dev/null +++ b/base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/sc/transformers/StaticCompilationTransformer.java @@ -0,0 +1,188 @@ +/* + * 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.transform.sc.transformers; + +import org.apache.groovy.util.Maps; +import org.codehaus.groovy.ast.ClassCodeExpressionTransformer; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.InnerClassNode; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.expr.BinaryExpression; +import org.codehaus.groovy.ast.expr.BooleanExpression; +import org.codehaus.groovy.ast.expr.CastExpression; +import org.codehaus.groovy.ast.expr.ClosureExpression; +import org.codehaus.groovy.ast.expr.ConstructorCallExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.ListExpression; +import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.ast.expr.PropertyExpression; +import org.codehaus.groovy.ast.expr.RangeExpression; +import org.codehaus.groovy.ast.expr.StaticMethodCallExpression; +import org.codehaus.groovy.ast.expr.VariableExpression; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.classgen.asm.sc.StaticTypesTypeChooser; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.runtime.ScriptBytecodeAdapter; +import org.codehaus.groovy.syntax.Types; +import org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor; + +import java.util.Iterator; +import java.util.Map; + +/** + * Some expressions use symbols as aliases to method calls (<<, +=, ...). In static compilation, + * if such a method call is found, we transform the original binary expression into a method + * call expression so that the call gets statically compiled. + */ +public class StaticCompilationTransformer extends ClassCodeExpressionTransformer { + + protected static final ClassNode BYTECODE_ADAPTER_CLASS = ClassHelper.make(ScriptBytecodeAdapter.class); + protected static final Map BYTECODE_BINARY_ADAPTERS = Maps.of( + Types.COMPARE_EQUAL, BYTECODE_ADAPTER_CLASS.getMethods("compareEqual").get(0), + Types.COMPARE_GREATER_THAN, BYTECODE_ADAPTER_CLASS.getMethods("compareGreaterThan").get(0), + Types.COMPARE_GREATER_THAN_EQUAL, BYTECODE_ADAPTER_CLASS.getMethods("compareGreaterThanEqual").get(0), + Types.COMPARE_LESS_THAN, BYTECODE_ADAPTER_CLASS.getMethods("compareLessThan").get(0), + Types.COMPARE_LESS_THAN_EQUAL, BYTECODE_ADAPTER_CLASS.getMethods("compareLessThanEqual").get(0), + Types.COMPARE_NOT_EQUAL, BYTECODE_ADAPTER_CLASS.getMethods("compareNotEqual").get(0), + Types.COMPARE_TO, BYTECODE_ADAPTER_CLASS.getMethods("compareTo").get(0) + ); + + private ClassNode classNode; + private final SourceUnit unit; + + private final StaticTypesTypeChooser typeChooser = new StaticTypesTypeChooser(); + private final StaticTypeCheckingVisitor staticCompilationVisitor; + + // various helpers in order to avoid a potential very big class + private final StaticMethodCallExpressionTransformer staticMethodCallExpressionTransformer = new StaticMethodCallExpressionTransformer(this); + private final ConstructorCallTransformer constructorCallTransformer = new ConstructorCallTransformer(this); + private final MethodCallExpressionTransformer methodCallExpressionTransformer = new MethodCallExpressionTransformer(this); + private final BinaryExpressionTransformer binaryExpressionTransformer = new BinaryExpressionTransformer(this); + private final ClosureExpressionTransformer closureExpressionTransformer = new ClosureExpressionTransformer(this); + private final BooleanExpressionTransformer booleanExpressionTransformer = new BooleanExpressionTransformer(this); + private final VariableExpressionTransformer variableExpressionTransformer = new VariableExpressionTransformer(); + private final RangeExpressionTransformer rangeExpressionTransformer = new RangeExpressionTransformer(this); + private final ListExpressionTransformer listExpressionTransformer = new ListExpressionTransformer(this); + private final CastExpressionOptimizer castExpressionTransformer = new CastExpressionOptimizer(this); + + public StaticCompilationTransformer(final SourceUnit unit, final StaticTypeCheckingVisitor visitor) { + this.unit = unit; + this.staticCompilationVisitor = visitor; + } + + @Override + protected SourceUnit getSourceUnit() { + return unit; + } + + public StaticTypesTypeChooser getTypeChooser() { + return typeChooser; + } + + public ClassNode getClassNode() { + return classNode; + } + + @Override + public void visitClassCodeContainer(final Statement code) { + super.visitClassCodeContainer(code); + } + + @Override + public Expression transform(Expression expr) { + // GRECLIPSE add -- GROOVY-6097, GROOVY-7300, et al. + if (expr instanceof PropertyExpression) { + MethodNode dmct = expr.getNodeMetaData(org.codehaus.groovy.transform.stc.StaticTypesMarker.DIRECT_METHOD_CALL_TARGET); + // NOTE: BinaryExpressionTransformer handles the setter + if (dmct != null && dmct.getParameters().length == 0) { + PropertyExpression pe = (PropertyExpression) expr; + + MethodCallExpression mce = new MethodCallExpression(transform(pe.getObjectExpression()), dmct.getName(), MethodCallExpression.NO_ARGUMENTS); + mce.setImplicitThis(pe.isImplicitThis()); + mce.setSpreadSafe(pe.isSpreadSafe()); + mce.setMethodTarget(dmct); + mce.setSourcePosition(pe); + mce.copyNodeMetaData(pe); + mce.setSafe(pe.isSafe()); + return mce; + } + return super.transform(expr); + } + // GRECLIPSE end + if (expr instanceof StaticMethodCallExpression) { + return staticMethodCallExpressionTransformer.transformStaticMethodCallExpression((StaticMethodCallExpression) expr); + } + if (expr instanceof BinaryExpression) { + return binaryExpressionTransformer.transformBinaryExpression((BinaryExpression)expr); + } + if (expr instanceof MethodCallExpression) { + return methodCallExpressionTransformer.transformMethodCallExpression((MethodCallExpression) expr); + } + if (expr instanceof ClosureExpression) { + return closureExpressionTransformer.transformClosureExpression((ClosureExpression) expr); + } + if (expr instanceof ConstructorCallExpression) { + return constructorCallTransformer.transformConstructorCall((ConstructorCallExpression) expr); + } + if (expr instanceof BooleanExpression) { + return booleanExpressionTransformer.transformBooleanExpression((BooleanExpression)expr); + } + if (expr instanceof VariableExpression) { + return variableExpressionTransformer.transformVariableExpression((VariableExpression)expr); + } + if (expr instanceof RangeExpression) { + return rangeExpressionTransformer.transformRangeExpression(((RangeExpression)expr)); + } + if (expr instanceof ListExpression) { + return listExpressionTransformer.transformListExpression((ListExpression) expr); + } + if (expr instanceof CastExpression) { + return castExpressionTransformer.transformCastExpression(((CastExpression)expr)); + } + return super.transform(expr); + } + + /** + * Called by helpers when super.transform() is needed. + */ + final Expression superTransform(Expression expr) { + return super.transform(expr); + } + + @Override + public void visitClass(final ClassNode node) { + ClassNode prev = classNode; + classNode = node; + super.visitClass(node); + for (Iterator innerClasses = classNode.getInnerClasses(); innerClasses.hasNext();) { + visitClass(innerClasses.next()); + } + classNode = prev; + } + + @Override + protected void visitConstructorOrMethod(final MethodNode node, final boolean isConstructor) { + if (staticCompilationVisitor.isSkipMode(node)) { + // method has already been visited by a static type checking visitor + return; + } + super.visitConstructorOrMethod(node, isConstructor); + } +} diff --git a/base/org.codehaus.groovy40/.checkstyle b/base/org.codehaus.groovy40/.checkstyle index b10536310c..b31d749dc4 100644 --- a/base/org.codehaus.groovy40/.checkstyle +++ b/base/org.codehaus.groovy40/.checkstyle @@ -58,6 +58,7 @@ + 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 11ce067e35..89411621a6 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 @@ -127,12 +127,12 @@ public StaticInvocationWriter(final WriterController wc) { @Override protected boolean makeDirectCall(final Expression origin, final Expression receiver, final Expression message, final Expression arguments, final MethodCallerMultiAdapter adapter, final boolean implicitThis, final boolean containsSpreadExpression) { if (origin instanceof MethodCallExpression && isSuperExpression(receiver)) { - ClassNode superClass = receiver.getNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER); - if (superClass != null && !controller.getCompileStack().isLHS()) { - // GROOVY-7300 - MethodCallExpression mce = (MethodCallExpression) origin; - MethodNode node = superClass.getDeclaredMethod(mce.getMethodAsString(), Parameter.EMPTY_ARRAY); - mce.setMethodTarget(node); + MethodCallExpression mce = (MethodCallExpression) origin; + if (mce.getMethodTarget() == null && !controller.getCompileStack().isLHS()) { // GROOVY-7300 + ClassNode owner = receiver.getNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER); + if (owner != null) { + mce.setMethodTarget(owner.getDeclaredMethod(mce.getMethodAsString(), Parameter.EMPTY_ARRAY)); + } } } return super.makeDirectCall(origin, receiver, message, arguments, adapter, implicitThis, containsSpreadExpression); diff --git a/base/org.codehaus.groovy40/src/org/codehaus/groovy/transform/sc/transformers/StaticCompilationTransformer.java b/base/org.codehaus.groovy40/src/org/codehaus/groovy/transform/sc/transformers/StaticCompilationTransformer.java new file mode 100644 index 0000000000..7a750419b2 --- /dev/null +++ b/base/org.codehaus.groovy40/src/org/codehaus/groovy/transform/sc/transformers/StaticCompilationTransformer.java @@ -0,0 +1,187 @@ +/* + * 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.transform.sc.transformers; + +import org.apache.groovy.util.Maps; +import org.codehaus.groovy.ast.ClassCodeExpressionTransformer; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.InnerClassNode; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.expr.BinaryExpression; +import org.codehaus.groovy.ast.expr.BooleanExpression; +import org.codehaus.groovy.ast.expr.CastExpression; +import org.codehaus.groovy.ast.expr.ClosureExpression; +import org.codehaus.groovy.ast.expr.ConstructorCallExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.ListExpression; +import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.ast.expr.PropertyExpression; +import org.codehaus.groovy.ast.expr.RangeExpression; +import org.codehaus.groovy.ast.expr.StaticMethodCallExpression; +import org.codehaus.groovy.ast.expr.VariableExpression; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.classgen.asm.sc.StaticTypesTypeChooser; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.runtime.ScriptBytecodeAdapter; +import org.codehaus.groovy.syntax.Types; +import org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor; +import org.codehaus.groovy.transform.stc.StaticTypesMarker; + +import java.util.Iterator; +import java.util.Map; + +/** + * Some expressions use symbols as aliases to method calls (<<, +=, ...). In static compilation, + * if such a method call is found, we transform the original binary expression into a method + * call expression so that the call gets statically compiled. + */ +public class StaticCompilationTransformer extends ClassCodeExpressionTransformer { + + protected static final ClassNode BYTECODE_ADAPTER_CLASS = ClassHelper.make(ScriptBytecodeAdapter.class); + protected static final Map BYTECODE_BINARY_ADAPTERS = Maps.of( + Types.COMPARE_EQUAL, BYTECODE_ADAPTER_CLASS.getMethods("compareEqual").get(0), + Types.COMPARE_GREATER_THAN, BYTECODE_ADAPTER_CLASS.getMethods("compareGreaterThan").get(0), + Types.COMPARE_GREATER_THAN_EQUAL, BYTECODE_ADAPTER_CLASS.getMethods("compareGreaterThanEqual").get(0), + Types.COMPARE_LESS_THAN, BYTECODE_ADAPTER_CLASS.getMethods("compareLessThan").get(0), + Types.COMPARE_LESS_THAN_EQUAL, BYTECODE_ADAPTER_CLASS.getMethods("compareLessThanEqual").get(0), + Types.COMPARE_NOT_EQUAL, BYTECODE_ADAPTER_CLASS.getMethods("compareNotEqual").get(0), + Types.COMPARE_TO, BYTECODE_ADAPTER_CLASS.getMethods("compareTo").get(0) + ); + + private ClassNode classNode; + private final SourceUnit unit; + + private final StaticTypesTypeChooser typeChooser = new StaticTypesTypeChooser(); + private final StaticTypeCheckingVisitor staticCompilationVisitor; + + // various helpers in order to avoid a potential very big class + private final StaticMethodCallExpressionTransformer staticMethodCallExpressionTransformer = new StaticMethodCallExpressionTransformer(this); + private final ConstructorCallTransformer constructorCallTransformer = new ConstructorCallTransformer(this); + private final MethodCallExpressionTransformer methodCallExpressionTransformer = new MethodCallExpressionTransformer(this); + private final BinaryExpressionTransformer binaryExpressionTransformer = new BinaryExpressionTransformer(this); + private final ClosureExpressionTransformer closureExpressionTransformer = new ClosureExpressionTransformer(this); + private final BooleanExpressionTransformer booleanExpressionTransformer = new BooleanExpressionTransformer(this); + private final VariableExpressionTransformer variableExpressionTransformer = new VariableExpressionTransformer(); + private final RangeExpressionTransformer rangeExpressionTransformer = new RangeExpressionTransformer(this); + private final ListExpressionTransformer listExpressionTransformer = new ListExpressionTransformer(this); + private final CastExpressionOptimizer castExpressionTransformer = new CastExpressionOptimizer(this); + + public StaticCompilationTransformer(final SourceUnit unit, final StaticTypeCheckingVisitor visitor) { + this.unit = unit; + this.staticCompilationVisitor = visitor; + } + + @Override + protected SourceUnit getSourceUnit() { + return unit; + } + + public StaticTypesTypeChooser getTypeChooser() { + return typeChooser; + } + + public ClassNode getClassNode() { + return classNode; + } + + @Override + public void visitClassCodeContainer(final Statement code) { + super.visitClassCodeContainer(code); + } + + @Override + public Expression transform(final Expression expr) { + if (expr instanceof PropertyExpression) { + MethodNode dmct = expr.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET); + // NOTE: BinaryExpressionTransformer handles the setter + if (dmct != null && dmct.getParameters().length == 0) { + PropertyExpression pe = (PropertyExpression) expr; + + MethodCallExpression mce = new MethodCallExpression(transform(pe.getObjectExpression()), dmct.getName(), MethodCallExpression.NO_ARGUMENTS); + mce.setImplicitThis(pe.isImplicitThis()); + mce.setSpreadSafe(pe.isSpreadSafe()); + mce.setMethodTarget(dmct); + mce.setSourcePosition(pe); + mce.copyNodeMetaData(pe); + mce.setSafe(pe.isSafe()); + return mce; + } + return super.transform(expr); + } + if (expr instanceof StaticMethodCallExpression) { + return staticMethodCallExpressionTransformer.transformStaticMethodCallExpression((StaticMethodCallExpression) expr); + } + if (expr instanceof MethodCallExpression) { + return methodCallExpressionTransformer.transformMethodCallExpression((MethodCallExpression) expr); + } + if (expr instanceof ConstructorCallExpression) { + return constructorCallTransformer.transformConstructorCall((ConstructorCallExpression) expr); + } + if (expr instanceof VariableExpression) { + return variableExpressionTransformer.transformVariableExpression((VariableExpression) expr); + } + if (expr instanceof ClosureExpression) { + return closureExpressionTransformer.transformClosureExpression((ClosureExpression) expr); + } + if (expr instanceof BooleanExpression) { + return booleanExpressionTransformer.transformBooleanExpression((BooleanExpression) expr); + } + if (expr instanceof BinaryExpression) { + return binaryExpressionTransformer.transformBinaryExpression((BinaryExpression) expr); + } + if (expr instanceof RangeExpression) { + return rangeExpressionTransformer.transformRangeExpression((RangeExpression) expr); + } + if (expr instanceof ListExpression) { + return listExpressionTransformer.transformListExpression((ListExpression) expr); + } + if (expr instanceof CastExpression) { + return castExpressionTransformer.transformCastExpression((CastExpression) expr); + } + return super.transform(expr); + } + + /** + * Called by helpers when super.transform() is needed. + */ + final Expression superTransform(Expression expr) { + return super.transform(expr); + } + + @Override + public void visitClass(final ClassNode node) { + ClassNode prev = classNode; + classNode = node; + super.visitClass(node); + for (Iterator innerClasses = classNode.getInnerClasses(); innerClasses.hasNext();) { + visitClass(innerClasses.next()); + } + classNode = prev; + } + + @Override + protected void visitConstructorOrMethod(final MethodNode node, final boolean isConstructor) { + if (staticCompilationVisitor.isSkipMode(node)) { + // method has already been visited by a static type checking visitor + return; + } + super.visitConstructorOrMethod(node, isConstructor); + } +}