Skip to content

Commit c05a538

Browse files
committed
GROOVY-11856: STC: save inferred type of list and map expressions
1 parent 4782cd7 commit c05a538

File tree

4 files changed

+41
-29
lines changed

4 files changed

+41
-29
lines changed

src/main/java/org/codehaus/groovy/classgen/asm/OperandStack.java

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@
2222
import org.codehaus.groovy.GroovyBugError;
2323
import org.codehaus.groovy.ast.ClassHelper;
2424
import org.codehaus.groovy.ast.ClassNode;
25-
import org.codehaus.groovy.ast.ConstructorNode;
26-
import org.codehaus.groovy.ast.MethodNode;
2725
import org.codehaus.groovy.ast.Variable;
2826
import org.codehaus.groovy.ast.expr.CastExpression;
2927
import org.codehaus.groovy.ast.expr.ConstantExpression;
@@ -313,36 +311,40 @@ public void doAsType(final ClassNode targetType) {
313311
doConvertAndCast(targetType,true);
314312
}
315313

316-
private void throwExceptionForNoStackElement(final int size, final ClassNode targetType, final boolean coerce) {
317-
if (size > 0) return;
318-
StringBuilder sb = new StringBuilder();
319-
sb.append("Internal compiler error while compiling ").append(controller.getSourceUnit().getName()).append("\n");
320-
MethodNode methodNode = controller.getMethodNode();
321-
if (methodNode!=null) {
322-
sb.append("Method: ");
323-
sb.append(methodNode);
324-
sb.append("\n");
325-
}
326-
ConstructorNode constructorNode = controller.getConstructorNode();
327-
if (constructorNode!=null) {
314+
private String missingOperand(final ClassNode targetType, final boolean coerce) {
315+
var sb = new StringBuilder("Internal compiler error while compiling ");
316+
sb.append(controller.getSourceUnit().getName());
317+
sb.append("\n");
318+
var constructorNode = controller.getConstructorNode();
319+
if (constructorNode != null) {
328320
sb.append("Constructor: ");
329-
sb.append(methodNode);
321+
sb.append(constructorNode);
330322
sb.append("\n");
323+
} else {
324+
var methodNode = controller.getMethodNode();
325+
if (methodNode != null) {
326+
sb.append("Method: ");
327+
sb.append(methodNode);
328+
sb.append("\n");
329+
}
331330
}
332-
sb.append("Line ").append(controller.getLineNumber()).append(",");
333-
sb.append(" expecting ").append(coerce ? "coercion" : "casting").append(" to ").append(targetType.toString(false));
331+
sb.append("Line ").append(controller.getLineNumber()).append(", expecting ").append(coerce ? "coercion" : "casting");
332+
sb.append(" to ").append(ClassNodeUtils.formatTypeName(targetType));
334333
sb.append(" but operand stack is empty");
335-
throw new ArrayIndexOutOfBoundsException(sb.toString());
334+
return sb.toString();
336335
}
337336

338337
private void doConvertAndCast(ClassNode targetType, final boolean coerce) {
339338
int size = stack.size();
340-
throwExceptionForNoStackElement(size, targetType, coerce);
339+
if (size == 0) {
340+
throw new ArrayIndexOutOfBoundsException(missingOperand(targetType, coerce));
341+
}
341342

342343
ClassNode top = stack.get(size - 1);
343-
targetType = targetType.redirect();
344-
if (top == targetType /* for better performance */
345-
|| ClassNodeUtils.isCompatibleWith(top, targetType)) return;
344+
if (top == (targetType = targetType.redirect()) // quick check
345+
|| ClassNodeUtils.isCompatibleWith(top, targetType)) {
346+
return;
347+
}
346348

347349
if (coerce) {
348350
controller.getInvocationWriter().coerce(top, targetType);

src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5271,11 +5271,15 @@ protected ClassNode getType(final ASTNode node) {
52715271
}
52725272

52735273
if (node instanceof ListExpression) {
5274-
return inferListExpressionType((ListExpression) node);
5274+
type = inferListExpressionType((ListExpression) node);
5275+
node.putNodeMetaData(INFERRED_TYPE, type);
5276+
return type;
52755277
}
52765278

52775279
if (node instanceof MapExpression) {
5278-
return inferMapExpressionType((MapExpression) node);
5280+
type = inferMapExpressionType((MapExpression) node);
5281+
node.putNodeMetaData(INFERRED_TYPE, type);
5282+
return type;
52795283
}
52805284

52815285
if (node instanceof RangeExpression) {

src/test/groovy/bugs/Groovy10034.groovy

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,14 @@ final class Groovy10034 extends AbstractBytecodeTestCase {
3131
["x"].toArray(new String[0])
3232
}
3333
'''
34-
int offset = result.indexOf('ANEWARRAY java/lang/String', result.indexOf('--BEGIN--'))
35-
assert result.hasStrictSequence(['ANEWARRAY java/lang/String','INVOKEVIRTUAL java/util/ArrayList.toArray'], offset)
36-
// there should be no 'INVOKEDYNAMIC cast' instruction here: ^
34+
int offset = result.indexOf('INVOKESTATIC', result.indexOf('--BEGIN--'))
35+
assert result.hasStrictSequence([
36+
'INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList',
37+
'CHECKCAST java/util/ArrayList', // not 'INVOKEDYNAMIC cast'
38+
'ICONST_0',
39+
'ANEWARRAY java/lang/String',
40+
// no 'INVOKEDYNAMIC cast' to Object[]
41+
'INVOKEVIRTUAL java/util/ArrayList.toArray'
42+
], offset)
3743
}
3844
}

src/test/groovy/bugs/Groovy9126.groovy

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919
package bugs
2020

2121
import org.codehaus.groovy.classgen.asm.AbstractBytecodeTestCase
22-
import org.junit.Test
22+
import org.junit.jupiter.api.Test
2323

2424
final class Groovy9126 extends AbstractBytecodeTestCase {
2525

2626
@Test
27-
void testUnreachableBytecode() {
27+
void testUnreachableBytecode1() {
2828
assert compile(method:'nonVoidMethod', '''@groovy.transform.CompileStatic
2929
int nonVoidMethod() {
3030
1 * 1

0 commit comments

Comments
 (0)