Skip to content

Commit 7abec84

Browse files
committed
GROOVY-8133, GROOVY-10476, GROOVY-10477
1 parent 85fc2db commit 7abec84

File tree

10 files changed

+1016
-41
lines changed

10 files changed

+1016
-41
lines changed

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

+43
Original file line numberDiff line numberDiff line change
@@ -2380,6 +2380,29 @@ public void testCompileStatic8051() {
23802380
runConformTest(sources, "1");
23812381
}
23822382

2383+
@Test
2384+
public void testCompileStatic8133() {
2385+
//@formatter:off
2386+
String[] sources = {
2387+
"Main.groovy",
2388+
"import groovy.transform.*\n" +
2389+
"import static org.codehaus.groovy.transform.stc.StaticTypesMarker.*\n" +
2390+
"\n" +
2391+
"@CompileStatic void test() {\n" +
2392+
" @ASTTest(phase=INSTRUCTION_SELECTION, value={\n" +
2393+
" def list_type = node.getNodeMetaData(INFERRED_TYPE)\n" +
2394+
" assert list_type?.toString(false) == 'java.util.List<java.lang.String>'\n" +
2395+
" })\n" +
2396+
" def list = ['foo','bar','baz'].stream()*.toUpperCase()\n" +
2397+
" print list\n" +
2398+
"}\n" +
2399+
"test()\n",
2400+
};
2401+
//@formatter:on
2402+
2403+
runConformTest(sources, "[FOO, BAR, BAZ]");
2404+
}
2405+
23832406
@Test
23842407
public void testCompileStatic8176() {
23852408
//@formatter:off
@@ -7278,4 +7301,24 @@ public void testCompileStatic10457() {
72787301

72797302
runConformTest(sources, "works");
72807303
}
7304+
7305+
@Test
7306+
public void testCompileStatic10476() {
7307+
//@formatter:off
7308+
String[] sources = {
7309+
"Main.groovy",
7310+
"@groovy.transform.CompileStatic\n" +
7311+
"void test() {\n" +
7312+
" def list = []\n" +
7313+
" for (e in ['foo','bar','baz'].stream()) {\n" +
7314+
" list.add(e.toUpperCase())\n" +
7315+
" }\n" +
7316+
" print list\n" +
7317+
"}\n" +
7318+
"test()\n",
7319+
};
7320+
//@formatter:on
7321+
7322+
runConformTest(sources, "[FOO, BAR, BAZ]");
7323+
}
72817324
}

base/org.codehaus.groovy25/.checkstyle

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
<file-match-pattern match-pattern="groovy/classgen/asm/(Optimizing)?StatementWriter.java" include-pattern="false" />
5454
<file-match-pattern match-pattern="groovy/classgen/asm/sc/StaticInvocationWriter.java" include-pattern="false" />
5555
<file-match-pattern match-pattern="groovy/classgen/asm/sc/StaticPropertyAccessHelper.java" include-pattern="false" />
56-
<file-match-pattern match-pattern="groovy/classgen/asm/sc/StaticTypes(CallSite|Closure)Writer.java" include-pattern="false" />
56+
<file-match-pattern match-pattern="groovy/classgen/asm/sc/StaticTypes(CallSite|Closure|Statement)Writer.java" include-pattern="false" />
5757
<file-match-pattern match-pattern="groovy/classgen/asm/sc/StaticTypesTypeChooser.java" include-pattern="false" />
5858
<file-match-pattern match-pattern="groovy/control/CompilationUnit.java" include-pattern="false" />
5959
<file-match-pattern match-pattern="groovy/control/CompilerConfiguration.java" include-pattern="false" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
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.classgen.asm.sc;
20+
21+
import org.codehaus.groovy.ast.ClassHelper;
22+
import org.codehaus.groovy.ast.ClassNode;
23+
import org.codehaus.groovy.ast.MethodNode;
24+
import org.codehaus.groovy.ast.Parameter;
25+
import org.codehaus.groovy.ast.expr.Expression;
26+
import org.codehaus.groovy.ast.expr.MethodCallExpression;
27+
import org.codehaus.groovy.ast.stmt.BlockStatement;
28+
import org.codehaus.groovy.ast.stmt.ForStatement;
29+
import org.codehaus.groovy.ast.tools.GeneralUtils;
30+
import org.codehaus.groovy.classgen.AsmClassGenerator;
31+
import org.codehaus.groovy.classgen.asm.BytecodeVariable;
32+
import org.codehaus.groovy.classgen.asm.CompileStack;
33+
import org.codehaus.groovy.classgen.asm.MethodCaller;
34+
import org.codehaus.groovy.classgen.asm.OperandStack;
35+
import org.codehaus.groovy.classgen.asm.StatementWriter;
36+
import org.codehaus.groovy.classgen.asm.TypeChooser;
37+
import org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport;
38+
import groovyjarjarasm.asm.Label;
39+
import groovyjarjarasm.asm.MethodVisitor;
40+
41+
import java.util.Enumeration;
42+
import java.util.Objects;
43+
44+
import static groovyjarjarasm.asm.Opcodes.AALOAD;
45+
import static groovyjarjarasm.asm.Opcodes.ALOAD;
46+
import static groovyjarjarasm.asm.Opcodes.ARRAYLENGTH;
47+
import static groovyjarjarasm.asm.Opcodes.BALOAD;
48+
import static groovyjarjarasm.asm.Opcodes.CALOAD;
49+
import static groovyjarjarasm.asm.Opcodes.DALOAD;
50+
import static groovyjarjarasm.asm.Opcodes.DUP;
51+
import static groovyjarjarasm.asm.Opcodes.FALOAD;
52+
import static groovyjarjarasm.asm.Opcodes.GOTO;
53+
import static groovyjarjarasm.asm.Opcodes.IALOAD;
54+
import static groovyjarjarasm.asm.Opcodes.ICONST_0;
55+
import static groovyjarjarasm.asm.Opcodes.IFEQ;
56+
import static groovyjarjarasm.asm.Opcodes.IFNULL;
57+
import static groovyjarjarasm.asm.Opcodes.IF_ICMPGE;
58+
import static groovyjarjarasm.asm.Opcodes.ILOAD;
59+
import static groovyjarjarasm.asm.Opcodes.INVOKESTATIC;
60+
import static groovyjarjarasm.asm.Opcodes.LALOAD;
61+
import static groovyjarjarasm.asm.Opcodes.SALOAD;
62+
63+
/**
64+
* A class to write out the optimized statements
65+
*/
66+
public class StaticTypesStatementWriter extends StatementWriter {
67+
68+
private static final ClassNode ENUMERATION_CLASSNODE = ClassHelper.make(Enumeration.class);
69+
private static final MethodCaller ENUMERATION_NEXT_METHOD = MethodCaller.newInterface(Enumeration.class, "nextElement");
70+
private static final MethodCaller ENUMERATION_HASMORE_METHOD = MethodCaller.newInterface(Enumeration.class, "hasMoreElements");
71+
72+
private final StaticTypesWriterController controller;
73+
74+
public StaticTypesStatementWriter(StaticTypesWriterController controller) {
75+
super(controller);
76+
this.controller = controller;
77+
}
78+
79+
@Override
80+
public void writeBlockStatement(BlockStatement statement) {
81+
controller.switchToFastPath();
82+
super.writeBlockStatement(statement);
83+
controller.switchToSlowPath();
84+
}
85+
86+
@Override
87+
protected void writeForInLoop(final ForStatement loop) {
88+
controller.getAcg().onLineNumber(loop,"visitForLoop");
89+
writeStatementLabel(loop);
90+
91+
CompileStack compileStack = controller.getCompileStack();
92+
MethodVisitor mv = controller.getMethodVisitor();
93+
OperandStack operandStack = controller.getOperandStack();
94+
95+
compileStack.pushLoop(loop.getVariableScope(), loop.getStatementLabels());
96+
97+
// Identify type of collection
98+
TypeChooser typeChooser = controller.getTypeChooser();
99+
Expression collectionExpression = loop.getCollectionExpression();
100+
ClassNode collectionType = typeChooser.resolveType(collectionExpression, controller.getClassNode());
101+
Parameter loopVariable = loop.getVariable();
102+
int size = operandStack.getStackLength();
103+
if (collectionType.isArray() && loopVariable.getType().equals(collectionType.getComponentType())) { // GRECLIPSE edit
104+
writeOptimizedForEachLoop(compileStack, operandStack, mv, loop, collectionExpression, collectionType, loopVariable);
105+
} else if (ENUMERATION_CLASSNODE.equals(collectionType)) {
106+
writeEnumerationBasedForEachLoop(compileStack, operandStack, mv, loop, collectionExpression, collectionType, loopVariable);
107+
} else {
108+
writeIteratorBasedForEachLoop(compileStack, operandStack, mv, loop, collectionExpression, collectionType, loopVariable);
109+
}
110+
operandStack.popDownTo(size);
111+
compileStack.pop();
112+
}
113+
114+
private void writeOptimizedForEachLoop(
115+
CompileStack compileStack,
116+
OperandStack operandStack,
117+
MethodVisitor mv,
118+
ForStatement loop,
119+
Expression collectionExpression,
120+
ClassNode collectionType,
121+
Parameter loopVariable) {
122+
BytecodeVariable variable = compileStack.defineVariable(loopVariable, false);
123+
124+
Label continueLabel = compileStack.getContinueLabel();
125+
Label breakLabel = compileStack.getBreakLabel();
126+
127+
AsmClassGenerator acg = controller.getAcg();
128+
129+
// load array on stack
130+
collectionExpression.visit(acg);
131+
mv.visitInsn(DUP);
132+
int array = compileStack.defineTemporaryVariable("$arr", collectionType, true);
133+
mv.visitJumpInsn(IFNULL, breakLabel);
134+
135+
// $len = array.length
136+
mv.visitVarInsn(ALOAD, array);
137+
mv.visitInsn(ARRAYLENGTH);
138+
operandStack.push(ClassHelper.int_TYPE);
139+
int arrayLen = compileStack.defineTemporaryVariable("$len", ClassHelper.int_TYPE, true);
140+
141+
// $idx = 0
142+
mv.visitInsn(ICONST_0);
143+
operandStack.push(ClassHelper.int_TYPE);
144+
int loopIdx = compileStack.defineTemporaryVariable("$idx", ClassHelper.int_TYPE, true);
145+
146+
mv.visitLabel(continueLabel);
147+
// $idx<$len?
148+
mv.visitVarInsn(ILOAD, loopIdx);
149+
mv.visitVarInsn(ILOAD, arrayLen);
150+
mv.visitJumpInsn(IF_ICMPGE, breakLabel);
151+
152+
// get array element
153+
loadFromArray(mv, variable, array, loopIdx);
154+
155+
// $idx++
156+
mv.visitIincInsn(loopIdx, 1);
157+
158+
// loop body
159+
loop.getLoopBlock().visit(acg);
160+
161+
mv.visitJumpInsn(GOTO, continueLabel);
162+
163+
mv.visitLabel(breakLabel);
164+
165+
compileStack.removeVar(loopIdx);
166+
compileStack.removeVar(arrayLen);
167+
compileStack.removeVar(array);
168+
}
169+
170+
private void loadFromArray(MethodVisitor mv, BytecodeVariable variable, int array, int iteratorIdx) {
171+
OperandStack os = controller.getOperandStack();
172+
mv.visitVarInsn(ALOAD, array);
173+
mv.visitVarInsn(ILOAD, iteratorIdx);
174+
175+
ClassNode varType = variable.getType();
176+
boolean primitiveType = ClassHelper.isPrimitiveType(varType);
177+
boolean isByte = ClassHelper.byte_TYPE.equals(varType);
178+
boolean isShort = ClassHelper.short_TYPE.equals(varType);
179+
boolean isInt = ClassHelper.int_TYPE.equals(varType);
180+
boolean isLong = ClassHelper.long_TYPE.equals(varType);
181+
boolean isFloat = ClassHelper.float_TYPE.equals(varType);
182+
boolean isDouble = ClassHelper.double_TYPE.equals(varType);
183+
boolean isChar = ClassHelper.char_TYPE.equals(varType);
184+
boolean isBoolean = ClassHelper.boolean_TYPE.equals(varType);
185+
186+
if (primitiveType) {
187+
if (isByte) {
188+
mv.visitInsn(BALOAD);
189+
}
190+
if (isShort) {
191+
mv.visitInsn(SALOAD);
192+
}
193+
if (isInt || isChar || isBoolean) {
194+
mv.visitInsn(isChar ? CALOAD : isBoolean ? BALOAD : IALOAD);
195+
}
196+
if (isLong) {
197+
mv.visitInsn(LALOAD);
198+
}
199+
if (isFloat) {
200+
mv.visitInsn(FALOAD);
201+
}
202+
if (isDouble) {
203+
mv.visitInsn(DALOAD);
204+
}
205+
} else {
206+
mv.visitInsn(AALOAD);
207+
}
208+
os.push(varType);
209+
os.storeVar(variable);
210+
}
211+
212+
private void writeIteratorBasedForEachLoop(
213+
CompileStack compileStack,
214+
OperandStack operandStack,
215+
MethodVisitor mv,
216+
ForStatement loop,
217+
Expression collectionExpression,
218+
ClassNode collectionType,
219+
Parameter loopVariable) {
220+
// Declare the loop counter.
221+
BytecodeVariable variable = compileStack.defineVariable(loopVariable, false);
222+
/* GRECLIPSE edit -- GROOVY-10476
223+
if (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(collectionType, ITERABLE_CLASSNODE)) {
224+
MethodCallExpression iterator = new MethodCallExpression(collectionExpression, "iterator", new ArgumentListExpression());
225+
iterator.setMethodTarget(collectionType.getMethod("iterator", Parameter.EMPTY_ARRAY));
226+
iterator.setImplicitThis(false);
227+
iterator.visit(controller.getAcg());
228+
*/
229+
MethodNode iterator = collectionType.getMethod("iterator", Parameter.EMPTY_ARRAY);
230+
if (iterator == null) {
231+
iterator = StaticTypeCheckingSupport.collectAllInterfaces(collectionType).stream()
232+
.map(in -> in.getMethod("iterator", Parameter.EMPTY_ARRAY))
233+
.filter(Objects::nonNull).findFirst().orElse(null);
234+
}
235+
if (iterator != null && iterator.getReturnType().equals(ClassHelper.Iterator_TYPE)) {
236+
MethodCallExpression call = GeneralUtils.callX(collectionExpression, "iterator");
237+
call.setImplicitThis(false);
238+
call.setMethodTarget(iterator);
239+
call.visit(controller.getAcg());
240+
// GRECLIPSE end
241+
} else {
242+
collectionExpression.visit(controller.getAcg());
243+
mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/runtime/DefaultGroovyMethods", "iterator", "(Ljava/lang/Object;)Ljava/util/Iterator;", false);
244+
operandStack.replace(ClassHelper.Iterator_TYPE);
245+
}
246+
247+
// Then get the iterator and generate the loop control
248+
249+
int iteratorIdx = compileStack.defineTemporaryVariable("iterator", ClassHelper.Iterator_TYPE, true);
250+
251+
Label continueLabel = compileStack.getContinueLabel();
252+
Label breakLabel = compileStack.getBreakLabel();
253+
254+
mv.visitLabel(continueLabel);
255+
mv.visitVarInsn(ALOAD, iteratorIdx);
256+
writeIteratorHasNext(mv);
257+
// note: ifeq tests for ==0, a boolean is 0 if it is false
258+
mv.visitJumpInsn(IFEQ, breakLabel);
259+
260+
mv.visitVarInsn(ALOAD, iteratorIdx);
261+
writeIteratorNext(mv);
262+
operandStack.push(ClassHelper.OBJECT_TYPE);
263+
operandStack.storeVar(variable);
264+
265+
// Generate the loop body
266+
loop.getLoopBlock().visit(controller.getAcg());
267+
268+
mv.visitJumpInsn(GOTO, continueLabel);
269+
mv.visitLabel(breakLabel);
270+
compileStack.removeVar(iteratorIdx);
271+
}
272+
273+
private void writeEnumerationBasedForEachLoop(
274+
CompileStack compileStack,
275+
OperandStack operandStack,
276+
MethodVisitor mv,
277+
ForStatement loop,
278+
Expression collectionExpression,
279+
ClassNode collectionType,
280+
Parameter loopVariable) {
281+
// Declare the loop counter.
282+
BytecodeVariable variable = compileStack.defineVariable(loopVariable, false);
283+
284+
collectionExpression.visit(controller.getAcg());
285+
286+
// Then get the iterator and generate the loop control
287+
288+
int enumIdx = compileStack.defineTemporaryVariable("$enum", ENUMERATION_CLASSNODE, true);
289+
290+
Label continueLabel = compileStack.getContinueLabel();
291+
Label breakLabel = compileStack.getBreakLabel();
292+
293+
mv.visitLabel(continueLabel);
294+
mv.visitVarInsn(ALOAD, enumIdx);
295+
ENUMERATION_HASMORE_METHOD.call(mv);
296+
// note: ifeq tests for ==0, a boolean is 0 if it is false
297+
mv.visitJumpInsn(IFEQ, breakLabel);
298+
299+
mv.visitVarInsn(ALOAD, enumIdx);
300+
ENUMERATION_NEXT_METHOD.call(mv);
301+
operandStack.push(ClassHelper.OBJECT_TYPE);
302+
operandStack.storeVar(variable);
303+
304+
// Generate the loop body
305+
loop.getLoopBlock().visit(controller.getAcg());
306+
307+
mv.visitJumpInsn(GOTO, continueLabel);
308+
mv.visitLabel(breakLabel);
309+
}
310+
}

0 commit comments

Comments
 (0)