Skip to content

Commit 2b4f3ca

Browse files
committed
GROOVY-5746
1 parent f4c4abf commit 2b4f3ca

File tree

11 files changed

+367
-68
lines changed

11 files changed

+367
-68
lines changed

base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/StaticCompilationTests.java

+22
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,28 @@ public void testCompileStatic1521() {
756756
runNegativeTest(sources, "");
757757
}
758758

759+
@Test
760+
public void testCompileStatic5746() {
761+
//@formatter:off
762+
String[] sources = {
763+
"Main.groovy",
764+
"import groovy.transform.*\n" +
765+
766+
"@Field int i = 0\n" +
767+
"int getIndex() { i++ }\n" +
768+
"@CompileStatic void test() {\n" +
769+
" def list = ['x','y','z']\n" +
770+
" print(list[index] += '!')\n" +
771+
" print(list[index] += '!')\n" +
772+
" print(list)\n" +
773+
"}\n" +
774+
"test()\n",
775+
};
776+
//@formatter:on
777+
778+
runConformTest(sources, "x!y![x!, y!, z]");
779+
}
780+
759781
@Test
760782
public void testCompileStatic6095() {
761783
//@formatter:off

base/org.codehaus.groovy25/.checkstyle

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
<file-match-pattern match-pattern="groovy/transform/NewifyASTTransformation.java" include-pattern="false" />
7878
<file-match-pattern match-pattern="groovy/transform/TupleConstructorASTTransformation.java" include-pattern="false" />
7979
<file-match-pattern match-pattern="groovy/transform/sc/StaticCompilationVisitor.java" include-pattern="false" />
80+
<file-match-pattern match-pattern="groovy/transform/sc/TemporaryVariableExpression.java" include-pattern="false" />
8081
<file-match-pattern match-pattern="groovy/transform/sc/transformers/(Binary|MethodCall)ExpressionTransformer.java" include-pattern="false" />
8182
<file-match-pattern match-pattern="groovy/transform/sc/transformers/ConstructorCallTransformer.java" include-pattern="false" />
8283
<file-match-pattern match-pattern="groovy/transform/sc/transformers/CompareToNullExpression.java" include-pattern="false" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.codehaus.groovy.transform.sc;
20+
21+
import org.codehaus.groovy.ast.ClassNode;
22+
import org.codehaus.groovy.ast.GroovyCodeVisitor;
23+
import org.codehaus.groovy.ast.expr.Expression;
24+
import org.codehaus.groovy.ast.expr.ExpressionTransformer;
25+
import org.codehaus.groovy.classgen.AsmClassGenerator;
26+
import org.codehaus.groovy.classgen.asm.ExpressionAsVariableSlot;
27+
import org.codehaus.groovy.classgen.asm.WriterController;
28+
29+
import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_TYPE;
30+
31+
/**
32+
* A front-end class for {@link org.codehaus.groovy.classgen.asm.ExpressionAsVariableSlot} which
33+
* allows defining temporary variables loaded from variable slots directly at the AST level,
34+
* without any knowledge of {@link org.codehaus.groovy.classgen.AsmClassGenerator}.
35+
*
36+
* @since 2.4.0
37+
*/
38+
public class TemporaryVariableExpression extends Expression {
39+
40+
private final Expression expression;
41+
42+
private ExpressionAsVariableSlot[] variable = {null};
43+
44+
public TemporaryVariableExpression(final Expression expression) {
45+
this.expression = expression;
46+
putNodeMetaData(INFERRED_TYPE, expression.getNodeMetaData(INFERRED_TYPE));
47+
}
48+
49+
@Override
50+
public Expression transformExpression(final ExpressionTransformer transformer) {
51+
TemporaryVariableExpression result = new TemporaryVariableExpression(transformer.transform(expression));
52+
result.copyNodeMetaData(this);
53+
result.variable = variable;
54+
return result;
55+
}
56+
57+
@Override
58+
public void visit(final GroovyCodeVisitor visitor) {
59+
if (visitor instanceof AsmClassGenerator) {
60+
if (variable[0] == null) {
61+
WriterController controller = ((AsmClassGenerator) visitor).getController();
62+
variable[0] = new ExpressionAsVariableSlot(controller, expression);
63+
}
64+
variable[0].visit(visitor);
65+
} else {
66+
expression.visit(visitor);
67+
}
68+
}
69+
70+
public void remove(final WriterController controller) {
71+
controller.getCompileStack().removeVar(variable[0].getIndex());
72+
variable[0] = null;
73+
}
74+
75+
@Override
76+
public ClassNode getType() {
77+
return expression.getType();
78+
}
79+
}

base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/sc/transformers/BinaryExpressionTransformer.java

+12
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.codehaus.groovy.syntax.Types;
4444
import org.codehaus.groovy.transform.sc.ListOfExpressionsExpression;
4545
import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys;
46+
import org.codehaus.groovy.transform.sc.TemporaryVariableExpression;
4647
import org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport;
4748
import org.codehaus.groovy.transform.stc.StaticTypesMarker;
4849

@@ -282,6 +283,17 @@ public Expression transform(final Expression expression) {
282283
call.setSourcePosition(bin);
283284
return call;
284285
}
286+
// GRECLIPSE add -- GROOVY-5746
287+
if (left instanceof BinaryExpression) {
288+
BinaryExpression be = (BinaryExpression) left;
289+
if (be.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
290+
be.setLeftExpression(new TemporaryVariableExpression(be.getLeftExpression()));
291+
be.setRightExpression(new TemporaryVariableExpression(be.getRightExpression()));
292+
((BinaryExpression) expr).setLeftExpression(be.getLeftExpression());
293+
((BinaryExpression) expr).setRightExpression(be.getRightExpression());
294+
}
295+
}
296+
// GRECLIPSE end
285297
expr = new BinaryExpression(left, Token.newSymbol(Types.EQUAL, operation.getStartLine(), operation.getStartColumn()), call);
286298
expr.putNodeMetaData("original.operator", operation);
287299
expr.setSourcePosition(bin);

base/org.codehaus.groovy30/.checkstyle

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
<file-match-pattern match-pattern="groovy/transform/LogASTTransformation.java" include-pattern="false" />
6868
<file-match-pattern match-pattern="groovy/transform/NullCheckASTTransformation.java" include-pattern="false" />
6969
<file-match-pattern match-pattern="groovy/transform/TupleConstructorASTTransformation.java" include-pattern="false" />
70+
<file-match-pattern match-pattern="groovy/transform/sc/TemporaryVariableExpression.java" include-pattern="false" />
7071
<file-match-pattern match-pattern="groovy/transform/sc/transformers/(Binary|MethodCall)ExpressionTransformer.java" include-pattern="false" />
7172
<file-match-pattern match-pattern="groovy/transform/stc/AbstractExtensionMethodCache.java" include-pattern="false" />
7273
<file-match-pattern match-pattern="groovy/transform/stc/StaticTypeCheckingSupport.java" include-pattern="false" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.codehaus.groovy.transform.sc;
20+
21+
import org.codehaus.groovy.ast.ClassNode;
22+
import org.codehaus.groovy.ast.GroovyCodeVisitor;
23+
import org.codehaus.groovy.ast.expr.Expression;
24+
import org.codehaus.groovy.ast.expr.ExpressionTransformer;
25+
import org.codehaus.groovy.classgen.AsmClassGenerator;
26+
import org.codehaus.groovy.classgen.asm.ExpressionAsVariableSlot;
27+
import org.codehaus.groovy.classgen.asm.WriterController;
28+
29+
import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_TYPE;
30+
31+
/**
32+
* A front-end class for {@link org.codehaus.groovy.classgen.asm.ExpressionAsVariableSlot} which
33+
* allows defining temporary variables loaded from variable slots directly at the AST level,
34+
* without any knowledge of {@link org.codehaus.groovy.classgen.AsmClassGenerator}.
35+
*
36+
* @since 2.4.0
37+
*/
38+
public class TemporaryVariableExpression extends Expression {
39+
40+
private final Expression expression;
41+
42+
private ExpressionAsVariableSlot[] variable = {null};
43+
44+
public TemporaryVariableExpression(final Expression expression) {
45+
this.expression = expression;
46+
putNodeMetaData(INFERRED_TYPE, expression.getNodeMetaData(INFERRED_TYPE));
47+
}
48+
49+
@Override
50+
public Expression transformExpression(final ExpressionTransformer transformer) {
51+
TemporaryVariableExpression result = new TemporaryVariableExpression(transformer.transform(expression));
52+
result.copyNodeMetaData(this);
53+
result.variable = variable;
54+
return result;
55+
}
56+
57+
@Override
58+
public void visit(final GroovyCodeVisitor visitor) {
59+
if (visitor instanceof AsmClassGenerator) {
60+
if (variable[0] == null) {
61+
WriterController controller = ((AsmClassGenerator) visitor).getController();
62+
variable[0] = new ExpressionAsVariableSlot(controller, expression);
63+
}
64+
variable[0].visit(visitor);
65+
} else {
66+
expression.visit(visitor);
67+
}
68+
}
69+
70+
public void remove(final WriterController controller) {
71+
controller.getCompileStack().removeVar(variable[0].getIndex());
72+
variable[0] = null;
73+
}
74+
75+
@Override
76+
public ClassNode getType() {
77+
return expression.getType();
78+
}
79+
}

base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/sc/transformers/BinaryExpressionTransformer.java

+12
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.codehaus.groovy.syntax.Types;
3939
import org.codehaus.groovy.transform.sc.ListOfExpressionsExpression;
4040
import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys;
41+
import org.codehaus.groovy.transform.sc.TemporaryVariableExpression;
4142
import org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport;
4243
import org.codehaus.groovy.transform.stc.StaticTypesMarker;
4344

@@ -256,6 +257,17 @@ public Expression transform(final Expression expression) {
256257
return call;
257258
}
258259
// case of +=, -=, /=, ...
260+
// GRECLIPSE add -- GROOVY-5746
261+
if (left instanceof BinaryExpression) {
262+
BinaryExpression be = (BinaryExpression) left;
263+
if (be.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
264+
be.setLeftExpression(new TemporaryVariableExpression(be.getLeftExpression()));
265+
be.setRightExpression(new TemporaryVariableExpression(be.getRightExpression()));
266+
((BinaryExpression) expr).setLeftExpression(be.getLeftExpression());
267+
((BinaryExpression) expr).setRightExpression(be.getRightExpression());
268+
}
269+
}
270+
// GRECLIPSE end
259271
// the method represents the operation type only, and we must add an assignment
260272
expr = binX(left, Token.newSymbol(Types.EQUAL, operation.getStartLine(), operation.getStartColumn()), call);
261273
// GRECLIPSE add

base/org.codehaus.groovy40/.checkstyle

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
<file-match-pattern match-pattern="groovy/transform/ASTTransformationVisitor.java" include-pattern="false" />
5858
<file-match-pattern match-pattern="groovy/transform/(Field|Log|NullCheck)ASTTransformation.java" include-pattern="false" />
5959
<file-match-pattern match-pattern="groovy/transform/NotYetImplemented.java" include-pattern="false" />
60+
<file-match-pattern match-pattern="groovy/transform/sc/TemporaryVariableExpression.java" include-pattern="false" />
6061
<file-match-pattern match-pattern="groovy/transform/sc/transformers/(Binary|MethodCall)ExpressionTransformer.java" include-pattern="false" />
6162
<file-match-pattern match-pattern="groovy/transform/stc/StaticTypeCheckingSupport.java" include-pattern="false" />
6263
<file-match-pattern match-pattern="groovy/transform/stc/StaticTypeCheckingVisitor.java" include-pattern="false" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.codehaus.groovy.transform.sc;
20+
21+
import org.codehaus.groovy.ast.ClassNode;
22+
import org.codehaus.groovy.ast.GroovyCodeVisitor;
23+
import org.codehaus.groovy.ast.expr.Expression;
24+
import org.codehaus.groovy.ast.expr.ExpressionTransformer;
25+
import org.codehaus.groovy.classgen.AsmClassGenerator;
26+
import org.codehaus.groovy.classgen.asm.ExpressionAsVariableSlot;
27+
import org.codehaus.groovy.classgen.asm.WriterController;
28+
29+
import static org.codehaus.groovy.transform.stc.StaticTypesMarker.INFERRED_TYPE;
30+
31+
/**
32+
* A front-end class for {@link org.codehaus.groovy.classgen.asm.ExpressionAsVariableSlot} which
33+
* allows defining temporary variables loaded from variable slots directly at the AST level,
34+
* without any knowledge of {@link org.codehaus.groovy.classgen.AsmClassGenerator}.
35+
*
36+
* @since 2.4.0
37+
*/
38+
public class TemporaryVariableExpression extends Expression {
39+
40+
private final Expression expression;
41+
42+
private ExpressionAsVariableSlot[] variable = {null};
43+
44+
public TemporaryVariableExpression(final Expression expression) {
45+
this.expression = expression;
46+
putNodeMetaData(INFERRED_TYPE, expression.getNodeMetaData(INFERRED_TYPE));
47+
}
48+
49+
@Override
50+
public Expression transformExpression(final ExpressionTransformer transformer) {
51+
TemporaryVariableExpression result = new TemporaryVariableExpression(transformer.transform(expression));
52+
result.copyNodeMetaData(this);
53+
result.variable = variable;
54+
return result;
55+
}
56+
57+
@Override
58+
public void visit(final GroovyCodeVisitor visitor) {
59+
if (visitor instanceof AsmClassGenerator) {
60+
if (variable[0] == null) {
61+
WriterController controller = ((AsmClassGenerator) visitor).getController();
62+
variable[0] = new ExpressionAsVariableSlot(controller, expression);
63+
}
64+
variable[0].visit(visitor);
65+
} else {
66+
expression.visit(visitor);
67+
}
68+
}
69+
70+
public void remove(final WriterController controller) {
71+
controller.getCompileStack().removeVar(variable[0].getIndex());
72+
variable[0] = null;
73+
}
74+
75+
@Override
76+
public ClassNode getType() {
77+
return expression.getType();
78+
}
79+
}

0 commit comments

Comments
 (0)