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 9793c9c2cc GROOVY-5619, GROOVY-5649: SC: remove legacy pathway
9793c9c2cc is described below
commit 9793c9c2cc25f812712ec52b0ff55917804fa729
Author: Eric Milles <[email protected]>
AuthorDate: Fri Sep 20 14:17:38 2024 -0500
GROOVY-5619, GROOVY-5649: SC: remove legacy pathway
---
...icTypesBinaryExpressionMultiTypeDispatcher.java | 139 +++------
.../groovy/transform/stc/GenericsSTCTest.groovy | 7 +-
.../sc/FieldsAndPropertiesStaticCompileTest.groovy | 347 ++++++++++-----------
.../asm/sc/StaticCompilationTestSupport.groovy | 56 ++--
.../asm/sc/StaticCompileConstructorsTest.groovy | 9 +-
5 files changed, 239 insertions(+), 319 deletions(-)
diff --git
a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
index 325c685814..7e0284cd01 100644
---
a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
+++
b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
@@ -35,7 +35,6 @@ import org.codehaus.groovy.ast.stmt.EmptyStatement;
import org.codehaus.groovy.ast.stmt.ForStatement;
import org.codehaus.groovy.ast.tools.WideningCategories;
import org.codehaus.groovy.classgen.asm.BinaryExpressionMultiTypeDispatcher;
-import org.codehaus.groovy.classgen.asm.BytecodeHelper;
import org.codehaus.groovy.classgen.asm.CompileStack;
import org.codehaus.groovy.classgen.asm.OperandStack;
import org.codehaus.groovy.classgen.asm.VariableSlotLoader;
@@ -46,7 +45,6 @@ import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import java.util.Map;
-import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import static org.apache.groovy.ast.tools.ExpressionUtils.isThisExpression;
@@ -91,8 +89,6 @@ import static org.objectweb.asm.Opcodes.ISUB;
import static org.objectweb.asm.Opcodes.LADD;
import static org.objectweb.asm.Opcodes.LCONST_1;
import static org.objectweb.asm.Opcodes.LSUB;
-import static org.objectweb.asm.Opcodes.PUTFIELD;
-import static org.objectweb.asm.Opcodes.PUTSTATIC;
/**
* A specialized version of the multi type binary expression dispatcher which
is aware of static compilation.
@@ -149,7 +145,8 @@ public class StaticTypesBinaryExpressionMultiTypeDispatcher
extends BinaryExpres
@Override
protected void evaluateBinaryExpressionWithAssignment(final String method,
final BinaryExpression expression) {
Expression leftExpression = expression.getLeftExpression();
- if (leftExpression instanceof PropertyExpression) {
+ if (leftExpression instanceof PropertyExpression
+ && !(leftExpression instanceof AttributeExpression)) {
PropertyExpression pexp = (PropertyExpression) leftExpression;
BinaryExpression expressionWithoutAssignment = binX(
@@ -171,8 +168,7 @@ public class StaticTypesBinaryExpressionMultiTypeDispatcher
extends BinaryExpres
expressionWithoutAssignment,
pexp.isSafe(),
pexp.isSpreadSafe(),
- pexp.isImplicitThis(),
- pexp instanceof AttributeExpression)) {
+ pexp.isImplicitThis())) {
return;
}
}
@@ -182,7 +178,8 @@ public class StaticTypesBinaryExpressionMultiTypeDispatcher
extends BinaryExpres
@Override
public void evaluateEqual(final BinaryExpression expression, final boolean
defineVariable) {
Expression leftExpression = expression.getLeftExpression();
- if (leftExpression instanceof PropertyExpression) {
+ if (leftExpression instanceof PropertyExpression
+ && !(leftExpression instanceof AttributeExpression)) {
PropertyExpression pexp = (PropertyExpression) leftExpression;
if (!defineVariable && makeSetProperty(
pexp.getObjectExpression(),
@@ -190,8 +187,7 @@ public class StaticTypesBinaryExpressionMultiTypeDispatcher
extends BinaryExpres
expression.getRightExpression(),
pexp.isSafe(),
pexp.isSpreadSafe(),
- pexp.isImplicitThis(),
- pexp instanceof AttributeExpression)) {
+ pexp.isImplicitThis())) {
return;
}
// GROOVY-5620: spread-safe operator on LHS is not supported
@@ -251,93 +247,56 @@ public class
StaticTypesBinaryExpressionMultiTypeDispatcher extends BinaryExpres
result.visit(controller.getAcg());
}
- private boolean makeSetProperty(final Expression receiver, final
Expression message, final Expression arguments, final boolean safe, final
boolean spreadSafe, final boolean implicitThis, final boolean isAttribute) {
+ private boolean makeSetProperty(final Expression receiver, final
Expression message, final Expression arguments, final boolean safe, final
boolean spreadSafe, final boolean implicitThis) {
var receiverType = controller.getTypeChooser().resolveType(receiver,
controller.getClassNode());
var thisReceiver = isThisExpression(receiver);
var propertyName = message.getText();
- if (isAttribute || (thisReceiver &&
receiverType.getDeclaredField(propertyName) != null)) {
- ClassNode current = receiverType;
- FieldNode fn = null;
- while (fn == null && current != null) {
- fn = current.getDeclaredField(propertyName);
- if (fn == null) {
- current = current.getSuperClass();
- }
+ String setterName = getSetterName(propertyName);
+ MethodNode setterMethod = receiverType.getSetterMethod(setterName,
false);
+ if (setterMethod != null) {
+ if ((thisReceiver &&
setterMethod.getDeclaringClass().equals(controller.getClassNode()))
+ || (!setterMethod.isPublic() && isOrImplements(receiverType,
ClassHelper.MAP_TYPE))) {
+ // this.x = ... should not use same-class setter
+ // that.x = ... should not use non-public setter for map
+ setterMethod = null;
+ } else { // GROOVY-11119
+ java.util.List<MethodNode> setters =
receiverType.getMethods(setterName);
+ setters.removeIf(s -> s.isAbstract() ||
s.getParameters().length != 1);
+ if (setters.size() > 1) setterMethod = null;
}
- if (fn != null && receiverType != current && !fn.isPublic()) {
- // check that direct access is allowed
- if (!fn.isProtected()) {
- return false;
- }
- if (!Objects.equals(receiverType.getPackageName(),
current.getPackageName())) {
- return false;
- }
- if (!fn.isStatic()) {
- receiver.visit(controller.getAcg());
- }
- arguments.visit(controller.getAcg());
- OperandStack operandStack = controller.getOperandStack();
- operandStack.doGroovyCast(fn.getOriginType());
- MethodVisitor mv = controller.getMethodVisitor();
- mv.visitFieldInsn(fn.isStatic() ? PUTSTATIC : PUTFIELD,
- BytecodeHelper.getClassInternalName(fn.getOwner()),
- propertyName,
- BytecodeHelper.getTypeDescription(fn.getOriginType()));
- operandStack.remove(fn.isStatic() ? 1 : 2);
- return true;
+ } else {
+ PropertyNode propertyNode = receiverType.getProperty(propertyName);
+ if (propertyNode != null && !propertyNode.isFinal()) {
+ setterMethod = new MethodNode(
+ setterName,
+ ACC_PUBLIC,
+ ClassHelper.VOID_TYPE,
+ new Parameter[]{new
Parameter(propertyNode.getOriginType(), "value")},
+ ClassNode.EMPTY_ARRAY,
+ EmptyStatement.INSTANCE
+ );
+ setterMethod.setDeclaringClass(receiverType);
+ setterMethod.setSynthetic(true);
}
}
+ if (setterMethod != null) {
+ Expression call = StaticPropertyAccessHelper.transformToSetterCall(
+ receiver,
+ setterMethod,
+ arguments,
+ implicitThis,
+ safe,
+ spreadSafe,
+ true, // to be replaced with a proper test whether a
return value should be used or not
+ message
+ );
+ call.visit(controller.getAcg());
+ return true;
+ }
- if (!isAttribute) {
- String setterName = getSetterName(propertyName);
- MethodNode setterMethod = receiverType.getSetterMethod(setterName,
false);
- if (setterMethod != null) {
- if ((thisReceiver &&
setterMethod.getDeclaringClass().equals(controller.getClassNode()))
- || (!setterMethod.isPublic() &&
isOrImplements(receiverType, ClassHelper.MAP_TYPE))) {
- // this.x = ... should not use same-class setter
- // that.x = ... should not use non-public setter for map
- setterMethod = null;
- } else { // GROOVY-11119
- java.util.List<MethodNode> setters =
receiverType.getMethods(setterName);
- setters.removeIf(s -> s.isAbstract() ||
s.getParameters().length != 1);
- if (setters.size() > 1) setterMethod = null;
- }
- } else {
- PropertyNode propertyNode =
receiverType.getProperty(propertyName);
- if (propertyNode != null && !propertyNode.isFinal()) {
- setterMethod = new MethodNode(
- setterName,
- ACC_PUBLIC,
- ClassHelper.VOID_TYPE,
- new Parameter[]{new
Parameter(propertyNode.getOriginType(), "value")},
- ClassNode.EMPTY_ARRAY,
- EmptyStatement.INSTANCE
- );
- setterMethod.setDeclaringClass(receiverType);
- setterMethod.setSynthetic(true);
- }
- }
- if (setterMethod != null) {
- Expression call =
StaticPropertyAccessHelper.transformToSetterCall(
- receiver,
- setterMethod,
- arguments,
- implicitThis,
- safe,
- spreadSafe,
- true, // to be replaced with a proper test whether a
return value should be used or not
- message
- );
- call.visit(controller.getAcg());
- return true;
- }
- if (thisReceiver && !controller.isInGeneratedFunction()) {
- receiverType = controller.getClassNode();
- }
- if (makeSetPrivateFieldWithBridgeMethod(receiver, receiverType,
propertyName, arguments, safe, spreadSafe, implicitThis)) {
- return true;
- }
+ if (makeSetPrivateFieldWithBridgeMethod(receiver, (thisReceiver &&
!controller.isInGeneratedFunction()) ? controller.getClassNode() :
receiverType, propertyName, arguments, safe, spreadSafe, implicitThis)) {
+ return true;
}
return false;
@@ -365,7 +324,7 @@ public class StaticTypesBinaryExpressionMultiTypeDispatcher
extends BinaryExpres
ClassNode classNode = controller.getClassNode();
if (field != null && field.isPrivate() &&
!receiverType.equals(classNode)
&&
(StaticInvocationWriter.isPrivateBridgeMethodsCallAllowed(receiverType,
classNode)
- ||
StaticInvocationWriter.isPrivateBridgeMethodsCallAllowed(classNode,receiverType)))
{
+ ||
StaticInvocationWriter.isPrivateBridgeMethodsCallAllowed(classNode,
receiverType))) {
Map<String, MethodNode> mutators =
receiverType.redirect().getNodeMetaData(PRIVATE_FIELDS_MUTATORS);
if (mutators != null) {
MethodNode methodNode = mutators.get(fieldName);
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 2d41d771a6..69d10e1dac 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -419,6 +419,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
test({k->c} as Input, {k->c} as State)
'''
+ tearDown(); setUp()
File parentDir = File.createTempDir()
config.with {
targetDirectory = File.createTempDir()
@@ -2036,6 +2037,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
test { Class clazz -> clazz.newInstance() }
'''
+ tearDown(); setUp()
File parentDir = File.createTempDir()
config.with {
targetDirectory = File.createTempDir()
@@ -4149,6 +4151,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
// GROOVY-9822
+ tearDown(); setUp()
File parentDir = File.createTempDir()
config.with {
targetDirectory = File.createTempDir()
@@ -5151,6 +5154,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
// GROOVY-9821
+ tearDown(); setUp()
for (op in ['.', '?.', '*.']) {
File parentDir = File.createTempDir()
config.with {
@@ -5216,8 +5220,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
assert n == 1
'''
- //
-
+ tearDown(); setUp()
File parentDir = File.createTempDir()
config.with {
targetDirectory = File.createTempDir()
diff --git
a/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy
b/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy
index df648e7324..4e2b2d5bd5 100644
---
a/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy
+++
b/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy
@@ -50,11 +50,11 @@ final class FieldsAndPropertiesStaticCompileTest extends
FieldsAndPropertiesSTCT
assert d.time == 1
'''
- assert astTrees.values().any {
- it.toString().contains 'INVOKEVIRTUAL java/util/Date.setTime (J)V'
- }
+ String typeName = astTrees.keySet().first()
+ assert astTrees[typeName][1].contains('INVOKEVIRTUAL
java/util/Date.setTime (J)V')
}
+ // GROOVY-5619
void testUseDirectWriteFieldAccess() {
assertScript '''
class C {
@@ -76,6 +76,7 @@ final class FieldsAndPropertiesStaticCompileTest extends
FieldsAndPropertiesSTCT
assert d.isSetterCalled() == false
assert d.x == 2
'''
+ assert astTrees['C'][1].contains('PUTFIELD C.x')
assert astTrees['D'][1].contains('PUTFIELD C.x')
}
@@ -193,7 +194,7 @@ final class FieldsAndPropertiesStaticCompileTest extends
FieldsAndPropertiesSTCT
}
void testReadFieldFromSameClass() {
- ['', 'public', 'private', 'protected',
'@groovy.transform.PackageScope'].each { mod ->
+ for (mod in ['', 'public', 'private', 'protected',
'@groovy.transform.PackageScope']) {
assertScript """
class C {
$mod int x
@@ -203,13 +204,13 @@ final class FieldsAndPropertiesStaticCompileTest extends
FieldsAndPropertiesSTCT
}
assert new C().m() == 0
"""
- def a = astTrees['C'][1]
- assert (a =~ 'GETFIELD C.x').collect().size() == mod.empty ? 2 : 1
+ String c = astTrees['C'][1]
+ assert (c =~ 'GETFIELD C.x').collect().size() == mod.empty ? 2 : 1
}
}
void testWriteFieldFromSameClass() {
- ['', 'public', 'private', 'protected',
'@groovy.transform.PackageScope'].each { mod ->
+ for (mod in ['', 'public', 'private', 'protected',
'@groovy.transform.PackageScope']) {
assertScript """
class C {
$mod int x
@@ -220,13 +221,13 @@ final class FieldsAndPropertiesStaticCompileTest extends
FieldsAndPropertiesSTCT
}
new C().m() == 5
"""
- def a = astTrees['C'][1]
- assert (a =~ 'PUTFIELD C.x').collect().size() == mod.empty ? 2 : 1
+ String c = astTrees['C'][1]
+ assert (c =~ 'PUTFIELD C.x').collect().size() == mod.empty ? 2 : 1
}
}
void testReadFieldFromSuperClass() {
- ['public', 'protected', '@groovy.transform.PackageScope'].each { mod ->
+ for (mod in ['public', 'protected', '@groovy.transform.PackageScope'])
{
assertScript """
class C {
$mod int x
@@ -238,7 +239,7 @@ final class FieldsAndPropertiesStaticCompileTest extends
FieldsAndPropertiesSTCT
}
assert new D().m() == 0
"""
- def d = astTrees['D'][1]
+ String d = astTrees['D'][1]
assert d.contains('GETFIELD C.x')
}
}
@@ -260,9 +261,9 @@ final class FieldsAndPropertiesStaticCompileTest extends
FieldsAndPropertiesSTCT
}
assert new D().m() == 0
'''
- def b = astTrees['D'][1]
- assert b.contains('GETFIELD p/C.x')
- assert !b.contains('INVOKEINTERFACE
groovy/lang/GroovyObject.getProperty')
+ String d = astTrees['D'][1]
+ assert d.contains('GETFIELD p/C.x')
+ assert !d.contains('INVOKEINTERFACE
groovy/lang/GroovyObject.getProperty')
}
// GROOVY-9791
@@ -282,13 +283,13 @@ final class FieldsAndPropertiesStaticCompileTest extends
FieldsAndPropertiesSTCT
}
assert D.m() == 0
'''
- def d = astTrees['D'][1]
+ String d = astTrees['D'][1]
assert d.contains('GETSTATIC D.x')
assert !d.contains('INVOKESTATIC
org/codehaus/groovy/runtime/ScriptBytecodeAdapter.getGroovyObjectProperty')
}
void testReadPropertyFromSuperClass() {
- ['', 'public', 'private', 'protected',
'@groovy.transform.PackageScope'].each { mod ->
+ for (mod in ['', 'public', 'private', 'protected',
'@groovy.transform.PackageScope']) {
assertScript """
class C {
$mod int x
@@ -301,7 +302,7 @@ final class FieldsAndPropertiesStaticCompileTest extends
FieldsAndPropertiesSTCT
}
assert new D().m() == 0
"""
- def d = astTrees['D'][1]
+ String d = astTrees['D'][1]
assert !d.contains('GETFIELD C.x') : 'no GETFIELD in D'
assert d.contains('INVOKEVIRTUAL D.getX') : 'getX() in D'
}
@@ -327,7 +328,7 @@ final class FieldsAndPropertiesStaticCompileTest extends
FieldsAndPropertiesSTCT
d.m()
assert d.isGetterCalled() == false
'''
- def d = astTrees['D'][1]
+ String d = astTrees['D'][1]
assert d.contains('GETFIELD C.x')
}
@@ -545,45 +546,36 @@ final class FieldsAndPropertiesStaticCompileTest extends
FieldsAndPropertiesSTCT
}
void testCallSetterAsPropertyWithinFinallyBlockShouldNotThrowVerifyError()
{
- try {
- assertScript '''
- class C {
- void setOut(int a) {}
- }
- def c = new C()
- try {
- } finally {
- c.out = 1
- }
- '''
- } finally {
- assert astTrees.values().any {
- it.toString().contains 'INVOKEVIRTUAL C.setOut (I)V'
+ assertScript '''
+ class C {
+ void setOut(int a) {}
}
- }
+ def c = new C()
+ try {
+ } finally {
+ c.out = 1
+ }
+ '''
+ String typeName = astTrees.keySet().minus('C').first()
+ assert astTrees[typeName][1].contains('INVOKEVIRTUAL C.setOut (I)V')
}
void
testCallMultiSetterAsPropertyWithinFinallyBlockShouldNotThrowVerifyError() {
- try {
- assertScript '''
- class C {
- void setOut(int a) {}
- void setOut(String a) {}
- }
- def c = new C()
- try {
- } finally {
- c.out = 1
- c.out = 'foo'
- }
- '''
- } finally {
- assert astTrees.values().any {
- def code = it.toString()
- code.contains('INVOKEVIRTUAL C.setOut (I)V')
- && code.contains('INVOKEVIRTUAL C.setOut
(Ljava/lang/String;)V')
+ assertScript '''
+ class C {
+ void setOut(int a) {}
+ void setOut(String a) {}
}
- }
+ def c = new C()
+ try {
+ } finally {
+ c.out = 1
+ c.out = 'foo'
+ }
+ '''
+ String typeName = astTrees.keySet().minus('C').first()
+ assert astTrees[typeName][1].contains('INVOKEVIRTUAL C.setOut (I)V')
+ assert astTrees[typeName][1].contains('INVOKEVIRTUAL C.setOut
(Ljava/lang/String;)V')
}
// GROOVY-7698
@@ -624,179 +616,154 @@ final class FieldsAndPropertiesStaticCompileTest
extends FieldsAndPropertiesSTCT
}
new Bar().test()
'''
-
- def bar = astTrees['Bar'][1]
+ String bar = astTrees['Bar'][1]
assert bar.contains('INVOKEVIRTUAL Bar.setX (Ljava/lang/Long;)V')
assert bar.contains('INVOKEVIRTUAL Bar.setX (Ljava/util/Date;)V')
assert !bar.contains('INVOKESTATIC
org/codehaus/groovy/runtime/ScriptBytecodeAdapter.setGroovyObjectProperty ')
}
void testPrivateFieldMutationInClosureUsesBridgeMethod() {
- try {
- assertScript '''
- class Foo {
- private String s
- Closure c = { this.s = 'abc' }
-
- void test() {
- c()
- assert s == 'abc'
- }
+ assertScript '''
+ class Foo {
+ private String s
+ Closure c = { this.s = 'abc' }
+
+ void test() {
+ c()
+ assert s == 'abc'
}
- new Foo().test()
- '''
- } finally {
- assert astTrees['Foo$_closure1'][1].contains('INVOKESTATIC
Foo.pfaccess$00 (LFoo;Ljava/lang/String;)Ljava/lang/String')
- }
+ }
+ new Foo().test()
+ '''
+ assert astTrees['Foo$_closure1'][1].contains('INVOKESTATIC
Foo.pfaccess$00 (LFoo;Ljava/lang/String;)Ljava/lang/String')
}
void testImplicitPrivateFieldMutationInClosureUsesBridgeMethod() {
- try {
- assertScript '''
- class Foo {
- private String s
- Closure c = { s = 'abc' }
-
- String test() {
- c()
- assert s == 'abc'
- }
+ assertScript '''
+ class Foo {
+ private String s
+ Closure c = { s = 'abc' }
+
+ String test() {
+ c()
+ assert s == 'abc'
}
- new Foo().test()
- '''
- } finally {
- assert astTrees['Foo$_closure1'][1].contains('INVOKESTATIC
Foo.pfaccess$00 (LFoo;Ljava/lang/String;)Ljava/lang/String')
- }
+ }
+ new Foo().test()
+ '''
+ assert astTrees['Foo$_closure1'][1].contains('INVOKESTATIC
Foo.pfaccess$00 (LFoo;Ljava/lang/String;)Ljava/lang/String')
}
void testPrivateStaticFieldMutationInClosureUsesBridgeMethod() {
- try {
- assertScript '''
- class Foo {
- private static String s
- Closure c = { s = 'abc' }
-
- String test() {
- c()
- assert s == 'abc'
- }
+ assertScript '''
+ class Foo {
+ private static String s
+ Closure c = { s = 'abc' }
+
+ String test() {
+ c()
+ assert s == 'abc'
}
- new Foo().test()
- '''
- } finally {
- assert astTrees['Foo$_closure1'][1].contains('INVOKESTATIC
Foo.pfaccess$00 (LFoo;Ljava/lang/String;)Ljava/lang/String')
- }
+ }
+ new Foo().test()
+ '''
+ assert astTrees['Foo$_closure1'][1].contains('INVOKESTATIC
Foo.pfaccess$00 (LFoo;Ljava/lang/String;)Ljava/lang/String')
}
void testPrivateFieldMutationInAICUsesBridgeMethod() {
- try {
- assertScript '''
- class C {
- private int x
- void test() {
- def aic = new Runnable() { void run() { C.this.x = 666
} }
- aic.run()
- assert x == 666
- }
+ assertScript '''
+ class C {
+ private int x
+ void test() {
+ def aic = new Runnable() { void run() { C.this.x = 666 } }
+ aic.run()
+ assert x == 666
}
- new C().test()
- '''
- } finally {
- assert astTrees['C$1'][1].contains('INVOKESTATIC C.pfaccess$00
(LC;I)I')
- }
+ }
+ new C().test()
+ '''
+ assert astTrees['C$1'][1].contains('INVOKESTATIC C.pfaccess$00
(LC;I)I')
}
void testImplicitPrivateFieldMutationInAICUsesBridgeMethod() {
- try {
- assertScript '''
- class C {
- private int x
- void test() {
- def aic = new Runnable() { void run() { x = 666 } }
- aic.run()
- assert x == 666
- }
+ assertScript '''
+ class C {
+ private int x
+ void test() {
+ def aic = new Runnable() { void run() { x = 666 } }
+ aic.run()
+ assert x == 666
}
- new C().test()
- '''
- } finally {
- assert astTrees['C$1'][1].contains('INVOKESTATIC C.pfaccess$00
(LC;I)I')
- }
+ }
+ new C().test()
+ '''
+ assert astTrees['C$1'][1].contains('INVOKESTATIC C.pfaccess$00
(LC;I)I')
}
void testPrivateStaticFieldMutationInAICUsesBridgeMethod() {
- try {
- assertScript '''
- class C {
- private static int x
- void test() {
- def aic = new Runnable() { void run() { x = 666 } }
- aic.run()
- assert x == 666
- }
+ assertScript '''
+ class C {
+ private static int x
+ void test() {
+ def aic = new Runnable() { void run() { x = 666 } }
+ aic.run()
+ assert x == 666
}
- new C().test()
- '''
- } finally {
- assert astTrees['C$1'][1].contains('INVOKESTATIC C.pfaccess$00
(LC;I)I')
- }
+ }
+ new C().test()
+ '''
+ assert astTrees['C$1'][1].contains('INVOKESTATIC C.pfaccess$00
(LC;I)I')
}
void testMultiplePrivateFieldMutatorBridgeMethods() {
- try {
- assertScript '''
- class C {
- private int x
- private String y
- Closure mutate = { x = 1; y = 'abc' }
-
- void test() {
- mutate()
- assert x == 1
- assert y == 'abc'
- }
+ assertScript '''
+ class C {
+ private int x
+ private String y
+ Closure mutate = { x = 1; y = 'abc' }
+
+ void test() {
+ mutate()
+ assert x == 1
+ assert y == 'abc'
}
- new C().test()
- '''
- } finally {
- assert astTrees['C$_closure1'][1].contains('INVOKESTATIC
C.pfaccess$00 (LC;I)I')
- assert astTrees['C$_closure1'][1].contains('INVOKESTATIC
C.pfaccess$01 (LC;Ljava/lang/String;)Ljava/lang/String;')
- }
+ }
+ new C().test()
+ '''
+ assert astTrees['C$_closure1'][1].contains('INVOKESTATIC C.pfaccess$00
(LC;I)I')
+ assert astTrees['C$_closure1'][1].contains('INVOKESTATIC C.pfaccess$01
(LC;Ljava/lang/String;)Ljava/lang/String;')
}
void testPrivateFieldBridgeMethodsAreGeneratedAsNecessary() {
- try {
- assertScript '''
- class C {
- private int accessed = 0
- private String mutated
- private String accessedAndMutated = ''
- Closure cl = {
- println accessed
- mutated = 'abc'
- println accessedAndMutated
- accessedAndMutated = 'def'
- }
- void test() {
- cl()
- assert mutated == 'abc'
- assert accessedAndMutated == 'def'
- }
+ assertScript '''
+ class C {
+ private int accessed = 0
+ private String mutated
+ private String accessedAndMutated = ''
+ Closure cl = {
+ println accessed
+ mutated = 'abc'
+ println accessedAndMutated
+ accessedAndMutated = 'def'
}
- new C().test()
- '''
- } finally {
- def dump = astTrees['C'][1]
- assert dump.contains('pfaccess$0') // accessor bridge method for
'accessed'
- assert !dump.contains('pfaccess$00') // no mutator bridge method
for 'accessed'
- assert dump.contains('pfaccess$01') // mutator bridge method for
'mutated'
- assert dump.contains('pfaccess$1') // accessor bridge method for
'mutated' -- GROOVY-9385
- assert dump.contains('pfaccess$2') // accessor bridge method for
'accessedAndMutated'
- assert dump.contains('pfaccess$02') // mutator bridge method for
'accessedAndMutated'
- dump = astTrees['C$_closure1'][1]
- assert dump.contains('INVOKESTATIC C.pfaccess$2
(LC;)Ljava/lang/String;')
- assert dump.contains('INVOKESTATIC C.pfaccess$02
(LC;Ljava/lang/String;)Ljava/lang/String;')
- }
+ void test() {
+ cl()
+ assert mutated == 'abc'
+ assert accessedAndMutated == 'def'
+ }
+ }
+ new C().test()
+ '''
+ String dump = astTrees['C'][1]
+ assert dump.contains('pfaccess$0') // accessor bridge method for
'accessed'
+ assert !dump.contains('pfaccess$00') // no mutator bridge method for
'accessed'
+ assert dump.contains('pfaccess$01') // mutator bridge method for
'mutated'
+ assert dump.contains('pfaccess$1') // accessor bridge method for
'mutated' -- GROOVY-9385
+ assert dump.contains('pfaccess$2') // accessor bridge method for
'accessedAndMutated'
+ assert dump.contains('pfaccess$02') // mutator bridge method for
'accessedAndMutated'
+ dump = astTrees['C$_closure1'][1]
+ assert dump.contains('INVOKESTATIC C.pfaccess$2
(LC;)Ljava/lang/String;')
+ assert dump.contains('INVOKESTATIC C.pfaccess$02
(LC;Ljava/lang/String;)Ljava/lang/String;')
}
// GROOVY-8369
diff --git
a/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompilationTestSupport.groovy
b/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompilationTestSupport.groovy
index 7b67973346..b5de1c6022 100644
---
a/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompilationTestSupport.groovy
+++
b/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompilationTestSupport.groovy
@@ -27,30 +27,25 @@ import org.codehaus.groovy.control.SourceUnit
import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
import org.codehaus.groovy.control.customizers.CompilationCustomizer
import org.codehaus.groovy.control.customizers.ImportCustomizer
-import org.codehaus.groovy.tools.GroovyClass
import org.objectweb.asm.ClassReader
import org.objectweb.asm.util.CheckClassAdapter
import java.security.CodeSource
/**
- * A mixin class which can be used to transform a static type checking test
case into a
- * static compilation test case.
- *
- * On the beginning of a test method, it initializes a property which is
available
- * to the developer for additional tests:
- * <ul>
- * <li>astTrees: a map which has for key the names of classes generated in
assertScript and an array of length 2
- * as a value, for which the first element is the generated AST tree of
the class, and the second element is
- * the result of the ASM check class adapter string, which can be used to
verify generated bytecode.</li>
- * </ul>
+ * A mixin class which can be used to transform a static type checking test
case
+ * into a static compilation test case.
+ * <p>
+ * For each test method, it initializes the property {@code astTrees}, which is
+ * available to the developer for additional checks. This property provides the
+ * AST and the result of the ASM check class adapter (bytecode) for each class.
*/
trait StaticCompilationTestSupport {
- Map<String, Object[]> astTrees
- CustomCompilationUnit compilationUnit
+ Map<String, Tuple2<ClassNode, String>> astTrees
+ CompilationUnit compilationUnit
void extraSetup() {
- astTrees = [:]
+ astTrees = new HashMap<>()
config = new CompilerConfiguration()
config.addCompilationCustomizers(
new ImportCustomizer().tap {
@@ -102,15 +97,7 @@ trait StaticCompilationTestSupport {
@Override
protected CompilationUnit createCompilationUnit(final
CompilerConfiguration config, final CodeSource source) {
- def cu = new CustomCompilationUnit(config, source, this)
- testCase.compilationUnit = cu
- return cu
- }
- }
-
- static class CustomCompilationUnit extends CompilationUnit {
- CustomCompilationUnit(final CompilerConfiguration configuration, final
CodeSource security, final GroovyClassLoader loader) {
- super(configuration, security, loader)
+ testCase.compilationUnit = new CompilationUnit(config, source,
this)
}
}
@@ -125,17 +112,22 @@ trait StaticCompilationTestSupport {
@Override
void call(final SourceUnit source, final GeneratorContext context,
final ClassNode classNode) {
def unit = testCase.compilationUnit
- if (!unit) return
- unit.classes.each { GroovyClass groovyClass ->
- StringWriter stringWriter = new StringWriter()
+ if (unit) {
+ def groovyClass = unit.classes.find { it.name ==
classNode.name }
+ def stringWriter = new StringWriter()
try {
- ClassReader cr = new ClassReader(groovyClass.bytes)
- CheckClassAdapter.verify(cr, source.getClassLoader(),
true, new PrintWriter(stringWriter))
- } catch (Throwable e) {
- // not a problem
- e.printStackTrace(new PrintWriter(stringWriter))
+ CheckClassAdapter.verify(new
ClassReader(groovyClass.bytes),
+ source.classLoader, true, new
PrintWriter(stringWriter))
+ } catch (Throwable t) {
+ t.printStackTrace(new PrintWriter(stringWriter))
+ }
+ testCase.astTrees[groovyClass.name] = new Tuple2<>(classNode,
stringWriter.toString())
+
+ for (innerClass in classNode.innerClasses) { // collect
closures, etc.
+ if
(context.compileUnit.getGeneratedInnerClass(innerClass.name)) {
+ this.call(source, context, innerClass)
+ }
}
- testCase.astTrees[groovyClass.name] = [classNode,
stringWriter.toString()] as Object[]
}
}
}
diff --git
a/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompileConstructorsTest.groovy
b/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompileConstructorsTest.groovy
index 3e6d03b2d2..89569cd829 100644
---
a/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompileConstructorsTest.groovy
+++
b/src/test/org/codehaus/groovy/classgen/asm/sc/StaticCompileConstructorsTest.groovy
@@ -100,10 +100,9 @@ class StaticCompileConstructorsTest extends
ConstructorsSTCTest implements Stati
@Override
void testMapStyleConstructorWithOverloadedSetterName() {
super.testMapStyleConstructorWithOverloadedSetterName()
-
- String bytecode = astTrees.values()[1][1]
- assert bytecode.contains('INVOKEVIRTUAL C.setP (Ljava/lang/String;)V')
- assert bytecode.contains('INVOKEVIRTUAL C.setP
(Ljava/util/regex/Pattern;)V')
- assert !bytecode.contains('INVOKESTATIC
org/codehaus/groovy/runtime/ScriptBytecodeAdapter.setGroovyObjectProperty')
+ String asserts = astTrees.find{ it.key != 'C' }.value[1]
+ assert asserts.contains('INVOKEVIRTUAL C.setP (Ljava/lang/String;)V')
+ assert asserts.contains('INVOKEVIRTUAL C.setP
(Ljava/util/regex/Pattern;)V')
+ assert !asserts.contains('INVOKESTATIC
org/codehaus/groovy/runtime/ScriptBytecodeAdapter.setGroovyObjectProperty')
}
}