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 c42a509f64 GROOVY-10687: SC: replace access bridge methods with direct field access c42a509f64 is described below commit c42a509f64616f8ca8c97d37e79d713dfb99a8ac Author: Eric Milles <eric.mil...@thomsonreuters.com> AuthorDate: Mon Sep 23 14:17:35 2024 -0500 GROOVY-10687: SC: replace access bridge methods with direct field access --- .../groovy/classgen/AsmClassGenerator.java | 40 +++++++- .../codehaus/groovy/classgen/GeneratorContext.java | 5 + ...icTypesBinaryExpressionMultiTypeDispatcher.java | 62 ++++-------- .../classgen/asm/sc/StaticTypesCallSiteWriter.java | 20 ++-- .../sc/StaticCompilationMetadataKeys.java | 29 ++++-- .../transform/sc/StaticCompilationVisitor.java | 1 + .../PropertyExpressionTransformer.java | 27 ++++++ .../VariableExpressionTransformer.java | 8 +- .../transform/stc/StaticTypeCheckingVisitor.java | 3 +- .../groovy/classgen/asm/NestHostTests.groovy | 15 +++ .../sc/FieldsAndPropertiesStaticCompileTest.groovy | 108 +++++++++++---------- 11 files changed, 200 insertions(+), 118 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java index da58703feb..d4410759c9 100644 --- a/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java +++ b/src/main/java/org/codehaus/groovy/classgen/AsmClassGenerator.java @@ -27,6 +27,7 @@ import org.codehaus.groovy.ast.AnnotatedNode; import org.codehaus.groovy.ast.AnnotationNode; import org.codehaus.groovy.ast.ClassHelper; import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.CodeVisitorSupport; import org.codehaus.groovy.ast.ConstructorNode; import org.codehaus.groovy.ast.FieldNode; import org.codehaus.groovy.ast.GenericsType; @@ -138,6 +139,7 @@ import static org.apache.groovy.ast.tools.ClassNodeUtils.getNestHost; import static org.apache.groovy.ast.tools.ExpressionUtils.isNullConstant; import static org.apache.groovy.ast.tools.ExpressionUtils.isSuperExpression; import static org.codehaus.groovy.ast.ClassHelper.isClassType; +import static org.codehaus.groovy.ast.ClassHelper.isFunctionalInterface; import static org.codehaus.groovy.ast.ClassHelper.isObjectType; import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveBoolean; import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveByte; @@ -381,6 +383,7 @@ public class AsmClassGenerator extends ClassGenerator { // GROOVY-10687 if (classNode.getOuterClass() == null && classNode.getInnerClasses().hasNext()) { makeNestmateEntries(classNode); + moreNestmateEntries(classNode); } // GROOVY-4649, GROOVY-6750, GROOVY-6808 for (Iterator<InnerClassNode> it = classNode.getInnerClasses(); it.hasNext(); ) { @@ -460,6 +463,38 @@ public class AsmClassGenerator extends ClassGenerator { } } + private void moreNestmateEntries(final ClassNode classNode) { + // edge case: nest host closures-within-closures + int[] n = {this.context.getClosureClassIndex()}; + for (ClassNode innerClass : getInnerClasses()) { + if (innerClass instanceof InterfaceHelperClassNode) continue; + var doCall = innerClass.getMethods().get(0); + doCall.getCode().visit(new CodeVisitorSupport() { + private String name = BytecodeHelper.getClassInternalName(innerClass); + private void visitNested(final String kind, final ClosureExpression expr) { + String save = name; + name += "$_" + kind + n[0]++; + classVisitor.visitNestMember(name); + super.visitClosureExpression(expr); + name = save; + } + @Override + public void visitClosureExpression(final ClosureExpression expression) { + visitNested("closure", expression); + } + @Override + public void visitLambdaExpression(final LambdaExpression expression) { + if (Boolean.TRUE.equals(innerClass.getNodeMetaData(org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys.STATIC_COMPILE_NODE)) + && isFunctionalInterface(expression.getNodeMetaData(org.codehaus.groovy.transform.stc.StaticTypesMarker.PARAMETER_TYPE))) { + visitNested("lambda", expression); + } else { + super.visitLambdaExpression(expression); + } + } + }); + } + } + /* * See http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.6-300-D.2-5 * for what flags are allowed depending on the fact we are writing the inner class table @@ -1063,8 +1098,9 @@ public class AsmClassGenerator extends ClassGenerator { // any member is accessible from the declaring class if (accessingClass.equals(declaringClass)) return true; - // a private member isn't accessible beyond the declaring class - if (Modifier.isPrivate(modifiers)) return false; + // a private member is accessible from any nestmates + if (Modifier.isPrivate(modifiers)) + return getNestHost(accessingClass).equals(getNestHost(declaringClass)); // a protected member is accessible from any subclass of the declaring class if (Modifier.isProtected(modifiers) && accessingClass.isDerivedFrom(declaringClass)) return true; diff --git a/src/main/java/org/codehaus/groovy/classgen/GeneratorContext.java b/src/main/java/org/codehaus/groovy/classgen/GeneratorContext.java index fcd115a280..0a0b78b270 100644 --- a/src/main/java/org/codehaus/groovy/classgen/GeneratorContext.java +++ b/src/main/java/org/codehaus/groovy/classgen/GeneratorContext.java @@ -43,6 +43,11 @@ public class GeneratorContext { this.innerClassIdx = innerClassOffset; } + // for ACG nestmate determination ! + int getClosureClassIndex() { + return closureClassIdx; + } + public int getNextInnerClassIdx() { return innerClassIdx++; } 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 7e0284cd01..45c89d41df 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 @@ -48,23 +48,20 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import static org.apache.groovy.ast.tools.ExpressionUtils.isThisExpression; -import static org.codehaus.groovy.ast.ClassHelper.CLOSURE_TYPE; +import static org.codehaus.groovy.ast.ClassHelper.isNumberType; import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveChar; import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveDouble; import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveFloat; import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveLong; +import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveType; import static org.codehaus.groovy.ast.tools.GeneralUtils.args; import static org.codehaus.groovy.ast.tools.GeneralUtils.binX; -import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX; import static org.codehaus.groovy.ast.tools.GeneralUtils.callX; -import static org.codehaus.groovy.ast.tools.GeneralUtils.castX; -import static org.codehaus.groovy.ast.tools.GeneralUtils.classX; import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX; import static org.codehaus.groovy.ast.tools.GeneralUtils.declX; import static org.codehaus.groovy.ast.tools.GeneralUtils.getSetterName; import static org.codehaus.groovy.ast.tools.GeneralUtils.isOrImplements; import static org.codehaus.groovy.ast.tools.GeneralUtils.nullX; -import static org.codehaus.groovy.ast.tools.GeneralUtils.propX; import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt; import static org.codehaus.groovy.ast.tools.GeneralUtils.varX; import static org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys.PRIVATE_FIELDS_MUTATORS; @@ -96,8 +93,6 @@ import static org.objectweb.asm.Opcodes.LSUB; */ public class StaticTypesBinaryExpressionMultiTypeDispatcher extends BinaryExpressionMultiTypeDispatcher { - private static final MethodNode CLOSURE_GETTHISOBJECT_METHOD = CLOSURE_TYPE.getMethod("getThisObject", Parameter.EMPTY_ARRAY); - private final AtomicInteger labelCounter = new AtomicInteger(); public StaticTypesBinaryExpressionMultiTypeDispatcher(final WriterController wc) { @@ -116,7 +111,7 @@ public class StaticTypesBinaryExpressionMultiTypeDispatcher extends BinaryExpres } ClassNode top = controller.getOperandStack().getTopOperand(); - if (ClassHelper.isPrimitiveType(top) && (ClassHelper.isNumberType(top) || isPrimitiveChar(top))) { + if (isPrimitiveType(top) && (isNumberType(top) || isPrimitiveChar(top))) { MethodVisitor mv = controller.getMethodVisitor(); visitInsnByType(top, mv, ICONST_1, LCONST_1, FCONST_1, DCONST_1); if ("next".equals(method)) { @@ -303,39 +298,24 @@ public class StaticTypesBinaryExpressionMultiTypeDispatcher extends BinaryExpres } private boolean makeSetPrivateFieldWithBridgeMethod(final Expression receiver, final ClassNode receiverType, final String fieldName, final Expression arguments, final boolean safe, final boolean spreadSafe, final boolean implicitThis) { - FieldNode field = receiverType.getField(fieldName); - ClassNode outerClass = receiverType.getOuterClass(); - if (field == null && implicitThis && outerClass != null && !receiverType.isStaticClass()) { - Expression pexp; - if (controller.isInGeneratedFunction()) { - MethodCallExpression mce = callThisX("getThisObject"); - mce.setImplicitThis(true); - mce.setMethodTarget(CLOSURE_GETTHISOBJECT_METHOD); - mce.putNodeMetaData(INFERRED_TYPE, controller.getThisType()); - pexp = castX(controller.getThisType(), mce); - } else { - pexp = propX(classX(outerClass), "this"); - ((PropertyExpression) pexp).setImplicitThis(true); - } - pexp.putNodeMetaData(INFERRED_TYPE, outerClass); - pexp.setSourcePosition(receiver); - return makeSetPrivateFieldWithBridgeMethod(pexp, outerClass, fieldName, arguments, safe, spreadSafe, true); - } - ClassNode classNode = controller.getClassNode(); - if (field != null && field.isPrivate() && !receiverType.equals(classNode) - && (StaticInvocationWriter.isPrivateBridgeMethodsCallAllowed(receiverType, classNode) - || StaticInvocationWriter.isPrivateBridgeMethodsCallAllowed(classNode, receiverType))) { - Map<String, MethodNode> mutators = receiverType.redirect().getNodeMetaData(PRIVATE_FIELDS_MUTATORS); - if (mutators != null) { - MethodNode methodNode = mutators.get(fieldName); - if (methodNode != null) { - MethodCallExpression call = callX(receiver, methodNode.getName(), args(field.isStatic() ? nullX() : receiver, arguments)); - call.setImplicitThis(implicitThis); - call.setMethodTarget(methodNode); - call.setSafe(safe); - call.setSpreadSafe(spreadSafe); - call.visit(controller.getAcg()); - return true; + FieldNode fieldNode = receiverType.getField(fieldName); + if (fieldNode != null) { + ClassNode classNode = controller.getClassNode(); + if (fieldNode.isPrivate() && !receiverType.equals(classNode) + && (StaticInvocationWriter.isPrivateBridgeMethodsCallAllowed(receiverType, classNode) + || StaticInvocationWriter.isPrivateBridgeMethodsCallAllowed(classNode, receiverType))) { + Map<String, MethodNode> mutators = receiverType.redirect().getNodeMetaData(PRIVATE_FIELDS_MUTATORS); + if (mutators != null) { + MethodNode methodNode = mutators.get(fieldName); + if (methodNode != null) { + MethodCallExpression call = callX(receiver, methodNode.getName(), args(fieldNode.isStatic() ? nullX() : receiver, arguments)); + call.setImplicitThis(implicitThis); + call.setMethodTarget(methodNode); + call.setSafe(safe); + call.setSpreadSafe(spreadSafe); + call.visit(controller.getAcg()); + return true; + } } } } diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java index 985e640108..1bb0415a61 100644 --- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java +++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesCallSiteWriter.java @@ -827,28 +827,30 @@ public class StaticTypesCallSiteWriter extends CallSiteWriter { return receiverType; } - // this is just a simple set field handling static and non-static, but not Closure and inner classes private boolean setField(final PropertyExpression expression, final Expression objectExpression, final ClassNode receiverType, final String name) { - if (expression.isSafe()) return false; + if (expression.isSafe() || expression.isSpreadSafe()) return false; + FieldNode fn = AsmClassGenerator.getDeclaredFieldOfCurrentClassOrAccessibleFieldOfSuper(controller.getClassNode(), receiverType, name, false); if (fn == null) return false; - OperandStack stack = controller.getOperandStack(); - stack.doGroovyCast(fn.getType()); + + OperandStack os = controller.getOperandStack(); + os.doGroovyCast(fn.getType()); MethodVisitor mv = controller.getMethodVisitor(); if (!fn.isStatic()) { controller.getCompileStack().pushLHS(false); objectExpression.visit(controller.getAcg()); controller.getCompileStack().popLHS(); - if (!receiverType.equals(stack.getTopOperand())) { + if (!receiverType.equals(os.getTopOperand())) { BytecodeHelper.doCast(mv, receiverType); - stack.replace(receiverType); + os.replace(receiverType); } - stack.swap(); + os.swap(); // stack: operand, receiver, ... mv.visitFieldInsn(PUTFIELD, BytecodeHelper.getClassInternalName(fn.getOwner()), name, BytecodeHelper.getTypeDescription(fn.getType())); - stack.remove(1); - } else { + os.remove(2); + } else { // stack: operand, ... mv.visitFieldInsn(PUTSTATIC, BytecodeHelper.getClassInternalName(receiverType), name, BytecodeHelper.getTypeDescription(fn.getType())); + os.remove(1); } return true; diff --git a/src/main/java/org/codehaus/groovy/transform/sc/StaticCompilationMetadataKeys.java b/src/main/java/org/codehaus/groovy/transform/sc/StaticCompilationMetadataKeys.java index 7183bb3c04..8c32ebad55 100644 --- a/src/main/java/org/codehaus/groovy/transform/sc/StaticCompilationMetadataKeys.java +++ b/src/main/java/org/codehaus/groovy/transform/sc/StaticCompilationMetadataKeys.java @@ -22,13 +22,24 @@ package org.codehaus.groovy.transform.sc; * Static compilation AST node metadata keys. */ public enum StaticCompilationMetadataKeys { - STATIC_COMPILE_NODE, // used to mark a section of code as to be statically compiled - BINARY_EXP_TARGET, // use to tell which method should be used in a binary expression - PRIVATE_BRIDGE_METHODS, // private bridge methods are methods used by an outer class to access an inner class method - PRIVATE_FIELDS_ACCESSORS, // private fields accessors are methods used by an inner class to access an outer class field - PRIVATE_FIELDS_MUTATORS, // private fields mutators are methods used by an inner class to set an outer class field - DYNAMIC_OUTER_NODE_CALLBACK, // callback for dynamic classes that contain statically compiled inner classes or methods - PROPERTY_OWNER, // the type of the class which owns the property - COMPONENT_TYPE, // for list.property expressions, we need the inferred component type - RECEIVER_OF_DYNAMIC_PROPERTY // if a receiver is the receiver of a dynamic property (for mixed mode compilation) + /** Marks a section of code for static compilation. */ + STATIC_COMPILE_NODE, + /** Tells which method should be used in a binary expression. */ + BINARY_EXP_TARGET, + /** Private bridge methods are methods used to access a nestmate's method. */ + PRIVATE_BRIDGE_METHODS, + /** Private fields accessors are methods used to read a nestmate's field. */ + @Deprecated(since = "5.0.0") + PRIVATE_FIELDS_ACCESSORS, + /** Private fields mutators are methods used to write a nestmate's field. */ + @Deprecated(since = "5.0.0") + PRIVATE_FIELDS_MUTATORS, + /** Callback for dynamic classes that contain statically compiled inner classes or methods. */ + DYNAMIC_OUTER_NODE_CALLBACK, + /** The type of the class which owns the property. */ + PROPERTY_OWNER, + /** For list.property expressions, we need the inferred component type. */ + COMPONENT_TYPE, + /** If a receiver is the receiver of a dynamic property (for mixed-mode compilation). */ + RECEIVER_OF_DYNAMIC_PROPERTY } 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 758731783b..462e49c3d0 100644 --- a/src/main/java/org/codehaus/groovy/transform/sc/StaticCompilationVisitor.java +++ b/src/main/java/org/codehaus/groovy/transform/sc/StaticCompilationVisitor.java @@ -429,6 +429,7 @@ public class StaticCompilationVisitor extends StaticTypeCheckingVisitor { /** * Adds special accessors and mutators for private fields so that inner classes can get/set them. */ + @Deprecated(since = "5.0.0") private static void addPrivateFieldsAccessors(final ClassNode node) { Map<String, MethodNode> privateFieldAccessors = node.getNodeMetaData(PRIVATE_FIELDS_ACCESSORS); Map<String, MethodNode> privateFieldMutators = node.getNodeMetaData(PRIVATE_FIELDS_MUTATORS); diff --git a/src/main/java/org/codehaus/groovy/transform/sc/transformers/PropertyExpressionTransformer.java b/src/main/java/org/codehaus/groovy/transform/sc/transformers/PropertyExpressionTransformer.java index fc54a954f9..f4811f5779 100644 --- a/src/main/java/org/codehaus/groovy/transform/sc/transformers/PropertyExpressionTransformer.java +++ b/src/main/java/org/codehaus/groovy/transform/sc/transformers/PropertyExpressionTransformer.java @@ -18,13 +18,18 @@ */ package org.codehaus.groovy.transform.sc.transformers; +import org.codehaus.groovy.ast.FieldNode; import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.expr.AttributeExpression; import org.codehaus.groovy.ast.expr.Expression; import org.codehaus.groovy.ast.expr.MethodCallExpression; import org.codehaus.groovy.ast.expr.PropertyExpression; +import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys; import org.codehaus.groovy.transform.stc.StaticTypesMarker; +import static org.apache.groovy.ast.tools.ExpressionUtils.isThisExpression; import static org.codehaus.groovy.ast.tools.GeneralUtils.callX; +import static org.codehaus.groovy.ast.tools.GeneralUtils.classX; class PropertyExpressionTransformer { @@ -48,6 +53,28 @@ class PropertyExpressionTransformer { return mce; } + FieldNode field = pe.getNodeMetaData(StaticTypesMarker.PV_FIELDS_ACCESS); + if (field == null) { + field = pe.getNodeMetaData(StaticTypesMarker.PV_FIELDS_MUTATION); + } + if (field != null) { + AttributeExpression ae; + if (field.isStatic()) { + ae = new AttributeExpression(classX(field.getDeclaringClass()), pe.getProperty()); + } else if (!isThisExpression(pe.getObjectExpression())) { + ae = new AttributeExpression(scTransformer.transform(pe.getObjectExpression()), pe.getProperty(), pe.isSafe()); + } else { + ae = new AttributeExpression(new PropertyExpression(classX(field.getDeclaringClass()), "this"), pe.getProperty(), pe.isSafe()); + + ae.getObjectExpression().putNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER, field.getDeclaringClass()); + ae.getObjectExpression().putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, field.getDeclaringClass()); + } + ae.setSpreadSafe(pe.isSpreadSafe()); + ae.setSourcePosition(pe); + ae.copyNodeMetaData(pe); + return ae; + } + return scTransformer.superTransform(pe); } } diff --git a/src/main/java/org/codehaus/groovy/transform/sc/transformers/VariableExpressionTransformer.java b/src/main/java/org/codehaus/groovy/transform/sc/transformers/VariableExpressionTransformer.java index f15915513b..c2d3d44fa7 100644 --- a/src/main/java/org/codehaus/groovy/transform/sc/transformers/VariableExpressionTransformer.java +++ b/src/main/java/org/codehaus/groovy/transform/sc/transformers/VariableExpressionTransformer.java @@ -28,10 +28,10 @@ import org.codehaus.groovy.ast.expr.VariableExpression; import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys; import org.codehaus.groovy.transform.stc.StaticTypesMarker; +import static org.codehaus.groovy.ast.tools.GeneralUtils.attrX; import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX; import static org.codehaus.groovy.ast.tools.GeneralUtils.classX; import static org.codehaus.groovy.ast.tools.GeneralUtils.propX; -import static org.codehaus.groovy.ast.tools.GeneralUtils.thisPropX; class VariableExpressionTransformer { @@ -86,9 +86,9 @@ class VariableExpressionTransformer { return null; } - // access to a private field from a section of code that normally doesn't have access to it, like a closure block or an inner class - PropertyExpression pe = !field.isStatic() ? thisPropX(true, ve.getName()) : propX(classX(field.getDeclaringClass()), ve.getName()); - // store the declaring class so that the class writer knows that it will have to call a bridge method + // access to a private field from a section of code that normally doesn't have access to it, like a closure block or inner class + Expression fieldOwner = field.isStatic() ? classX(field.getDeclaringClass()) : propX(classX(field.getDeclaringClass()), "this"); + PropertyExpression pe = attrX(fieldOwner, ve.getName()); // GROOVY-10687, GROOVY-11412: direct access pe.getObjectExpression().putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, field.getDeclaringClass()); pe.putNodeMetaData(StaticTypesMarker.DECLARATION_INFERRED_TYPE, field.getOriginType()); pe.getProperty().setSourcePosition(ve); diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java index 2d46571a71..87ed4596a2 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -592,8 +592,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport { ClassNode declaringClass = fn.getDeclaringClass(); ClassNode enclosingClass = typeCheckingContext.getEnclosingClassNode(); if (declaringClass == enclosingClass ? typeCheckingContext.getEnclosingClosure() != null : getNestHost(declaringClass) == getNestHost(enclosingClass)) { - StaticTypesMarker accessKind = lhsOfAssignment ? PV_FIELDS_MUTATION : PV_FIELDS_ACCESS; - addPrivateFieldOrMethodAccess(source, declaringClass, accessKind, fn); + source.putNodeMetaData(lhsOfAssignment ? PV_FIELDS_MUTATION : PV_FIELDS_ACCESS, fn); } } } diff --git a/src/test/org/codehaus/groovy/classgen/asm/NestHostTests.groovy b/src/test/org/codehaus/groovy/classgen/asm/NestHostTests.groovy index 3ccf2a2f96..db63589cda 100644 --- a/src/test/org/codehaus/groovy/classgen/asm/NestHostTests.groovy +++ b/src/test/org/codehaus/groovy/classgen/asm/NestHostTests.groovy @@ -119,4 +119,19 @@ final class NestHostTests { assert type.nestMembers*.name.sort() == ['C', 'C$D', 'C$D$_m_lambda1', 'C$D$_m_lambda1$_lambda2'] } } + + @Test + void testNestHost6() { + def types = compileScript ''' + @groovy.transform.CompileStatic + class C { + def closure = { -> { -> Optional.empty().orElseGet(() -> 42) } } + } + ''' + + types.each { type -> + assert type.nestHost.name == 'C' + assert type.nestMembers*.name.sort() == ['C', 'C$_closure1', 'C$_closure1$_closure2', 'C$_closure1$_closure2$_lambda3'] + } + } } 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 4e2b2d5bd5..f9a5f17f8a 100644 --- a/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy +++ b/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy @@ -616,45 +616,35 @@ final class FieldsAndPropertiesStaticCompileTest extends FieldsAndPropertiesSTCT } new Bar().test() ''' - 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') + 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() { - assertScript ''' - class Foo { - private String s - Closure c = { this.s = 'abc' } - - void test() { - c() - assert s == 'abc' - } - } - new Foo().test() - ''' - assert astTrees['Foo$_closure1'][1].contains('INVOKESTATIC Foo.pfaccess$00 (LFoo;Ljava/lang/String;)Ljava/lang/String') - } - - void testImplicitPrivateFieldMutationInClosureUsesBridgeMethod() { - assertScript ''' - class Foo { - private String s - Closure c = { s = 'abc' } + // GROOVY-7705, GROOVY-10687 + void testPrivateFieldMutationInClosureUsesDirectAccess() { + for (prefix in ['','this.','thisObject.','getThisObject().']) { + assertScript """ + class Foo { + Closure c = { -> ${prefix}s = 'abc' } + private s - String test() { - c() - assert s == 'abc' + void test() { + c() + assert s == 'abc' + } } - } - new Foo().test() - ''' - assert astTrees['Foo$_closure1'][1].contains('INVOKESTATIC Foo.pfaccess$00 (LFoo;Ljava/lang/String;)Ljava/lang/String') + new Foo().test() + """ + String c = astTrees['Foo$_closure1'][1] + assert c.contains('PUTFIELD Foo.s') // JEP 181: nestmate access + assert !c.contains('INVOKESTATIC Foo.pfaccess$00 (LFoo;Ljava/lang/Object;)') + } } - void testPrivateStaticFieldMutationInClosureUsesBridgeMethod() { + // GROOVY-7705, GROOVY-10687 + void testPrivateStaticFieldMutationInClosureUsesDirectAccess() { assertScript ''' class Foo { private static String s @@ -667,10 +657,13 @@ final class FieldsAndPropertiesStaticCompileTest extends FieldsAndPropertiesSTCT } new Foo().test() ''' - assert astTrees['Foo$_closure1'][1].contains('INVOKESTATIC Foo.pfaccess$00 (LFoo;Ljava/lang/String;)Ljava/lang/String') + String c = astTrees['Foo$_closure1'][1] + assert c.contains('PUTSTATIC Foo.s') // JEP 181: nestmate access + assert !c.contains('INVOKESTATIC Foo.pfaccess$00 (LFoo;Ljava/lang/String;)') } - void testPrivateFieldMutationInAICUsesBridgeMethod() { + // GROOVY-7705, GROOVY-10687 + void testPrivateFieldMutationInAICUsesDirectAccess() { assertScript ''' class C { private int x @@ -682,10 +675,13 @@ final class FieldsAndPropertiesStaticCompileTest extends FieldsAndPropertiesSTCT } new C().test() ''' - assert astTrees['C$1'][1].contains('INVOKESTATIC C.pfaccess$00 (LC;I)I') + String aic = astTrees['C$1'][1] + assert aic.contains('PUTFIELD C.x') // direct access + assert !aic.contains('INVOKESTATIC C.pfaccess$00 (LC;I)I') } - void testImplicitPrivateFieldMutationInAICUsesBridgeMethod() { + // GROOVY-7705, GROOVY-10687 + void testImplicitPrivateFieldMutationInAICUsesDirectAccess() { assertScript ''' class C { private int x @@ -697,10 +693,13 @@ final class FieldsAndPropertiesStaticCompileTest extends FieldsAndPropertiesSTCT } new C().test() ''' - assert astTrees['C$1'][1].contains('INVOKESTATIC C.pfaccess$00 (LC;I)I') + String aic = astTrees['C$1'][1] + assert aic.contains('PUTFIELD C.x') // direct access + assert !aic.contains('INVOKESTATIC C.pfaccess$00 (LC;I)I') } - void testPrivateStaticFieldMutationInAICUsesBridgeMethod() { + // GROOVY-7705, GROOVY-10687 + void testPrivateStaticFieldMutationInAICUsesDirectAccess() { assertScript ''' class C { private static int x @@ -712,10 +711,13 @@ final class FieldsAndPropertiesStaticCompileTest extends FieldsAndPropertiesSTCT } new C().test() ''' - assert astTrees['C$1'][1].contains('INVOKESTATIC C.pfaccess$00 (LC;I)I') + String aic = astTrees['C$1'][1] + assert aic.contains('PUTSTATIC C.x') // direct access + assert !aic.contains('INVOKESTATIC C.pfaccess$00 (LC;I)I') } - void testMultiplePrivateFieldMutatorBridgeMethods() { + // GROOVY-7705, GROOVY-10687 + void testMultiplePrivateFieldMutatorDirectAccess() { assertScript ''' class C { private int x @@ -730,10 +732,14 @@ final class FieldsAndPropertiesStaticCompileTest extends FieldsAndPropertiesSTCT } 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;') + String closure = astTrees['C$_closure1'][1] + assert closure.contains('PUTFIELD C.x') // direct access + assert closure.contains('PUTFIELD C.y') // direct access + assert !closure.contains('INVOKESTATIC C.pfaccess$00 (LC;I)I') + assert !closure.contains('INVOKESTATIC C.pfaccess$01 (LC;Ljava/lang/String;)Ljava/lang/String;') } + // GROOVY-7705, GROOVY-9385, GROOVY-10687 void testPrivateFieldBridgeMethodsAreGeneratedAsNecessary() { assertScript ''' class C { @@ -754,16 +760,16 @@ final class FieldsAndPropertiesStaticCompileTest extends FieldsAndPropertiesSTCT } 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' + String dump = astTrees['C'][1] + assert !dump.contains('pfaccess$0') // no access bridge method for 'accessed' + assert !dump.contains('pfaccess$00') // no mutate bridge method for 'accessed' + assert !dump.contains('pfaccess$01') // no mutate bridge method for 'mutated' + assert !dump.contains('pfaccess$1') // no access bridge method for 'mutated' + assert !dump.contains('pfaccess$2') // no access bridge method for 'accessedAndMutated' + assert !dump.contains('pfaccess$02') // no mutate 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;') + 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