Skip to content

Commit

Permalink
GROOVY-1736, GROOVY-6097, GROOVY-7300, GROOVY-7924
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-milles committed Dec 6, 2021
1 parent 35c908e commit 0fcd983
Show file tree
Hide file tree
Showing 10 changed files with 670 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions base/org.codehaus.groovy25/.checkstyle
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
<file-match-pattern match-pattern="groovy/transform/sc/transformers/(Binary|MethodCall)ExpressionTransformer.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/sc/transformers/ConstructorCallTransformer.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/sc/transformers/CompareToNullExpression.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/sc/transformers/StaticCompilationTransformer.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/stc/StaticTypeCheckingSupport.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/stc/StaticTypeCheckingVisitor.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/trait/SuperCallTraitTransformer.java" include-pattern="false" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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 (&lt;&lt;, +=, ...). 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<Integer, MethodNode> BYTECODE_BINARY_ADAPTERS = Collections.unmodifiableMap(new HashMap<Integer, MethodNode>() {
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<InnerClassNode> 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);
}
}
1 change: 1 addition & 0 deletions base/org.codehaus.groovy30/.checkstyle
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
<file-match-pattern match-pattern="groovy/transform/sc/TemporaryVariableExpression.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/sc/transformers/(Binary|MethodCall)ExpressionTransformer.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/sc/transformers/CompareIdentityExpression.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/sc/transformers/StaticCompilationTransformer.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/stc/AbstractExtensionMethodCache.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/stc/StaticTypeCheckingSupport.java" include-pattern="false" />
<file-match-pattern match-pattern="groovy/transform/stc/StaticTypeCheckingVisitor.java" include-pattern="false" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Loading

0 comments on commit 0fcd983

Please sign in to comment.