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 2022/11/02 19:01:04 UTC
[groovy] branch master updated: GROOVY-6925: SC: disable SC but run STC for class, method or constructor
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 d5a9406e20 GROOVY-6925: SC: disable SC but run STC for class, method or constructor
d5a9406e20 is described below
commit d5a9406e20dafed9500b7c6dc1ed4246d17d094b
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Nov 2 13:20:22 2022 -0500
GROOVY-6925: SC: disable SC but run STC for class, method or constructor
---
.../transform/sc/StaticCompilationVisitor.java | 429 +++++++++++----------
.../classgen/asm/sc/StaticCompilationTest.groovy | 59 ++-
2 files changed, 269 insertions(+), 219 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/transform/sc/StaticCompilationVisitor.java b/src/main/java/org/codehaus/groovy/transform/sc/StaticCompilationVisitor.java
index 3c222adc29..871c9ae88f 100644
--- a/src/main/java/org/codehaus/groovy/transform/sc/StaticCompilationVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/sc/StaticCompilationVisitor.java
@@ -46,10 +46,8 @@ import org.codehaus.groovy.ast.stmt.ForStatement;
import org.codehaus.groovy.ast.stmt.Statement;
import org.codehaus.groovy.classgen.asm.InvocationWriter;
import org.codehaus.groovy.classgen.asm.MopWriter;
-import org.codehaus.groovy.classgen.asm.TypeChooser;
import org.codehaus.groovy.classgen.asm.WriterControllerFactory;
import org.codehaus.groovy.classgen.asm.sc.StaticCompilationMopWriter;
-import org.codehaus.groovy.classgen.asm.sc.StaticTypesTypeChooser;
import org.codehaus.groovy.control.CompilationUnit.IPrimaryClassNodeOperation;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport;
@@ -125,10 +123,6 @@ public class StaticCompilationVisitor extends StaticTypeCheckingVisitor {
ARRAYLIST_CONSTRUCTOR.setDeclaringClass(StaticCompilationVisitor.ARRAYLIST_CLASSNODE);
}
- private final TypeChooser typeChooser = new StaticTypesTypeChooser();
-
- private ClassNode classNode;
-
public StaticCompilationVisitor(final SourceUnit unit, final ClassNode node) {
super(unit, node);
}
@@ -138,44 +132,6 @@ public class StaticCompilationVisitor extends StaticTypeCheckingVisitor {
return new ClassNode[]{TYPECHECKED_CLASSNODE, COMPILESTATIC_CLASSNODE};
}
- public static boolean isStaticallyCompiled(final AnnotatedNode node) {
- if (node != null && node.getNodeMetaData(STATIC_COMPILE_NODE) != null) {
- return Boolean.TRUE.equals(node.getNodeMetaData(STATIC_COMPILE_NODE));
- }
- if (node instanceof MethodNode) {
- // GROOVY-6851, GROOVY-9151, GROOVY-10104
- if (!Boolean.TRUE.equals(node.getNodeMetaData(DEFAULT_PARAMETER_GENERATED))) {
- return isStaticallyCompiled(node.getDeclaringClass());
- }
- } else if (node instanceof ClassNode) {
- return isStaticallyCompiled(((ClassNode) node).getOuterClass());
- }
- return false;
- }
-
- private void addPrivateFieldAndMethodAccessors(final ClassNode node) {
- addPrivateBridgeMethods(node);
- addPrivateFieldsAccessors(node);
- for (Iterator<InnerClassNode> it = node.getInnerClasses(); it.hasNext(); ) {
- addPrivateFieldAndMethodAccessors(it.next());
- }
- }
-
- private void addDynamicOuterClassAccessorsCallback(final ClassNode outer) {
- if (outer != null) {
- if (!isStaticallyCompiled(outer) && outer.getNodeMetaData(DYNAMIC_OUTER_NODE_CALLBACK) == null) {
- outer.putNodeMetaData(DYNAMIC_OUTER_NODE_CALLBACK, (IPrimaryClassNodeOperation) (source, context, classNode) -> {
- if (classNode == outer) {
- addPrivateBridgeMethods(classNode);
- addPrivateFieldsAccessors(classNode);
- }
- });
- }
- // GROOVY-9328: apply to outer classes
- addDynamicOuterClassAccessorsCallback(outer.getOuterClass());
- }
- }
-
@Override
public void visitClass(final ClassNode node) {
boolean skip = shouldSkipClassNode(node);
@@ -183,24 +139,22 @@ public class StaticCompilationVisitor extends StaticTypeCheckingVisitor {
node.putNodeMetaData(MopWriter.Factory.class, StaticCompilationMopWriter.FACTORY);
}
- ClassNode previousClassNode = classNode; classNode = node;
-
- classNode.getInnerClasses().forEachRemaining(innerClassNode -> {
- boolean innerStaticCompile = !(skip || isSkippedInnerClass(innerClassNode));
- innerClassNode.putNodeMetaData(STATIC_COMPILE_NODE, Boolean.valueOf(innerStaticCompile));
+ node.getInnerClasses().forEachRemaining(innerClassNode -> {
+ boolean innerClassSkip = !(skip || isSkippedInnerClass(innerClassNode));
+ innerClassNode.putNodeMetaData(STATIC_COMPILE_NODE, Boolean.valueOf(innerClassSkip));
innerClassNode.putNodeMetaData(WriterControllerFactory.class, node.getNodeMetaData(WriterControllerFactory.class));
- if (innerStaticCompile && !anyMethodSkip(innerClassNode)) {
+ if (innerClassSkip && !anyMethodSkip(innerClassNode)) {
innerClassNode.putNodeMetaData(MopWriter.Factory.class, StaticCompilationMopWriter.FACTORY);
}
});
+
super.visitClass(node);
- addPrivateFieldAndMethodAccessors(node);
+
if (isStaticallyCompiled(node)) {
ClassNode outerClass = node.getOuterClass();
addDynamicOuterClassAccessorsCallback(outerClass);
}
-
- classNode = previousClassNode;
+ addPrivateFieldAndMethodAccessors(node); // includes inner types
}
private boolean anyMethodSkip(final ClassNode node) {
@@ -250,6 +204,215 @@ public class StaticCompilationVisitor extends StaticTypeCheckingVisitor {
visitConstructorOrMethod(node);
}
+ private AnnotatedNode getEnclosingDeclaration() {
+ ClassNode cn = typeCheckingContext.getEnclosingClassNode();
+ MethodNode mn = typeCheckingContext.getEnclosingMethod();
+ if (cn != null && cn.getEnclosingMethod() == mn) {
+ return cn;
+ } else {
+ return mn;
+ }
+ }
+
+ @Override
+ public void visitMethodCallExpression(final MethodCallExpression call) {
+ super.visitMethodCallExpression(call);
+
+ if (!isStaticallyCompiled(getEnclosingDeclaration())) return;
+
+ MethodNode target = call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET);
+ if (target != null) {
+ call.setMethodTarget(target);
+ memorizeInitialExpressions(target);
+ }
+
+ if (call.getMethodTarget() == null && call.getLineNumber() > 0) {
+ addError("Target method for method call expression hasn't been set", call);
+ }
+ }
+
+ @Override
+ public void visitConstructorCallExpression(final ConstructorCallExpression call) {
+ super.visitConstructorCallExpression(call);
+
+ if (call.isUsingAnonymousInnerClass() && call.getType().getNodeMetaData(StaticTypeCheckingVisitor.class) != null) {
+ ClassNode anonType = call.getType();
+ anonType.putNodeMetaData(STATIC_COMPILE_NODE, anonType.getEnclosingMethod().getNodeMetaData(STATIC_COMPILE_NODE));
+ anonType.putNodeMetaData(WriterControllerFactory.class, anonType.getOuterClass().getNodeMetaData(WriterControllerFactory.class));
+ }
+
+ if (!isStaticallyCompiled(getEnclosingDeclaration())) return;
+
+ MethodNode target = call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET);
+ if (target == null && call.getLineNumber() > 0) {
+ addError("Target constructor for constructor call expression hasn't been set", call);
+ } else if (target == null) { assert call.isSpecialCall(); // try to find target constructor
+ ClassNode enclosingClass = typeCheckingContext.getEnclosingMethod().getDeclaringClass();
+ ClassNode[] args = getArgumentTypes(InvocationWriter.makeArgumentList(call.getArguments()));
+ target = findMethodOrFail(call, call.isSuperCall() ? enclosingClass.getSuperClass() : enclosingClass, "<init>", args);
+ call.putNodeMetaData(DIRECT_METHOD_CALL_TARGET, target);
+ }
+ if (target != null) {
+ memorizeInitialExpressions(target);
+ }
+ }
+
+ @Override
+ public void visitForLoop(final ForStatement statement) {
+ super.visitForLoop(statement);
+ Expression collectionExpression = statement.getCollectionExpression();
+ if (!(collectionExpression instanceof ClosureListExpression)) {
+ ClassNode forLoopVariableType = statement.getVariableType();
+ ClassNode collectionType = getType(collectionExpression);
+ ClassNode componentType;
+ if (isWrapperCharacter(ClassHelper.getWrapper(forLoopVariableType)) && isStringType(collectionType)) {
+ // we allow auto-coercion here
+ componentType = forLoopVariableType;
+ } else {
+ componentType = inferLoopElementType(collectionType);
+ }
+ statement.getVariable().setType(componentType);
+ }
+ }
+
+ @Override
+ public void visitPropertyExpression(final PropertyExpression expression) {
+ super.visitPropertyExpression(expression);
+ Object dynamic = expression.getNodeMetaData(DYNAMIC_RESOLUTION);
+ if (dynamic != null) {
+ expression.getObjectExpression().putNodeMetaData(RECEIVER_OF_DYNAMIC_PROPERTY, dynamic);
+ }
+ }
+
+ @Override
+ protected boolean existsProperty(final PropertyExpression pexp, final boolean checkForReadOnly, final ClassCodeVisitorSupport visitor) {
+ Expression objectExpression = pexp.getObjectExpression();
+ ClassNode objectExpressionType = getType(objectExpression);
+ Reference<ClassNode> rType = new Reference<>(objectExpressionType);
+ ClassCodeVisitorSupport receiverMemoizer = new ClassCodeVisitorSupport() {
+ @Override
+ protected SourceUnit getSourceUnit() {
+ return null;
+ }
+
+ @Override
+ public void visitField(final FieldNode node) {
+ if (visitor != null) visitor.visitField(node);
+ ClassNode declaringClass = node.getDeclaringClass();
+ if (declaringClass != null) {
+ if (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(declaringClass, LIST_TYPE)) {
+ boolean spread = declaringClass.getDeclaredField(node.getName()) != node;
+ pexp.setSpreadSafe(spread);
+ }
+ rType.set(declaringClass);
+ }
+ }
+
+ @Override
+ public void visitMethod(final MethodNode node) {
+ if (visitor != null) visitor.visitMethod(node);
+ ClassNode declaringClass = node.getDeclaringClass();
+ if (declaringClass != null) {
+ if (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(declaringClass, LIST_TYPE)) {
+ List<MethodNode> properties = declaringClass.getDeclaredMethods(node.getName());
+ boolean spread = true;
+ for (MethodNode mn : properties) {
+ if (node == mn) {
+ spread = false;
+ break;
+ }
+ }
+ // it's no real property but a property of the component
+ pexp.setSpreadSafe(spread);
+ }
+ rType.set(declaringClass);
+ }
+ }
+
+ @Override
+ public void visitProperty(final PropertyNode node) {
+ if (visitor != null) visitor.visitProperty(node);
+ ClassNode declaringClass = node.getDeclaringClass();
+ if (declaringClass != null) {
+ if (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(declaringClass, LIST_TYPE)) {
+ List<PropertyNode> properties = declaringClass.getProperties();
+ boolean spread = true;
+ for (PropertyNode propertyNode : properties) {
+ if (propertyNode == node) {
+ spread = false;
+ break;
+ }
+ }
+ // it's no real property but a property of the component
+ pexp.setSpreadSafe(spread);
+ }
+ rType.set(declaringClass);
+ }
+ }
+ };
+
+ boolean exists = super.existsProperty(pexp, checkForReadOnly, receiverMemoizer);
+ if (exists) {
+ objectExpressionType = rType.get();
+ if (objectExpression.getNodeMetaData(PROPERTY_OWNER) == null) {
+ objectExpression.putNodeMetaData(PROPERTY_OWNER, objectExpressionType);
+ }
+ if (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(objectExpressionType, LIST_TYPE)) {
+ objectExpression.putNodeMetaData(COMPONENT_TYPE, inferComponentType(objectExpressionType, int_TYPE));
+ }
+ }
+ return exists;
+ }
+
+ @Override
+ protected MethodNode findMethodOrFail(final Expression expr, final ClassNode receiver, final String name, final ClassNode... args) {
+ MethodNode methodNode = super.findMethodOrFail(expr, receiver, name, args);
+ if (expr instanceof BinaryExpression && methodNode != null) {
+ expr.putNodeMetaData(BINARY_EXP_TARGET, new Object[]{methodNode, name});
+ }
+ return methodNode;
+ }
+
+ //--------------------------------------------------------------------------
+
+ public static boolean isStaticallyCompiled(final AnnotatedNode node) {
+ if (node != null && node.getNodeMetaData(STATIC_COMPILE_NODE) != null) {
+ return Boolean.TRUE.equals(node.getNodeMetaData(STATIC_COMPILE_NODE));
+ }
+ if (node instanceof MethodNode) {
+ // GROOVY-6851, GROOVY-9151, GROOVY-10104
+ if (!Boolean.TRUE.equals(node.getNodeMetaData(DEFAULT_PARAMETER_GENERATED))) {
+ return isStaticallyCompiled(node.getDeclaringClass());
+ }
+ } else if (node instanceof ClassNode) {
+ return isStaticallyCompiled(((ClassNode) node).getOuterClass());
+ }
+ return false;
+ }
+
+ private static void addDynamicOuterClassAccessorsCallback(final ClassNode outer) {
+ if (outer != null) {
+ if (!isStaticallyCompiled(outer) && outer.getNodeMetaData(DYNAMIC_OUTER_NODE_CALLBACK) == null) {
+ outer.putNodeMetaData(DYNAMIC_OUTER_NODE_CALLBACK, (IPrimaryClassNodeOperation) (source, context, classNode) -> {
+ if (classNode == outer) {
+ addPrivateBridgeMethods(classNode);
+ addPrivateFieldsAccessors(classNode);
+ }
+ });
+ }
+ // GROOVY-9328: apply to outer classes
+ addDynamicOuterClassAccessorsCallback(outer.getOuterClass());
+ }
+ }
+
+ private static void addPrivateFieldAndMethodAccessors(final ClassNode node) {
+ addPrivateBridgeMethods(node);
+ addPrivateFieldsAccessors(node);
+ for (Iterator<InnerClassNode> it = node.getInnerClasses(); it.hasNext(); ) {
+ addPrivateFieldAndMethodAccessors(it.next());
+ }
+ }
+
/**
* Adds special accessors and mutators for private fields so that inner classes can get/set them.
*/
@@ -414,164 +577,4 @@ public class StaticCompilationVisitor extends StaticTypeCheckingVisitor {
}
}
}
-
- @Override
- public void visitMethodCallExpression(final MethodCallExpression call) {
- super.visitMethodCallExpression(call);
-
- MethodNode target = call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET);
- if (target != null) {
- call.setMethodTarget(target);
- memorizeInitialExpressions(target);
- }
-
- if (call.getMethodTarget() == null && call.getLineNumber() > 0) {
- addError("Target method for method call expression hasn't been set", call);
- }
- }
-
- @Override
- public void visitConstructorCallExpression(final ConstructorCallExpression call) {
- super.visitConstructorCallExpression(call);
-
- if (call.isUsingAnonymousInnerClass() && call.getType().getNodeMetaData(StaticTypeCheckingVisitor.class) != null) {
- ClassNode anonType = call.getType();
- anonType.putNodeMetaData(STATIC_COMPILE_NODE, anonType.getEnclosingMethod().getNodeMetaData(STATIC_COMPILE_NODE));
- anonType.putNodeMetaData(WriterControllerFactory.class, anonType.getOuterClass().getNodeMetaData(WriterControllerFactory.class));
- }
-
- MethodNode target = call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET);
- if (target == null && call.getLineNumber() > 0) {
- addError("Target constructor for constructor call expression hasn't been set", call);
- } else if (target == null) {
- // try to find a target
- ArgumentListExpression argumentListExpression = InvocationWriter.makeArgumentList(call.getArguments());
- List<Expression> expressions = argumentListExpression.getExpressions();
- ClassNode[] args = new ClassNode[expressions.size()];
- for (int i = 0, n = args.length; i < n; i += 1) {
- args[i] = typeChooser.resolveType(expressions.get(i), classNode);
- }
- target = findMethodOrFail(call, call.isSuperCall() ? classNode.getSuperClass() : classNode, "<init>", args);
- call.putNodeMetaData(DIRECT_METHOD_CALL_TARGET, target);
- }
- if (target != null) {
- memorizeInitialExpressions(target);
- }
- }
-
- @Override
- public void visitForLoop(final ForStatement statement) {
- super.visitForLoop(statement);
- Expression collectionExpression = statement.getCollectionExpression();
- if (!(collectionExpression instanceof ClosureListExpression)) {
- ClassNode forLoopVariableType = statement.getVariableType();
- ClassNode collectionType = getType(collectionExpression);
- ClassNode componentType;
- if (isWrapperCharacter(ClassHelper.getWrapper(forLoopVariableType)) && isStringType(collectionType)) {
- // we allow auto-coercion here
- componentType = forLoopVariableType;
- } else {
- componentType = inferLoopElementType(collectionType);
- }
- statement.getVariable().setType(componentType);
- }
- }
-
- @Override
- protected MethodNode findMethodOrFail(final Expression expr, final ClassNode receiver, final String name, final ClassNode... args) {
- MethodNode methodNode = super.findMethodOrFail(expr, receiver, name, args);
- if (expr instanceof BinaryExpression && methodNode != null) {
- expr.putNodeMetaData(BINARY_EXP_TARGET, new Object[]{methodNode, name});
- }
- return methodNode;
- }
-
- @Override
- protected boolean existsProperty(final PropertyExpression pexp, final boolean checkForReadOnly, final ClassCodeVisitorSupport visitor) {
- Expression objectExpression = pexp.getObjectExpression();
- ClassNode objectExpressionType = getType(objectExpression);
- Reference<ClassNode> rType = new Reference<>(objectExpressionType);
- ClassCodeVisitorSupport receiverMemoizer = new ClassCodeVisitorSupport() {
- @Override
- protected SourceUnit getSourceUnit() {
- return null;
- }
-
- @Override
- public void visitField(final FieldNode node) {
- if (visitor != null) visitor.visitField(node);
- ClassNode declaringClass = node.getDeclaringClass();
- if (declaringClass != null) {
- if (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(declaringClass, LIST_TYPE)) {
- boolean spread = declaringClass.getDeclaredField(node.getName()) != node;
- pexp.setSpreadSafe(spread);
- }
- rType.set(declaringClass);
- }
- }
-
- @Override
- public void visitMethod(final MethodNode node) {
- if (visitor != null) visitor.visitMethod(node);
- ClassNode declaringClass = node.getDeclaringClass();
- if (declaringClass != null) {
- if (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(declaringClass, LIST_TYPE)) {
- List<MethodNode> properties = declaringClass.getDeclaredMethods(node.getName());
- boolean spread = true;
- for (MethodNode mn : properties) {
- if (node == mn) {
- spread = false;
- break;
- }
- }
- // it's no real property but a property of the component
- pexp.setSpreadSafe(spread);
- }
- rType.set(declaringClass);
- }
- }
-
- @Override
- public void visitProperty(final PropertyNode node) {
- if (visitor != null) visitor.visitProperty(node);
- ClassNode declaringClass = node.getDeclaringClass();
- if (declaringClass != null) {
- if (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(declaringClass, LIST_TYPE)) {
- List<PropertyNode> properties = declaringClass.getProperties();
- boolean spread = true;
- for (PropertyNode propertyNode : properties) {
- if (propertyNode == node) {
- spread = false;
- break;
- }
- }
- // it's no real property but a property of the component
- pexp.setSpreadSafe(spread);
- }
- rType.set(declaringClass);
- }
- }
- };
-
- boolean exists = super.existsProperty(pexp, checkForReadOnly, receiverMemoizer);
- if (exists) {
- objectExpressionType = rType.get();
- if (objectExpression.getNodeMetaData(PROPERTY_OWNER) == null) {
- objectExpression.putNodeMetaData(PROPERTY_OWNER, objectExpressionType);
- }
- if (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(objectExpressionType, LIST_TYPE)) {
- objectExpression.putNodeMetaData(COMPONENT_TYPE, inferComponentType(objectExpressionType, int_TYPE));
- }
- }
- return exists;
- }
-
- @Override
- public void visitPropertyExpression(final PropertyExpression expression) {
- super.visitPropertyExpression(expression);
- Object dynamic = expression.getNodeMetaData(DYNAMIC_RESOLUTION);
- if (dynamic != null) {
- expression.getObjectExpression().putNodeMetaData(RECEIVER_OF_DYNAMIC_PROPERTY, dynamic);
- }
- }
}
diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompilationTest.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompilationTest.groovy
index 46e5ffce77..a870a869cf 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompilationTest.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompilationTest.groovy
@@ -23,7 +23,7 @@ import org.codehaus.groovy.runtime.MethodClosure
import static org.codehaus.groovy.control.CompilerConfiguration.DEFAULT as config
-class StaticCompilationTest extends AbstractBytecodeTestCase {
+final class StaticCompilationTest extends AbstractBytecodeTestCase {
void testEmptyMethod() {
def bytecode = compile(method: 'm', '''
@@ -213,7 +213,7 @@ class StaticCompilationTest extends AbstractBytecodeTestCase {
])
}
-/* void testPlusPlus() {
+ void _testPlusPlus() {
assert compile(method: 'm', '''
@groovy.transform.CompileStatic
void m() {
@@ -225,7 +225,7 @@ class StaticCompilationTest extends AbstractBytecodeTestCase {
])
}
- void testMinusMinus() {
+ void _testMinusMinus() {
assert compile(method: 'm', '''
@groovy.transform.CompileStatic
void m() {
@@ -237,7 +237,7 @@ class StaticCompilationTest extends AbstractBytecodeTestCase {
])
}
- void testPlusEquals() {
+ void _testPlusEquals() {
assert compile(method: 'm', '''
@groovy.transform.CompileStatic
int m() {
@@ -253,7 +253,7 @@ class StaticCompilationTest extends AbstractBytecodeTestCase {
])
}
- void testPlusEqualsFromArgs() {
+ void _testPlusEqualsFromArgs() {
assert compile(method: 'm', '''
@groovy.transform.CompileStatic
void m(int i, int j) {
@@ -265,7 +265,7 @@ class StaticCompilationTest extends AbstractBytecodeTestCase {
"IADD",
"ISTORE"
])
- }*/
+ }
void testFlow() {
assert compile(method: 'm', '''
@@ -721,4 +721,51 @@ class StaticCompilationTest extends AbstractBytecodeTestCase {
])
assertScript(code)
}
+
+ // GROOVY-6925
+ void testOuterSCInnerSTC() {
+ assert compile(classNamePattern: 'C', method: 'test', '''
+ import groovy.transform.*
+ import static org.codehaus.groovy.control.CompilePhase.*
+ import static org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys.*
+
+ @CompileStatic
+ class C {
+ Object myObject() {
+ Integer.valueOf(1)
+ }
+ // TODO: package this into an annotation
+ @ASTTest(phase=CANONICALIZATION, value={
+ node.putNodeMetaData(STATIC_COMPILE_NODE, Boolean.FALSE)
+ })
+ void test() {
+ String myString = myObject()
+ }
+ }
+ ''').hasStrictSequence([
+ 'ALOAD 0',
+ 'INVOKEDYNAMIC invoke(LC;)Ljava/lang/Object;' // not INVOKEVIRTUAL
+ ])
+
+ def err = shouldFail '''
+ import groovy.transform.*
+ import static org.codehaus.groovy.control.CompilePhase.*
+ import static org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys.*
+
+ @CompileStatic
+ class C {
+ Object myObject() {
+ Integer.valueOf(1)
+ }
+ // TODO: package this into an annotation
+ @ASTTest(phase=CANONICALIZATION, value={
+ node.putNodeMetaData(STATIC_COMPILE_NODE, Boolean.FALSE)
+ })
+ void test() {
+ Number myNumber = myObject()
+ }
+ }
+ '''
+ assert err =~ /Cannot assign value of type java.lang.Object to variable of type java.lang.Number/
+ }
}