You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2020/08/08 13:40:36 UTC
[groovy] branch master updated: GROOVY-9255: transform
"TraitType.super.name" to getName()/isName() call
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new ffb8322 GROOVY-9255: transform "TraitType.super.name" to getName()/isName() call
ffb8322 is described below
commit ffb83225b5be7c7f567b6fed91b23612b2bcc185
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sat Aug 8 08:40:27 2020 -0500
GROOVY-9255: transform "TraitType.super.name" to getName()/isName() call
"TraitType.super.name = ..." was already handled; binary expression is
transformed before subbing "TraitType$Helper.setName(this, ...)", so be
careful not to transform LHS (assign target) to accessor call.
closes #1340
---
.../java/org/codehaus/groovy/syntax/Types.java | 3 +
.../transform/trait/SuperCallTraitTransformer.java | 55 +++++++++++++
.../traitx/TraitASTTransformationTest.groovy | 94 ++++++++++++++++++++++
3 files changed, 152 insertions(+)
diff --git a/src/main/java/org/codehaus/groovy/syntax/Types.java b/src/main/java/org/codehaus/groovy/syntax/Types.java
index 0464dc6..4cf90c4 100644
--- a/src/main/java/org/codehaus/groovy/syntax/Types.java
+++ b/src/main/java/org/codehaus/groovy/syntax/Types.java
@@ -368,6 +368,9 @@ public class Types {
//---------------------------------------------------------------------------
// TYPE HIERARCHIES
+ /**
+ * @since 3.0.0
+ */
public static boolean isAssignment(int type) {
return ofType(type, ASSIGNMENT_OPERATOR);
}
diff --git a/src/main/java/org/codehaus/groovy/transform/trait/SuperCallTraitTransformer.java b/src/main/java/org/codehaus/groovy/transform/trait/SuperCallTraitTransformer.java
index 4eccccb..ba57ecc 100644
--- a/src/main/java/org/codehaus/groovy/transform/trait/SuperCallTraitTransformer.java
+++ b/src/main/java/org/codehaus/groovy/transform/trait/SuperCallTraitTransformer.java
@@ -36,6 +36,8 @@ import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.syntax.Types;
+import java.util.function.Function;
+
import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
@@ -66,6 +68,9 @@ class SuperCallTraitTransformer extends ClassCodeExpressionTransformer {
if (exp instanceof BinaryExpression) {
return transformBinaryExpression((BinaryExpression) exp);
}
+ if (exp instanceof PropertyExpression) {
+ return transformPropertyExpression((PropertyExpression) exp);
+ }
if (exp instanceof MethodCallExpression) {
return transformMethodCallExpression((MethodCallExpression) exp);
}
@@ -73,6 +78,10 @@ class SuperCallTraitTransformer extends ClassCodeExpressionTransformer {
}
private Expression transformBinaryExpression(final BinaryExpression exp) {
+ if (Types.isAssignment(exp.getOperation().getType()))
+ // prevent transform of assignment target to accessor method call
+ exp.getLeftExpression().putNodeMetaData("assign.target", exp.getOperation());
+
Expression trn = super.transform(exp);
if (trn instanceof BinaryExpression) {
BinaryExpression bin = (BinaryExpression) trn;
@@ -109,6 +118,52 @@ class SuperCallTraitTransformer extends ClassCodeExpressionTransformer {
return trn;
}
+ private Expression transformPropertyExpression(final PropertyExpression exp) {
+ if (exp.getNodeMetaData("assign.target") == null) {
+ ClassNode traitType = getTraitSuperTarget(exp.getObjectExpression());
+ if (traitType != null) {
+ ClassNode helperType = getHelper(traitType);
+ // TraitType.super.foo -> TraitType$Helper.getFoo(this)
+
+ Function<MethodNode, MethodCallExpression> xform = (methodNode) -> {
+ MethodCallExpression methodCall = new MethodCallExpression(
+ new ClassExpression(helperType),
+ methodNode.getName(),
+ new ArgumentListExpression(
+ new VariableExpression("this")
+ )
+ );
+ methodCall.getObjectExpression().setSourcePosition(((PropertyExpression) exp.getObjectExpression()).getObjectExpression());
+ methodCall.getMethod().setSourcePosition(exp.getProperty());
+ methodCall.setSpreadSafe(exp.isSpreadSafe());
+ methodCall.setMethodTarget(methodNode);
+ methodCall.setImplicitThis(false);
+ return methodCall;
+ };
+
+ String getterName = MetaProperty.getGetterName(exp.getPropertyAsString(), null);
+ for (MethodNode method : helperType.getMethods(getterName)) {
+ if (method.isStatic() && method.getParameters().length == 1
+ && method.getParameters()[0].getType().equals(traitType)
+ && !method.getReturnType().equals(ClassHelper.VOID_TYPE)) {
+ return xform.apply(method);
+ }
+ }
+
+ String isserName = "is" + getterName.substring(3);
+ for (MethodNode method : helperType.getMethods(isserName)) {
+ if (method.isStatic() && method.getParameters().length == 1
+ && method.getParameters()[0].getType().equals(traitType)
+ && method.getReturnType().equals(ClassHelper.boolean_TYPE)) {
+ return xform.apply(method);
+ }
+ }
+ }
+ }
+ exp.removeNodeMetaData("assign.target");
+ return super.transform(exp);
+ }
+
private Expression transformMethodCallExpression(final MethodCallExpression exp) {
ClassNode traitType = getTraitSuperTarget(exp.getObjectExpression());
if (traitType != null) {
diff --git a/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy b/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
index 81d6fd0..5a57531 100644
--- a/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
+++ b/src/test/org/codehaus/groovy/transform/traitx/TraitASTTransformationTest.groovy
@@ -1014,6 +1014,100 @@ final class TraitASTTransformationTest {
'''
}
+ @Test // GROOVY-9255
+ void testTraitSuperPropertyGet() {
+ assertScript '''
+ trait T {
+ def x = 'value'
+ }
+ class C implements T {
+ def test() {
+ T.super.x
+ }
+ }
+ assert new C().test() == 'value'
+ '''
+
+ assertScript '''
+ trait T {
+ boolean x = true
+ }
+ class C implements T {
+ def test() {
+ T.super.x
+ }
+ }
+ assert new C().test() == true
+ '''
+
+ assertScript '''
+ trait T {
+ def getX() { 'value' }
+ }
+ class C implements T {
+ def test() {
+ T.super.x
+ }
+ }
+ assert new C().test() == 'value'
+ '''
+
+ assertScript '''
+ trait T {
+ boolean isX() { true }
+ }
+ class C implements T {
+ def test() {
+ T.super.x
+ }
+ }
+ assert new C().test() == true
+ '''
+ }
+
+ @Test
+ void testTraitSuperPropertySet() {
+ assertScript '''
+ trait T {
+ def x
+ }
+ class C implements T {
+ def test() {
+ T.super.x = 'value'
+ return x
+ }
+ }
+ assert new C().test() == 'value'
+ '''
+
+ // TODO: add support for compound assignment
+ shouldFail MissingPropertyException, '''
+ trait T {
+ def x = 'value'
+ }
+ class C implements T {
+ def test() {
+ T.super.x -= ~/e\b/
+ T.super.x += 'able'
+ return x
+ }
+ }
+ assert new C().test() == 'valuable'
+ '''
+
+ assertScript '''
+ trait T {
+ def setX(value) { 'retval' }
+ }
+ class C implements T {
+ def test() {
+ T.super.x = 'value'
+ }
+ }
+ assert new C().test() == 'retval'
+ '''
+ }
+
@Test
void testSuperCallInTraitExtendingAnotherTrait() {
assertScript '''