This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY-4737
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY-4737 by this push:
new 32328595d6 next step
32328595d6 is described below
commit 32328595d65602be84a990c847681e33dac536ba
Author: Eric Milles <[email protected]>
AuthorDate: Mon Feb 2 13:52:24 2026 -0600
next step
7024 8050
---
src/main/java/groovy/lang/MetaClassImpl.java | 37 +++-
.../classgen/InnerClassCompletionVisitor.java | 237 +--------------------
.../groovy/classgen/InnerClassVisitorHelper.java | 57 +----
.../groovy/gls/innerClass/InnerClassTest.groovy | 102 +++++----
4 files changed, 100 insertions(+), 333 deletions(-)
diff --git a/src/main/java/groovy/lang/MetaClassImpl.java
b/src/main/java/groovy/lang/MetaClassImpl.java
index 7f88b8314d..bbcc25b319 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -1232,8 +1232,9 @@ public class MetaClassImpl implements MetaClass,
MutableMetaClass {
var outerClass = sender.getEnclosingClass(); // check outer class
nesting of call site
if (outerClass != null && (sender == theClass ||
sender.isAssignableFrom(theClass))) {
MetaClass omc = registry.getMetaClass(outerClass);
+ Object target = getOuterReference(sender, object);
try {
- return omc.invokeMethod(outerClass, outerClass,
methodName, arguments, false, false);
+ return omc.invokeMethod(outerClass, target, methodName,
arguments, false, false);
} catch (MissingMethodException e) {
mme.addSuppressed(e);
}
@@ -1242,6 +1243,32 @@ public class MetaClassImpl implements MetaClass,
MutableMetaClass {
throw mme;
}
+ private Object getOuterReference(final Class<?> innerClass, final Object
object) {
+ Object outer = null;
+ // non-static inner class may have outer class reference available in
this$0 field
+ if (!(object instanceof Class) && (innerClass.getModifiers() &
Opcodes.ACC_STATIC) == 0) {
+ try {
+ innerClass.getDeclaredField("this$0");
+ outer = getAttribute(object,"this$0");
+ if (outer instanceof GeneratedClosure) {
+ outer = ((Closure<?>) outer).getThisObject(); // skip
closure(s)
+ }
+ } catch (NoSuchFieldException ignored) {
+ // an AIC is non-static but may not have this$0
+ } catch (Throwable t) {
+ throw new GroovyRuntimeException(t);
+ }
+ }
+ if (outer == null) {
+ Class<?> outerClass = innerClass.getEnclosingClass();
+ while (GeneratedClosure.class.isAssignableFrom(outerClass)) {
+ outerClass = outerClass.getEnclosingClass(); // skip closure(s)
+ }
+ outer = outerClass;
+ }
+ return outer;
+ }
+
private MetaMethod getMetaMethod(final Class<?> sender, final Object
object, final String methodName, final boolean isCallToSuper, final Object[]
arguments) {
MetaMethod method = null;
if (CALL_METHOD.equals(methodName) && object instanceof
GeneratedClosure) {
@@ -1951,8 +1978,10 @@ public class MetaClassImpl implements MetaClass,
MutableMetaClass {
var outerClass = sender.getEnclosingClass(); // check outer class
nesting of call site
if (outerClass != null && (sender == theClass ||
sender.isAssignableFrom(theClass))) {
MetaClass omc = registry.getMetaClass(outerClass);
+ Object target = getOuterReference(sender, object);
try {
- return omc.getProperty(outerClass, outerClass, name,
false, false);
+ return /*target instanceof GroovyObject go ?
go.getProperty(name)
+ : */omc.getProperty(outerClass, target, name, false,
false);
} catch (MissingPropertyException e) {
mpe.addSuppressed(e);
}
@@ -2847,8 +2876,10 @@ public class MetaClassImpl implements MetaClass,
MutableMetaClass {
var outerClass = sender.getEnclosingClass(); // check outer class
nesting of call site
if (outerClass != null && (sender == theClass ||
sender.isAssignableFrom(theClass))) {
MetaClass omc = registry.getMetaClass(outerClass);
+ Object target = getOuterReference(sender, object);
try {
- omc.setProperty(outerClass, outerClass, name, newValue,
false, false);
+ if (target instanceof GroovyObject go)
go.setProperty(name, newValue);
+ else omc.setProperty(outerClass, target, name, newValue,
false, false);
return;
} catch (MissingPropertyException e) {
mpe.addSuppressed(e);
diff --git
a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
index 8706be493b..399a82d438 100644
---
a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
+++
b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
@@ -18,56 +18,31 @@
*/
package org.codehaus.groovy.classgen;
-import groovy.transform.CompileStatic;
-import groovy.transform.stc.POJO;
-import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ConstructorNode;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.InnerClassNode;
-import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.TupleExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.ast.stmt.BlockStatement;
-import org.codehaus.groovy.ast.stmt.TryCatchStatement;
-import org.codehaus.groovy.classgen.asm.BytecodeHelper;
import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.SourceUnit;
-import org.objectweb.asm.MethodVisitor;
-import java.util.Arrays;
import java.util.List;
import java.util.StringJoiner;
-import java.util.function.BiConsumer;
-import static org.apache.groovy.ast.tools.AnnotatedNodeUtils.hasAnnotation;
import static
org.apache.groovy.ast.tools.ClassNodeUtils.addGeneratedConstructor;
-import static org.apache.groovy.ast.tools.ClassNodeUtils.getMethod;
import static
org.apache.groovy.ast.tools.ConstructorNodeUtils.getFirstIfSpecialConstructorCall;
import static org.apache.groovy.ast.tools.MethodNodeUtils.getCodeAsBlock;
-import static org.codehaus.groovy.ast.ClassHelper.CLOSURE_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.OBJECT_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.STRING_TYPE;
-import static org.codehaus.groovy.ast.ClassHelper.VOID_TYPE;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
-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.catchS;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorSuperX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorThisX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.nullX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.params;
import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.throwS;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.tryCatchS;
import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
import static org.codehaus.groovy.transform.trait.Traits.isTrait;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
@@ -75,12 +50,6 @@ import static org.objectweb.asm.Opcodes.ACC_MANDATED;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
-import static org.objectweb.asm.Opcodes.ACONST_NULL;
-import static org.objectweb.asm.Opcodes.ALOAD;
-import static org.objectweb.asm.Opcodes.ARETURN;
-import static org.objectweb.asm.Opcodes.CHECKCAST;
-import static org.objectweb.asm.Opcodes.GETFIELD;
-import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
public class InnerClassCompletionVisitor extends InnerClassVisitorHelper {
@@ -104,25 +73,13 @@ public class InnerClassCompletionVisitor extends
InnerClassVisitorHelper {
if (node.isEnum() || node.isInterface() ||
isTrait(node.getOuterClass())) return;
- // if the class has an inner class, add methods to support private
member access
- if (node.getInnerClasses().hasNext()) {
- addDispatcherMethods(node);
- }
-
if (node instanceof InnerClassNode innerClass) {
thisField = node.getDeclaredField("this$0");
if (innerClass.getVariableScope() == null &&
node.getDeclaredConstructors().isEmpty()) {
// add empty default constructor
addGeneratedConstructor(innerClass, ACC_PUBLIC,
Parameter.EMPTY_ARRAY, null, null);
}
-
super.visitClass(node);
-
- boolean innerPojo = hasAnnotation(node, new ClassNode(POJO.class))
- && hasAnnotation(node, new ClassNode(CompileStatic.class));
- if (!innerPojo && !isStatic(innerClass)) {
- addMopMethods(innerClass);
- }
}
}
@@ -156,195 +113,7 @@ public class InnerClassCompletionVisitor extends
InnerClassVisitorHelper {
}
}
- private static String getTypeDescriptor(ClassNode node, boolean isStatic) {
- return BytecodeHelper.getTypeDescription(getClassNode(node, isStatic));
- }
-
- private static String getInternalName(ClassNode node, boolean isStatic) {
- return BytecodeHelper.getClassInternalName(getClassNode(node,
isStatic));
- }
-
- private static void addDispatcherMethods(ClassNode classNode) {
- final int objectDistance = getObjectDistance(classNode);
-
- // since we added an anonymous inner class we should also
- // add the dispatcher methods
-
- // add method dispatcher
- BlockStatement block = new BlockStatement();
- MethodNode method = classNode.addSyntheticMethod(
- "this$dist$invoke$" + objectDistance,
- ACC_PUBLIC,
- OBJECT_TYPE,
- params(param(STRING_TYPE, "name"), param(OBJECT_TYPE, "args")),
- ClassNode.EMPTY_ARRAY,
- block
- );
- setMethodDispatcherCode(block, VariableExpression.THIS_EXPRESSION,
method.getParameters());
-
- // add property setter
- block = new BlockStatement();
- method = classNode.addSyntheticMethod(
- "this$dist$set$" + objectDistance,
- ACC_PUBLIC,
- VOID_TYPE,
- params(param(STRING_TYPE, "name"), param(OBJECT_TYPE,
"value")),
- ClassNode.EMPTY_ARRAY,
- block
- );
- setPropertySetterDispatcher(block, VariableExpression.THIS_EXPRESSION,
method.getParameters());
-
- // add property getter
- block = new BlockStatement();
- method = classNode.addSyntheticMethod(
- "this$dist$get$" + objectDistance,
- ACC_PUBLIC,
- OBJECT_TYPE,
- params(param(STRING_TYPE, "name")),
- ClassNode.EMPTY_ARRAY,
- block
- );
- setPropertyGetterDispatcher(block, VariableExpression.THIS_EXPRESSION,
method.getParameters());
- }
-
- private void getThis(final MethodVisitor mv, final String
classInternalName, final String outerClassDescriptor, final String
innerClassInternalName) {
- mv.visitVarInsn(ALOAD, 0);
- if (thisField != null && CLOSURE_TYPE.equals(thisField.getType())) {
- mv.visitFieldInsn(GETFIELD, classInternalName, "this$0",
BytecodeHelper.getTypeDescription(CLOSURE_TYPE));
- mv.visitMethodInsn(INVOKEVIRTUAL,
BytecodeHelper.getClassInternalName(CLOSURE_TYPE), "getThisObject",
"()Ljava/lang/Object;", false);
- mv.visitTypeInsn(CHECKCAST, innerClassInternalName);
- } else {
- mv.visitFieldInsn(GETFIELD, classInternalName, "this$0",
outerClassDescriptor);
- }
- }
-
- private void addMopMethods(final InnerClassNode node) {
- final boolean isStatic = isStatic(node);
- final ClassNode outerClass = node.getOuterClass();
- final int outerClassDistance = getObjectDistance(outerClass);
- final String classInternalName =
BytecodeHelper.getClassInternalName(node);
- final String outerClassInternalName = getInternalName(outerClass,
isStatic);
- final String outerClassDescriptor = getTypeDescriptor(outerClass,
isStatic);
-
- addMissingHandler(node,
- "methodMissing",
- ACC_PUBLIC,
- OBJECT_TYPE,
- params(param(STRING_TYPE, "name"), param(OBJECT_TYPE, "args")),
- (methodBody, parameters) -> {
- if (isStatic) {
- setMethodDispatcherCode(methodBody,
classX(outerClass), parameters);
- } else {
- methodBody.addStatement(
- new BytecodeSequence(new BytecodeInstruction()
{
- @Override
- public void visit(final MethodVisitor mv) {
- getThis(mv, classInternalName,
outerClassDescriptor, outerClassInternalName);
- mv.visitVarInsn(ALOAD, 1);
- mv.visitVarInsn(ALOAD, 2);
- mv.visitMethodInsn(INVOKEVIRTUAL,
outerClassInternalName, "this$dist$invoke$" + outerClassDistance,
"(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;", false);
- mv.visitInsn(ARETURN);
- }
- })
- );
- }
- }
- );
-
- ClassNode[] nameValueTypes = {STRING_TYPE, OBJECT_TYPE};
- MethodNode propertyMissing = getMethod(node, "propertyMissing", (m) ->
!m.isStatic() && !m.isPrivate()
- &&
Arrays.equals(Arrays.stream(m.getParameters()).map(Parameter::getType).toArray(),nameValueTypes));
- ClassNode returnType = propertyMissing != null ?
propertyMissing.getReturnType() : VOID_TYPE; // GROOVY-11822
-
- addMissingHandler(node,
- "propertyMissing",
- ACC_PUBLIC,
- returnType,
- params(param(STRING_TYPE, "name"), param(OBJECT_TYPE,
"value")),
- (methodBody, parameters) -> {
- if (isStatic) {
- setPropertySetterDispatcher(methodBody,
classX(outerClass), parameters);
- } else {
- methodBody.addStatement(
- new BytecodeSequence(new BytecodeInstruction()
{
- @Override
- public void visit(final MethodVisitor mv) {
- getThis(mv, classInternalName,
outerClassDescriptor, outerClassInternalName);
- mv.visitVarInsn(ALOAD, 1);
- mv.visitVarInsn(ALOAD, 2);
- mv.visitMethodInsn(INVOKEVIRTUAL,
outerClassInternalName, "this$dist$set$" + outerClassDistance,
"(Ljava/lang/String;Ljava/lang/Object;)V", false);
- if
(!ClassHelper.isPrimitiveVoid(returnType)) mv.visitInsn(ACONST_NULL);
- BytecodeHelper.doReturn(mv,
returnType);
- }
- })
- );
- }
- }
- );
-
- addMissingHandler(node,
- "propertyMissing",
- ACC_PUBLIC,
- OBJECT_TYPE,
- params(param(STRING_TYPE, "name")),
- (methodBody, parameters) -> {
- if (isStatic) {
- setPropertyGetterDispatcher(methodBody,
classX(outerClass), parameters);
- } else {
- methodBody.addStatement(
- new BytecodeSequence(new BytecodeInstruction()
{
- @Override
- public void visit(final MethodVisitor mv) {
- getThis(mv, classInternalName,
outerClassDescriptor, outerClassInternalName);
- mv.visitVarInsn(ALOAD, 1);
- mv.visitMethodInsn(INVOKEVIRTUAL,
outerClassInternalName, "this$dist$get$" + outerClassDistance,
"(Ljava/lang/String;)Ljava/lang/Object;", false);
- mv.visitInsn(ARETURN);
- }
- })
- );
- }
- }
- );
- }
-
- private void addMissingHandler(final InnerClassNode innerClass, final
String methodName, final int modifiers,
- final ClassNode returnType, final Parameter[] parameters, final
BiConsumer<BlockStatement, Parameter[]> consumer) {
- MethodNode method = innerClass.getDeclaredMethod(methodName,
parameters);
- if (method == null) {
- // try {
- // <consumer dispatch>
- // } catch (MissingMethodException notFound) {
- // throw new MissingMethodException(notFound.method, this,
notFound.arguments)
- // }
- Parameter catchParam = param(OBJECT_TYPE, "notFound"); // dummy
type
- ClassNode exceptionT;
- Expression newException;
- if (methodName.endsWith("methodMissing")) {
- exceptionT =
ClassHelper.make(groovy.lang.MissingMethodException.class);
- newException = ctorX(exceptionT,
args(callX(varX(catchParam),"getMethod"), callThisX("getClass"),
callX(varX(catchParam),"getArguments")));
- } else {
- exceptionT =
ClassHelper.make(groovy.lang.MissingPropertyException.class);
- newException = ctorX(exceptionT,
args(callX(varX(catchParam),"getProperty"), callThisX("getClass"),
callX(varX(catchParam),"getCause")));
- }
- catchParam.setType(exceptionT);
- catchParam.setOriginType(exceptionT);
- BlockStatement handleMissing = block();
- consumer.accept(handleMissing, parameters);
- TryCatchStatement tryCatch = tryCatchS(handleMissing);
- tryCatch.addCatch(catchS(catchParam, throwS(newException)));
-
- innerClass.addSyntheticMethod(methodName, modifiers, returnType,
parameters, ClassNode.EMPTY_ARRAY, tryCatch);
-
- // if there is a user-defined method, add compiler error and
continue
- } else if (isStatic(innerClass) && (method.getModifiers() &
ACC_SYNTHETIC) == 0) {
- addError("\"" + methodName + "\" implementations are not supported
on static inner classes as " +
- "a synthetic version of \"" + methodName + "\" is added during
compilation for the purpose " +
- "of outer class delegation.",
- method);
- }
- }
-
- private void addThisReference(ConstructorNode node) {
+ private void addThisReference(final ConstructorNode node) {
if (!shouldHandleImplicitThisForInnerClass(classNode)) return;
// add "this$0" field init
@@ -396,7 +165,7 @@ public class InnerClassCompletionVisitor extends
InnerClassVisitorHelper {
node.setCode(block);
}
- private boolean shouldImplicitlyPassThisZero(ConstructorCallExpression
cce) {
+ private boolean shouldImplicitlyPassThisZero(final
ConstructorCallExpression cce) {
boolean pass = false;
if (cce.isThisCall()) {
pass = true;
@@ -415,7 +184,7 @@ public class InnerClassCompletionVisitor extends
InnerClassVisitorHelper {
return pass;
}
- private String getUniqueName(Parameter[] params, ConstructorNode node) {
+ private String getUniqueName(final Parameter[] params, final
ConstructorNode node) {
String namePrefix = "$p";
outer:
for (int i = 0; i < 100; i++) {
diff --git
a/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitorHelper.java
b/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitorHelper.java
index 32c5395164..ff1a9202da 100644
--- a/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitorHelper.java
+++ b/src/main/java/org/codehaus/groovy/classgen/InnerClassVisitorHelper.java
@@ -19,85 +19,30 @@
package org.codehaus.groovy.classgen;
import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
-import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.InnerClassNode;
import org.codehaus.groovy.ast.Parameter;
-import org.codehaus.groovy.ast.expr.Expression;
-import org.codehaus.groovy.ast.expr.SpreadExpression;
import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.objectweb.asm.Opcodes;
import static org.codehaus.groovy.ast.tools.GeneralUtils.assignS;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.assignX;
-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.constX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.eqX;
import static org.codehaus.groovy.ast.tools.GeneralUtils.fieldX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.ifS;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.indexX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.isInstanceOfX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.notX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.propX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
public abstract class InnerClassVisitorHelper extends ClassCodeVisitorSupport {
- private static final ClassNode OBJECT_ARRAY =
ClassHelper.OBJECT_TYPE.makeArray();
-
protected static void addFieldInit(final Parameter p, final FieldNode fn,
final BlockStatement block) {
block.addStatement(assignS(fieldX(fn), varX(p)));
}
- protected static void setPropertyGetterDispatcher(final BlockStatement
block, final Expression target, final Parameter[] parameters) {
- block.addStatement(returnS(propX(target, varX(parameters[0]))));
- }
-
- protected static void setPropertySetterDispatcher(final BlockStatement
block, final Expression target, final Parameter[] parameters) {
- block.addStatement(stmt(assignX(propX(target, varX(parameters[0])),
varX(parameters[1]))));
- }
-
- protected static void setMethodDispatcherCode (final BlockStatement
block, final Expression target, final Parameter[] parameters) {
- // if (!(args instanceof Object[])) return target.(name)(args)
- block.addStatement(ifS(
- notX(isInstanceOfX(varX(parameters[1]), OBJECT_ARRAY)),
- returnS(callX(target, varX(parameters[0]), varX(parameters[1])))));
-
- // if (((Object[])args).length == 1) return target.(name)(args[0])
- block.addStatement(ifS(
- eqX(propX(castX(OBJECT_ARRAY, varX(parameters[1])), "length"),
constX(1, true)),
- returnS(callX(target, varX(parameters[0]),
indexX(castX(OBJECT_ARRAY, varX(parameters[1])), constX(0, true))))));
-
- // return target.(name)(*args)
- block.addStatement(returnS(callX(target, varX(parameters[0]), new
SpreadExpression(varX(parameters[1])))));
- }
-
-
//--------------------------------------------------------------------------
-
- protected static ClassNode getClassNode(final ClassNode cn, final boolean
isStatic) {
- return isStatic ? ClassHelper.CLASS_Type : cn; // TODO: Set class type
parameter?
- }
-
- protected static int getObjectDistance(ClassNode cn) {
- int count = 0;
- while (cn != null && !ClassHelper.isObjectType(cn)) {
- cn = cn.getSuperClass();
- count += 1;
- }
- return count;
- }
-
protected static boolean isStatic(final InnerClassNode cn) {
return cn.getDeclaredField("this$0") == null;
}
protected static boolean shouldHandleImplicitThisForInnerClass(final
ClassNode cn) {
final int explicitOrImplicitStatic = Opcodes.ACC_ENUM |
Opcodes.ACC_INTERFACE | Opcodes.ACC_RECORD | Opcodes.ACC_STATIC;
- return (cn.getModifiers() & explicitOrImplicitStatic) == 0 && (cn
instanceof InnerClassNode && !((InnerClassNode) cn).isAnonymous())
+ return (cn.getModifiers() & explicitOrImplicitStatic) == 0 && (cn
instanceof InnerClassNode inner && !inner.isAnonymous())
&& cn.getAnnotations().stream().noneMatch(aNode ->
"groovy.transform.RecordType".equals(aNode.getClassNode().getName())); //
GROOVY-11600
}
}
diff --git a/src/test/groovy/gls/innerClass/InnerClassTest.groovy
b/src/test/groovy/gls/innerClass/InnerClassTest.groovy
index 90f7f7a1ae..a5a79381f3 100644
--- a/src/test/groovy/gls/innerClass/InnerClassTest.groovy
+++ b/src/test/groovy/gls/innerClass/InnerClassTest.groovy
@@ -594,14 +594,14 @@ final class InnerClassTest {
@Test
void testUsageOfOuterField() {
assertScript '''
- interface Run {
+ interface A {
def run()
}
- class Foo {
+ class C {
private x = 1
def foo() {
- def runner = new Run() {
+ def runner = new A() {
def run() { return x }
}
runner.run()
@@ -609,24 +609,25 @@ final class InnerClassTest {
void x(y) { x = y }
}
- def foo = new Foo()
- assert foo.foo() == 1
- foo.x(2)
- assert foo.foo() == 2
+
+ def c = new C()
+ assert c.foo() == 1
+ c.x(2)
+ assert c.foo() == 2
'''
}
@Test
void testUsageOfOuterField2() {
assertScript '''
- interface Run {
+ interface A {
def run()
}
- class Foo {
+ class C {
private static x = 1
static foo() {
- def runner = new Run() {
+ def runner = new A() {
def run() { return x }
}
runner.run()
@@ -634,33 +635,34 @@ final class InnerClassTest {
static x(y) { x = y }
}
- assert Foo.foo() == 1
- Foo.x(2)
- assert Foo.foo() == 2
+
+ assert C.foo() == 1
+ C.x(2)
+ assert C.foo() == 2
'''
}
@Test
void testUsageOfOuterField3() {
assertScript '''
- interface X {
+ interface A {
def m()
}
-
- class A {
- def pm = "pm"
-
- def bar(x) {x().m()}
+ class C {
+ def bar(x) {
+ x().m()
+ }
def foo() {
bar { ->
- return new X() {
- def m() { pm }
+ return new A() {
+ def m() { p }
}
}
}
+ final p = 'p'
}
- def a = new A()
- assert "pm" == a.foo()
+
+ assert new C().foo() == 'p'
'''
}
@@ -713,6 +715,7 @@ final class InnerClassTest {
static x(y) { x = y }
}
+
assert Foo.foo() == 1
Foo.x(2)
assert Foo.foo() == 2
@@ -928,15 +931,16 @@ final class InnerClassTest {
// GROOVY-8050
@Test
void testUsageOfOuterField13() {
- assertScript '''
+ def err = shouldFail '''
class Outer {
class Inner {
}
def p = 1
}
- def i = new Outer.Inner(new Outer())
- assert i.p == 1
+
+ new Outer.Inner(new Outer()).p
'''
+ assert err =~ /MissingPropertyException: No such property: p for
class: Outer.Inner/
}
@NotYetImplemented @Test
@@ -1063,28 +1067,27 @@ final class InnerClassTest {
@Test
void testUsageOfOuterFieldOverridden() {
assertScript '''
- interface Run {
+ interface A {
def run()
}
- class Foo {
- private x = 1
-
- def foo() {
- def runner = new Run() {
+ class B {
+ def test() {
+ def runner = new A() {
def run() { return x } // <-- dynamic variable
}
runner.run()
}
-
+ private x = 1
void setX(val) { x = val }
}
- class Bar extends Foo {
- def x = 'string' // hides 'foo.@x' and overrides
'foo.setX(val)'
+ class C extends B {
+ def x = 'string' // hides 'B.@x' and overrides 'B.setX(val)'
}
- def bar = new Bar()
- assert bar.foo() == 'string'
- bar.x = 'new string'
- assert bar.foo() == 'new string'
+
+ def c = new C()
+ assert c.test() == 1
+ c.x = 'new string'
+ assert c.test() == 1
'''
}
@@ -2410,9 +2413,28 @@ final class InnerClassTest {
assert err =~ /No such property: missing for class: Outer.Inner/
}
- // GROOVY-9618
+ // GROOVY-11823
@Test
void testNestedPropertyHandling5() {
+ assertScript '''
+ class Upper {
+ Object propertyMissing(String name) {
+ if (name == 'fizz') return 'buzz'
+ throw new MissingPropertyException(name, getClass())
+ }
+ }
+ class Outer {
+ static class Inner extends Upper {
+ }
+ }
+ def inner = new Outer.Inner()
+ assert inner.fizz == 'buzz'
+ '''
+ }
+
+ // GROOVY-9618
+ @Test
+ void testNestedPropertyHandling6() {
assertScript '''
class Super {
public static X = 1