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 1e4b781  GROOVY-5746: SC: one execution of receiver and subscript for 
`a[i] += x`
1e4b781 is described below

commit 1e4b7816896724553f2d2ef6f9bf447e39c8e836
Author: Eric Milles <[email protected]>
AuthorDate: Wed Oct 20 14:37:47 2021 -0500

    GROOVY-5746: SC: one execution of receiver and subscript for `a[i] += x`
---
 .../transform/sc/TemporaryVariableExpression.java  | 19 +++++-----
 .../transformers/BinaryExpressionTransformer.java  | 42 ++++++++++++----------
 .../groovy/transform/stc/STCAssignmentTest.groovy  | 29 +++++++++++----
 3 files changed, 57 insertions(+), 33 deletions(-)

diff --git 
a/src/main/java/org/codehaus/groovy/transform/sc/TemporaryVariableExpression.java
 
b/src/main/java/org/codehaus/groovy/transform/sc/TemporaryVariableExpression.java
index ef41205..515cbf8 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/sc/TemporaryVariableExpression.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/sc/TemporaryVariableExpression.java
@@ -26,6 +26,8 @@ import org.codehaus.groovy.classgen.AsmClassGenerator;
 import org.codehaus.groovy.classgen.asm.ExpressionAsVariableSlot;
 import org.codehaus.groovy.classgen.asm.WriterController;
 
+import static 
org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_TYPE;
+
 /**
  * A front-end class for {@link 
org.codehaus.groovy.classgen.asm.ExpressionAsVariableSlot} which
  * allows defining temporary variables loaded from variable slots directly at 
the AST level,
@@ -37,36 +39,37 @@ public class TemporaryVariableExpression extends Expression 
{
 
     private final Expression expression;
 
-    private ExpressionAsVariableSlot variable;
+    private ExpressionAsVariableSlot[] variable = {null};
 
     public TemporaryVariableExpression(final Expression expression) {
         this.expression = expression;
+        putNodeMetaData(INFERRED_TYPE, 
expression.getNodeMetaData(INFERRED_TYPE));
     }
 
     @Override
     public Expression transformExpression(final ExpressionTransformer 
transformer) {
         TemporaryVariableExpression result = new 
TemporaryVariableExpression(transformer.transform(expression));
         result.copyNodeMetaData(this);
+        result.variable = variable;
         return result;
     }
 
     @Override
     public void visit(final GroovyCodeVisitor visitor) {
         if (visitor instanceof AsmClassGenerator) {
-            if (variable == null) {
-                AsmClassGenerator acg = (AsmClassGenerator) visitor;
-                WriterController controller = acg.getController();
-                variable = new ExpressionAsVariableSlot(controller, 
expression);
+            if (variable[0] == null) {
+                WriterController controller = ((AsmClassGenerator) 
visitor).getController();
+                variable[0] = new ExpressionAsVariableSlot(controller, 
expression);
             }
-            variable.visit(visitor);
+            variable[0].visit(visitor);
         } else {
             expression.visit(visitor);
         }
     }
 
     public void remove(final WriterController controller) {
-        controller.getCompileStack().removeVar(variable.getIndex());
-        variable = null;
+        controller.getCompileStack().removeVar(variable[0].getIndex());
+        variable[0] = null;
     }
 
     @Override
diff --git 
a/src/main/java/org/codehaus/groovy/transform/sc/transformers/BinaryExpressionTransformer.java
 
b/src/main/java/org/codehaus/groovy/transform/sc/transformers/BinaryExpressionTransformer.java
index 6caafa6..2948187 100644
--- 
a/src/main/java/org/codehaus/groovy/transform/sc/transformers/BinaryExpressionTransformer.java
+++ 
b/src/main/java/org/codehaus/groovy/transform/sc/transformers/BinaryExpressionTransformer.java
@@ -39,6 +39,7 @@ import org.codehaus.groovy.syntax.Token;
 import org.codehaus.groovy.syntax.Types;
 import org.codehaus.groovy.transform.sc.ListOfExpressionsExpression;
 import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys;
+import org.codehaus.groovy.transform.sc.TemporaryVariableExpression;
 import org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport;
 import org.codehaus.groovy.transform.stc.StaticTypesMarker;
 
@@ -212,37 +213,42 @@ public class BinaryExpressionTransformer {
                 return expr;
             }
 
-            Expression optimized = tryOptimizeCharComparison(left, right, bin);
-            if (optimized != null) {
-                
optimized.removeNodeMetaData(StaticCompilationMetadataKeys.BINARY_EXP_TARGET);
-                
optimized.removeNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
-                return optimized;
+            Expression expr = tryOptimizeCharComparison(left, right, bin);
+            if (expr != null) {
+                
expr.removeNodeMetaData(StaticCompilationMetadataKeys.BINARY_EXP_TARGET);
+                
expr.removeNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
+                return expr;
             }
 
-            String name = (String) list[1];
-            MethodNode node = (MethodNode) list[0];
-            boolean isAssignment = Types.isAssignment(operationType);
-            Expression expr = left; // TODO: if (isAssignment) scrub source 
offsets from new copy of left?
+            // replace the binary expression with a method call to 
ScriptBytecodeAdapter or something else
             MethodNode adapter = 
StaticCompilationTransformer.BYTECODE_BINARY_ADAPTERS.get(operationType);
             if (adapter != null) {
                 Expression sba = 
classX(StaticCompilationTransformer.BYTECODE_ADAPTER_CLASS);
-                call = callX(sba, adapter.getName(), args(expr, right));
+                call = callX(sba, adapter.getName(), args(left, right));
                 call.setMethodTarget(adapter);
             } else {
-                call = callX(expr, name, args(right));
-                call.setMethodTarget(node);
+                call = callX(left, (String) list[1], args(right));
+                call.setMethodTarget((MethodNode) list[0]);
             }
             call.setImplicitThis(false);
-            if (!isAssignment) {
-                call.setSourcePosition(bin);
-                return call;
+            if (Types.isAssignment(operationType)) { // +=, -=, /=, ...
+                // GROOVY-5746: one execution of receiver and subscript
+                if (left instanceof BinaryExpression) {
+                    BinaryExpression be = (BinaryExpression) left;
+                    if (be.getOperation().getType() == 
Types.LEFT_SQUARE_BRACKET) {
+                        be.setLeftExpression(new 
TemporaryVariableExpression(be.getLeftExpression()));
+                        be.setRightExpression(new 
TemporaryVariableExpression(be.getRightExpression()));
+                    }
+                }
+                // call handles the operation, so we must add the assignment 
now
+                expr = binX(left, Token.newSymbol(Types.ASSIGN, 
operation.getStartLine(), operation.getStartColumn()), call);
+            } else {
+                expr = call;
             }
-            // case of +=, -=, /=, ...
-            // the method represents the operation type only, and we must add 
an assignment
-            expr = binX(left, Token.newSymbol(Types.ASSIGN, 
operation.getStartLine(), operation.getStartColumn()), call);
             expr.setSourcePosition(bin);
             return expr;
         }
+
         if (operationType == Types.ASSIGN && leftExpression instanceof 
TupleExpression && rightExpression instanceof ListExpression) {
             // multiple assignment
             ListOfExpressionsExpression cle = new 
ListOfExpressionsExpression();
diff --git a/src/test/groovy/transform/stc/STCAssignmentTest.groovy 
b/src/test/groovy/transform/stc/STCAssignmentTest.groovy
index 28307ab..7b8a01f 100644
--- a/src/test/groovy/transform/stc/STCAssignmentTest.groovy
+++ b/src/test/groovy/transform/stc/STCAssignmentTest.groovy
@@ -142,14 +142,29 @@ class STCAssignmentTest extends 
StaticTypeCheckingTestCase {
         assertScript '''
             class C {
                 int i
-
-                static main(args) {
-                    def c = new C()
-                    c.i = 5
-                    c.i += 10
-                    assert c.i == 15
-                }
             }
+            def c = new C(i: 5)
+            def ret = c.i += 10
+
+            assert c.i == 15
+            assert ret == 15
+        '''
+    }
+
+    // GROOVY-5746
+    void testPlusEqualsAndSubscript() {
+        assertScript '''
+            import groovy.transform.Field
+
+            @Field int i = 0
+            int getIndex() { i++ }
+            def list = ['x','y','z']
+            def x = (list[index] += '!')
+            def y = (list[index] += '!')
+
+            assert x == 'x!'
+            assert y == 'y!'
+            assert list.toString() == '[x!, y!, z]'
         '''
     }
 

Reply via email to