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 2026/02/02 18:01:54 UTC
(groovy) branch GROOVY-4737 updated (7dd818d092 -> a36ae26ff9)
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a change to branch GROOVY-4737
in repository https://gitbox.apache.org/repos/asf/groovy.git
discard 7dd818d092 next step
new 02a8b0edb6 next step
new a36ae26ff9 next step
This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version. This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:
* -- * -- B -- O -- O -- O (7dd818d092)
\
N -- N -- N refs/heads/GROOVY-4737 (a36ae26ff9)
You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.
Any revisions marked "omit" are not gone; other references still
refer to them. Any revisions marked "discard" are gone forever.
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
src/main/java/groovy/lang/MetaClassImpl.java | 124 ++++-----
.../groovy/classgen/EnumCompletionVisitor.java | 51 ----
.../classgen/InnerClassCompletionVisitor.java | 6 +-
.../groovy/gls/innerClass/InnerClassTest.groovy | 147 ++++++++++-
src/test/groovy/groovy/ClosureTest.groovy | 285 ++++++++-------------
src/test/groovy/groovy/PropertyTest.groovy | 18 --
.../packageScope/PackageScopeTransformTest.groovy | 4 +-
7 files changed, 319 insertions(+), 316 deletions(-)
(groovy) 02/02: next step
Posted by em...@apache.org.
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
commit a36ae26ff95b610749176b2034e129ee699f9ad1
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Mon Feb 2 10:43:20 2026 -0600
next step
---
src/main/java/groovy/lang/MetaClassImpl.java | 124 ++++-----
.../groovy/classgen/EnumCompletionVisitor.java | 51 ----
.../classgen/InnerClassCompletionVisitor.java | 4 +-
.../groovy/gls/innerClass/InnerClassTest.groovy | 147 ++++++++++-
src/test/groovy/groovy/ClosureTest.groovy | 285 ++++++++-------------
src/test/groovy/groovy/PropertyTest.groovy | 18 --
.../packageScope/PackageScopeTransformTest.groovy | 4 +-
7 files changed, 318 insertions(+), 315 deletions(-)
diff --git a/src/main/java/groovy/lang/MetaClassImpl.java b/src/main/java/groovy/lang/MetaClassImpl.java
index 6df885bcb3..7f88b8314d 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -1217,7 +1217,29 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
return transformedMetaMethod.doMethodInvoke(object, arguments);
}
- return invokePropertyOrMissing(sender, object, methodName, originalArguments, fromInsideClass, isCallToSuper);
+ try {
+ return invokePropertyOrMissing(sender, object, methodName, originalArguments, fromInsideClass, isCallToSuper);
+ } catch (MissingMethodException mme) {
+ if (!isCallToSuper) {
+ return invokeOuterMethod(sender, object, methodName, originalArguments, mme); // GROOVY-11823
+ }
+ throw mme;
+ }
+ }
+
+ private Object invokeOuterMethod(final Class<?> sender, final Object object, final String methodName, final Object[] arguments, final MissingMethodException mme) {
+ if (sender == theClass ? isGroovyObject() : GroovyObject.class.isAssignableFrom(sender)) {
+ var outerClass = sender.getEnclosingClass(); // check outer class nesting of call site
+ if (outerClass != null && (sender == theClass || sender.isAssignableFrom(theClass))) {
+ MetaClass omc = registry.getMetaClass(outerClass);
+ try {
+ return omc.invokeMethod(outerClass, outerClass, methodName, arguments, false, false);
+ } catch (MissingMethodException e) {
+ mme.addSuppressed(e);
+ }
+ }
+ }
+ throw mme;
}
private MetaMethod getMetaMethod(final Class<?> sender, final Object object, final String methodName, final boolean isCallToSuper, final Object[] arguments) {
@@ -1894,28 +1916,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
try {
return invokeMissingProperty(object, name, null, true);
} catch (MissingPropertyException mpe) {
- if (false && sender == theClass) {
- Class<?> outerClass = theClass.getEnclosingClass();
- if (outerClass != null) {
- MetaClass omc = registry.getMetaClass(outerClass);
- try {
- Object outer = outerClass;
- if ((theClass.getModifiers() & Opcodes.ACC_STATIC) == 0) {
- try {
- theClass.getDeclaredField("this$0");
- outer = getAttribute(object, "this$0");
- } catch (NoSuchFieldException e) {
- }
- }
- return omc.getProperty(outerClass, outer, name, false, false);
- } catch (MissingPropertyException suppressed) {
- mpe.addSuppressed(suppressed);
- } catch (Throwable t) {
- t.printStackTrace();
- }
- }
- }
- throw mpe;
+ return getOuterProperty(sender, object, name, mpe);
}
}
@@ -1945,6 +1946,21 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
}
}
+ private Object getOuterProperty(final Class<?> sender, final Object object, final String name, final MissingPropertyException mpe) {
+ if (sender == theClass ? isGroovyObject() : GroovyObject.class.isAssignableFrom(sender)) {
+ var outerClass = sender.getEnclosingClass(); // check outer class nesting of call site
+ if (outerClass != null && (sender == theClass || sender.isAssignableFrom(theClass))) {
+ MetaClass omc = registry.getMetaClass(outerClass);
+ try {
+ return omc.getProperty(outerClass, outerClass, name, false, false);
+ } catch (MissingPropertyException e) {
+ mpe.addSuppressed(e);
+ }
+ }
+ }
+ throw mpe;
+ }
+
public MetaProperty getEffectiveGetMetaProperty(final Class sender, final Object object, final String name, final boolean useSuper) {
//----------------------------------------------------------------------
@@ -2073,28 +2089,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
try {
return invokeMissingProperty(receiver, getName(), null, true);
} catch (MissingPropertyException mpe) {
- if (false && sender == theClass) {
- Class<?> outerClass = theClass.getEnclosingClass();
- if (outerClass != null) {
- MetaClass omc = registry.getMetaClass(outerClass);
- try {
- Object outer = outerClass;
- if ((theClass.getModifiers() & Opcodes.ACC_STATIC) == 0) {
- try {
- theClass.getDeclaredField("this$0");
- outer = getAttribute(receiver, "this$0");
- } catch (NoSuchFieldException e) {
- }
- }
- return omc.getProperty(outerClass, outer, getName(), false, false);
- } catch (MissingPropertyException suppressed) {
- mpe.addSuppressed(suppressed);
- } catch (Throwable t) {
- t.printStackTrace();
- }
- }
- }
- throw mpe;
+ return getOuterProperty(sender, receiver, getName(), mpe);
}
}
};
@@ -2835,27 +2830,32 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
if (mp != null) {
throw new ReadOnlyPropertyException(name, theClass);
}
- if (!isStatic) {
- invokeMissingProperty(object, name, newValue, false);
- } else {
- try {
+ try {
+ if (!isStatic) {
+ invokeMissingProperty(object, name, newValue, false);
+ } else {
invokeStaticMissingProperty(object, name, newValue, false);
- } catch (MissingPropertyException missing) {
- if (isGroovyObject()) { // GROOVY-11823, et al.
- var outerClass = theClass.getEnclosingClass();
- if (outerClass != null && sender.isNestmateOf(outerClass)) {
- try {
- MetaClass omc = registry.getMetaClass(outerClass);
- omc.setProperty(sender, outerClass, name, newValue, false, false);
- return;
- } catch (MissingPropertyException mpe) {
- missing.addSuppressed(mpe);
- }
- }
+ }
+ } catch (MissingPropertyException e) {
+ if (!useSuper) setOuterProperty(sender, object, name, newValue, e); else throw e; // GROOVY-11823
+ }
+ }
+
+ private void setOuterProperty(Class<?> sender, final Object object, final String name, final Object newValue, final MissingPropertyException mpe) {
+ if (sender == null) sender = theClass; // GROOVY-11745
+ if (sender == theClass ? isGroovyObject() : GroovyObject.class.isAssignableFrom(sender)) {
+ var outerClass = sender.getEnclosingClass(); // check outer class nesting of call site
+ if (outerClass != null && (sender == theClass || sender.isAssignableFrom(theClass))) {
+ MetaClass omc = registry.getMetaClass(outerClass);
+ try {
+ omc.setProperty(outerClass, outerClass, name, newValue, false, false);
+ return;
+ } catch (MissingPropertyException e) {
+ mpe.addSuppressed(e);
}
- throw missing;
}
}
+ throw mpe;
}
private MetaProperty getMetaProperty(final Class<?> clazz, final String name, final boolean useSuper, final boolean useStatic) {
diff --git a/src/main/java/org/codehaus/groovy/classgen/EnumCompletionVisitor.java b/src/main/java/org/codehaus/groovy/classgen/EnumCompletionVisitor.java
index cd12c3b993..1d0d9653ae 100644
--- a/src/main/java/org/codehaus/groovy/classgen/EnumCompletionVisitor.java
+++ b/src/main/java/org/codehaus/groovy/classgen/EnumCompletionVisitor.java
@@ -23,7 +23,6 @@ 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.InnerClassNode;
import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.ast.expr.CastExpression;
@@ -44,15 +43,7 @@ import java.util.List;
import static java.util.stream.Collectors.toList;
import static org.apache.groovy.ast.tools.ClassNodeUtils.addGeneratedConstructor;
-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.classX;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
-import static org.codehaus.groovy.ast.tools.GeneralUtils.params;
-import static org.codehaus.groovy.transform.sc.StaticCompilationVisitor.isStaticallyCompiled;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
-import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
/**
@@ -93,11 +84,6 @@ public class EnumCompletionVisitor extends ClassCodeVisitorSupport {
for (ConstructorNode ctor : nonSyntheticConstructors(enumClass)) {
transformConstructor(ctor);
}
-
- var outerClass = enumClass.getOuterClass(); // GROOVY-7024
- if (outerClass != null && !isStaticallyCompiled(enumClass)) {
- addOuterClassDispatch((InnerClassNode) enumClass, outerClass);
- }
}
/**
@@ -206,41 +192,4 @@ public class EnumCompletionVisitor extends ClassCodeVisitorSupport {
private static List<ConstructorNode> nonSyntheticConstructors(final ClassNode cn) {
return cn.getDeclaredConstructors().stream().filter(c -> !c.isSynthetic()).collect(toList());
}
-
- private void addOuterClassDispatch(final InnerClassNode innerClass, final ClassNode outerClass) {
- var visitor = new InnerClassCompletionVisitor(null, sourceUnit);
-
- visitor.addMissingHandler(
- innerClass,
- "methodMissing",
- ACC_PUBLIC,
- OBJECT_TYPE,
- params(param(STRING_TYPE, "name"), param(OBJECT_TYPE, "args")),
- (methodBody, parameters) -> {
- InnerClassVisitorHelper.setMethodDispatcherCode(methodBody, classX(outerClass), parameters);
- }
- );
-
- visitor.addMissingHandler(
- innerClass,
- "propertyMissing",
- ACC_PUBLIC,
- OBJECT_TYPE,
- params(param(STRING_TYPE, "name")),
- (methodBody, parameters) -> {
- InnerClassVisitorHelper.setPropertyGetterDispatcher(methodBody, classX(outerClass), parameters);
- }
- );
-
- visitor.addMissingHandler(
- innerClass,
- "propertyMissing",
- ACC_PUBLIC,
- VOID_TYPE,
- params(param(STRING_TYPE, "name"), param(OBJECT_TYPE, "value")),
- (methodBody, parameters) -> {
- InnerClassVisitorHelper.setPropertySetterDispatcher(methodBody, classX(outerClass), parameters);
- }
- );
- }
}
diff --git a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
index 3217289209..8706be493b 100644
--- a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
+++ b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
@@ -120,7 +120,7 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper {
boolean innerPojo = hasAnnotation(node, new ClassNode(POJO.class))
&& hasAnnotation(node, new ClassNode(CompileStatic.class));
- if (!innerPojo) {
+ if (!innerPojo && !isStatic(innerClass)) {
addMopMethods(innerClass);
}
}
@@ -307,7 +307,7 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper {
);
}
- /* */ void addMissingHandler(final InnerClassNode innerClass, final String methodName, final int modifiers,
+ 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) {
diff --git a/src/test/groovy/gls/innerClass/InnerClassTest.groovy b/src/test/groovy/gls/innerClass/InnerClassTest.groovy
index a4fdb45a7d..90f7f7a1ae 100644
--- a/src/test/groovy/gls/innerClass/InnerClassTest.groovy
+++ b/src/test/groovy/gls/innerClass/InnerClassTest.groovy
@@ -406,8 +406,10 @@ final class InnerClassTest {
void testStaticInnerClass2() {
assertScript '''
class A {
- static class B {}
+ static class B {
+ }
}
+
assert A.declaredClasses.length == 1
assert A.declaredClasses[0] == A.B
'''
@@ -419,11 +421,12 @@ final class InnerClassTest {
class A {
static class B {
String p
+ String getQ() { WHY }
}
B m() {
return [p:'x'] // calls ScriptBytecodeAdapter.castToType([p:'x'], A$B.class)
}
- static final String q = 'y'
+ private static final String WHY = 'y'
}
o = new A().m()
@@ -936,6 +939,22 @@ final class InnerClassTest {
'''
}
+ @NotYetImplemented @Test
+ void testUsageOfOuterField14() {
+ assertScript '''
+ class Outer {
+ interface Inner {
+ default i() {
+ 'i' + o
+ }
+ }
+ private static o = 'o'
+ }
+
+ assert (new Outer.Inner() {}).i() == 'io'
+ '''
+ }
+
@Test
void testUsageOfOuterSuperField() {
assertScript '''
@@ -1085,6 +1104,7 @@ final class InnerClassTest {
runner.run()
}
}
+
def foo = new Foo()
assert foo.foo() == 1
'''
@@ -1106,6 +1126,7 @@ final class InnerClassTest {
runner.run()
}
}
+
def foo = new Foo()
assert foo.foo() == 1
'''
@@ -1126,6 +1147,7 @@ final class InnerClassTest {
runner.run()
}
}
+
def foo = new Foo()
assert foo.foo() == 1
'''
@@ -1147,6 +1169,7 @@ final class InnerClassTest {
runner.run()
}
}
+
def foo = new Foo()
assert foo.foo() == 1
'''
@@ -1297,6 +1320,22 @@ final class InnerClassTest {
'''
}
+ @NotYetImplemented @Test
+ void testUsageOfOuterMethod10() {
+ assertScript '''
+ class Outer {
+ interface Inner {
+ default i() {
+ 'i' + o()
+ }
+ }
+ private static o() { 'o' }
+ }
+
+ assert (new Outer.Inner() {}).i() == 'io'
+ '''
+ }
+
@Test
void testUsageOfOuterMethodOverridden() {
assertScript '''
@@ -2184,6 +2223,7 @@ final class InnerClassTest {
inner.inner()
}
}
+
assert new Outer().test() == 1
'''
}
@@ -2205,17 +2245,18 @@ final class InnerClassTest {
}
}
}
+
new Outer().obj.toString()
'''
}
// GROOVY-8274
@Test
- void testMissingMethodHandling() {
+ void testMethodMissing1() {
assertScript '''
class Outer {
class Inner {
- def methodMissing(String name, args) {
+ def methodMissing(String name, Object args) {
return name
}
}
@@ -2234,6 +2275,68 @@ final class InnerClassTest {
'''
}
+ @Test
+ void testMethodMissing2() {
+ assertScript '''
+ class Outer {
+ class Inner {
+ def methodMissing(String name, Object args) {
+ return 42
+ }
+ def propertyMissing(String name) {
+ return 42
+ }
+ }
+ }
+
+ def i = new Outer.Inner(new Outer())
+ assert i.foo() == 42
+ assert i.foobar == 42
+ '''
+ }
+
+ @Test
+ void testMethodMissing3() {
+ assertScript '''
+ class Outer {
+ static class Inner {
+ def methodMissing(String name, Object args) {
+ return 42
+ }
+ def propertyMissing(String name) {
+ return 42
+ }
+ }
+ }
+
+ def i = new Outer.Inner()
+ assert i.foo() == 42
+ assert i.foobar == 42
+ '''
+ }
+
+ @Test
+ void testMethodMissing4() {
+ assertScript '''
+ class Outer {
+ static class Inner {
+ def methodMissing(String name, Object args) {
+ return 42
+ }
+ def propertyMissing(String name) {
+ return 42
+ }
+ }
+ static class Other extends Inner {
+ }
+ }
+
+ def o = new Outer.Other()
+ assert o.foo() == 42
+ assert o.foobar == 42
+ '''
+ }
+
// GROOVY-6831
@Test
void testNestedPropertyHandling() {
@@ -2295,6 +2398,7 @@ final class InnerClassTest {
def err = shouldFail """
class Upper {
$returnType propertyMissing(String name, Object value) {
+ throw new MissingPropertyException(name, getClass())
}
}
class Outer {
@@ -2306,6 +2410,41 @@ final class InnerClassTest {
assert err =~ /No such property: missing for class: Outer.Inner/
}
+ // GROOVY-9618
+ @Test
+ void testNestedPropertyHandling5() {
+ assertScript '''
+ class Super {
+ public static X = 1
+ static getX() { 2 }
+ }
+ class Outer extends Super {
+ static class Inner {
+ def m() { X }
+ }
+ }
+
+ assert new Outer.Inner().m() == 2
+ '''
+
+ def err = shouldFail '''
+ class Outer {
+ public static X = 1
+ static getX() { 2 }
+ static class Inner {
+ }
+ }
+ class Other extends Outer.Inner {
+ def m() {
+ X // can't read super outer this way
+ }
+ }
+
+ new Other().m()
+ '''
+ assert err =~ /MissingPropertyException: No such property: X for class: Other/
+ }
+
// GROOVY-7312
@Test
void testInnerClassOfInterfaceIsStatic() {
diff --git a/src/test/groovy/groovy/ClosureTest.groovy b/src/test/groovy/groovy/ClosureTest.groovy
index 829645151d..7797016645 100644
--- a/src/test/groovy/groovy/ClosureTest.groovy
+++ b/src/test/groovy/groovy/ClosureTest.groovy
@@ -18,8 +18,9 @@
*/
package groovy
-import org.codehaus.groovy.control.MultipleCompilationErrorsException
-import org.junit.Test
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.params.ParameterizedTest
+import org.junit.jupiter.params.provider.ValueSource
import static groovy.test.GroovyAssert.assertScript
import static groovy.test.GroovyAssert.shouldFail
@@ -484,72 +485,6 @@ final class ClosureTest {
'''
}
- @Test
- void testStaticInnerClassOwnerWithPropertyMissingImplementation() {
- def err = shouldFail MultipleCompilationErrorsException, '''
- class ClosureTestA {
- static class ClosureTestB {
- def propertyMissing(String myName, Object myValue) {
- return myValue
- }
-
- def propertyMissing(String myName) {
- return 42
- }
-
- def methodMissing(String myName, Object myArgs) {
- return 42
- }
- }
- }
- '''
-
- assert err.message.contains('"methodMissing" implementations are not supported on static inner classes as a synthetic version of "methodMissing" is added during compilation for the purpose of outer class delegation.')
- assert err.message.contains('"propertyMissing" implementations are not supported on static inner classes as a synthetic version of "propertyMissing" is added during compilation for the purpose of outer class delegation.')
- }
-
- @Test
- void testInnerClassOwnerWithPropertyMissingImplementation() {
- assertScript '''
- class ClosureTestA {
- class ClosureTestB {
- def propertyMissing(String myName, Object myValue) {
- return myValue
- }
-
- def propertyMissing(String myName) {
- return 42
- }
-
- def methodMissing(String myName, Object myArgs) {
- return 42
- }
- }
- }
-
- def a = new ClosureTestA()
- def b = new ClosureTestA.ClosureTestB(a)
- '''
- }
-
- @Test
- void testStaticInnerClassHierarchyWithMethodMissing() {
- def err = shouldFail MultipleCompilationErrorsException, '''
- class ClosureTestA {
- static class ClosureTestB {
- def methodMissing(String myName, Object myArgs) {
- return 42
- }
- }
-
- static class ClosureTestB1 extends ClosureTestB {
- }
- }
- '''
-
- assert err.message.contains('"methodMissing" implementations are not supported on static inner classes as a synthetic version of "methodMissing" is added during compilation for the purpose of outer class delegation.')
- }
-
// GROOVY-10943
@Test
void testClosureUnderscorePlaceholder() {
@@ -564,150 +499,148 @@ final class ClosureTest {
}
// GROOVY-2433, GROOVY-3073, GROOVY-9987, GROOVY-11128
- @Test
- void testClosureAccessToEnclosingClassPrivateMethod() {
- for (who in ['this.', 'owner.', 'thisObject.', '']) {
- assertScript """
- class C {
- def getIds() {
- populateIds()
- }
- def populateIds = { ->
- ${who}sort([ 1, 5, 3, 4, 2 ])
- }
- private sort(list) {
- list.sort{ one, two -> one <=> two }
- }
+ @ParameterizedTest
+ @ValueSource(strings=['this.', 'owner.', 'thisObject.', ''])
+ void testClosureAccessToEnclosingClassPrivateMethod(String who) {
+ assertScript """
+ class C {
+ def getIds() {
+ populateIds()
}
+ def populateIds = { ->
+ ${who}sort([ 1, 5, 3, 4, 2 ])
+ }
+ private sort(list) {
+ list.sort{ one, two -> one <=> two }
+ }
+ }
- class D extends C {
- void test() {
- assert ids == [1,2,3,4,5]
- }
+ class D extends C {
+ void test() {
+ assert ids == [1,2,3,4,5]
}
+ }
- new D().test()
- """
+ new D().test()
+ """
- assertScript """
- class C {
- protected String protectedMethod() {
- def closure = { ->
- ${who}privateMethod()
- }
- closure()
- }
- private String privateMethod() {
- 'hello world'
+ assertScript """
+ class C {
+ protected String protectedMethod() {
+ def closure = { ->
+ ${who}privateMethod()
}
+ closure()
+ }
+ private String privateMethod() {
+ 'hello world'
}
+ }
- class D extends C {
- void test() {
- def result = protectedMethod()
- assert result == 'hello world'
- }
+ class D extends C {
+ void test() {
+ def result = protectedMethod()
+ assert result == 'hello world'
}
+ }
- new D().test()
- """
+ new D().test()
+ """
- assertScript """
- class C {
- def publicMethod() {
- [1].each {
- ${who}privateStaticMethod()
- }
- }
- private static privateStaticMethod() {
- 'hello world'
+ assertScript """
+ class C {
+ def publicMethod() {
+ [1].each {
+ ${who}privateStaticMethod()
}
}
+ private static privateStaticMethod() {
+ 'hello world'
+ }
+ }
- class D extends C {
- void test() {
- publicMethod()
- }
+ class D extends C {
+ void test() {
+ publicMethod()
}
+ }
- new D().test()
- """
- }
+ new D().test()
+ """
}
// GROOVY-3142, GROOVY-5438, GROOVY-6335, GROOVY-11128
- @Test
- void testClosureAccessToEnclosingClassPrivateField() {
- for (who in ['this.@', 'this.', 'owner.', 'thisObject.', '']) {
- assertScript """
- class C {
- String data
- C(arg) {
- arg.each() { ${who}data = it } // MissingFieldException
- }
+ @ParameterizedTest
+ @ValueSource(strings=['this.@', 'this.', 'owner.', 'thisObject.', ''])
+ void testClosureAccessToEnclosingClassPrivateField(String who) {
+ assertScript """
+ class C {
+ String data
+ C(arg) {
+ arg.each() { ${who}data = it } // MissingFieldException
}
+ }
- class D extends C {
- D(arg) {
- super(arg)
- }
+ class D extends C {
+ D(arg) {
+ super(arg)
}
+ }
- new D(["test"])
- """
+ new D(["test"])
+ """
- assertScript """
- class C {
- private String data
- C(arg) {
- arg.each() { ${who}data = it } // ReadOnlyPropertyException
- }
- String getData() { this.@data }
- private void setData(String value) { this.@data = value }
+ assertScript """
+ class C {
+ private String data
+ C(arg) {
+ arg.each() { ${who}data = it } // ReadOnlyPropertyException
}
+ String getData() { this.@data }
+ private void setData(String value) { this.@data = value }
+ }
- class D extends C {
- D(arg) {
- super(arg)
- }
+ class D extends C {
+ D(arg) {
+ super(arg)
}
+ }
- new D(["test"])
- """
+ new D(["test"])
+ """
- assertScript """
- class C {
- private String string = 'foo'
- def test(List<String> strings) {
- strings.collect { ${who}string + it }
- }
+ assertScript """
+ class C {
+ private String string = 'foo'
+ def test(List<String> strings) {
+ strings.collect { ${who}string + it }
}
+ }
- def result = new C().test(['bar','baz'])
- assert result == ['foobar','foobaz']
+ def result = new C().test(['bar','baz'])
+ assert result == ['foobar','foobaz']
- class D extends C {
- }
+ class D extends C {
+ }
+
+ result = new D().test(['bar','baz'])
+ assert result == ['foobar','foobaz']
+ """
- result = new D().test(['bar','baz'])
- assert result == ['foobar','foobaz']
- """
-
- assertScript """
- @groovy.util.logging.Log
- class C {
- void test() {
- 1.times {
- ${who}log.info('sth')
- }
+ assertScript """
+ @groovy.util.logging.Log
+ class C {
+ void test() {
+ 1.times {
+ ${who}log.info('sth')
}
}
+ }
- class D extends C {
- }
+ class D extends C {
+ }
- new D().test()
- """
- }
+ new D().test()
+ """
}
}
diff --git a/src/test/groovy/groovy/PropertyTest.groovy b/src/test/groovy/groovy/PropertyTest.groovy
index 761fcde041..2b2547912e 100644
--- a/src/test/groovy/groovy/PropertyTest.groovy
+++ b/src/test/groovy/groovy/PropertyTest.groovy
@@ -831,24 +831,6 @@ final class PropertyTest {
'''
}
- // GROOVY-9618
- @Test
- void testPropertyAndStaticUppercaseFieldPriority() {
- assertScript '''
- class A {
- public static X = 1
- static getX() { 2 }
- static class B { }
- }
- class C extends A.B {
- def test() {
- X
- }
- }
- assert new C().test() == 2
- '''
- }
-
//--------------------------------------------------------------------------
static class Base {
diff --git a/src/test/groovy/org/codehaus/groovy/transform/packageScope/PackageScopeTransformTest.groovy b/src/test/groovy/org/codehaus/groovy/transform/packageScope/PackageScopeTransformTest.groovy
index 12f3a5e6e6..ee3ccaa267 100644
--- a/src/test/groovy/org/codehaus/groovy/transform/packageScope/PackageScopeTransformTest.groovy
+++ b/src/test/groovy/org/codehaus/groovy/transform/packageScope/PackageScopeTransformTest.groovy
@@ -191,7 +191,7 @@ final class PackageScopeTransformTest {
import groovy.transform.PackageScope
@CompileStatic
- class Test {
+ class Outer {
@PackageScope static final String S = 'S'
protected static final String T = 'T'
private static final String U = 'U'
@@ -202,7 +202,7 @@ final class PackageScopeTransformTest {
}
}
- assert new Test.Inner().method() == 'STU'
+ assert new Outer.Inner().method() == 'STU'
'''
}
}
(groovy) 01/02: next step
Posted by em...@apache.org.
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
commit 02a8b0edb60ce4843871f6db5936e604731988c1
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun Feb 1 09:53:17 2026 -0600
next step
---
src/main/java/groovy/lang/MetaClassImpl.java | 54 +++++++++--------
.../groovy/classgen/EnumCompletionVisitor.java | 38 ------------
.../classgen/InnerClassCompletionVisitor.java | 10 ++--
src/test/groovy/bugs/Groovy7938Bug.groovy | 70 ----------------------
src/test/groovy/gls/enums/EnumTest.groovy | 17 ++++++
.../groovy/gls/innerClass/InnerClassTest.groovy | 53 ++++++++++++++++
6 files changed, 104 insertions(+), 138 deletions(-)
diff --git a/src/main/java/groovy/lang/MetaClassImpl.java b/src/main/java/groovy/lang/MetaClassImpl.java
index bf6d78f4c7..6df885bcb3 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -380,7 +380,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
inheritInterfaceNewMetaMethods(interfaces);
- if (isGroovyObject) {
+ if (isGroovyObject()) {
metaMethodIndex.copyMethodsToSuper(); // methods --> methodsForSuper
connectMultimethods(superClasses, firstGroovySuper);
removeMultimethodsOverloadedWithPrivateMethods();
@@ -662,7 +662,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
}
if (firstGroovy == null) {
- return isGroovyObject ? theCachedClass.getCachedSuperClass() : theCachedClass;
+ return isGroovyObject() ? theCachedClass.getCachedSuperClass() : theCachedClass;
}
// Closure for closures and GroovyObjectSupport for extenders (including Closure)
if (firstGroovy.getTheClass() == GroovyObjectSupport.class) {
@@ -1490,13 +1490,15 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
try {
return invokeStaticMissingMethod(theClass, methodName, nonNullArguments);
} catch (MissingMethodException missing) {
- Class<?> outerClass = theClass.getEnclosingClass();
- if (outerClass != null) {
- try {
+ if (isGroovyObject()) { // GROOVY-11823, et al.
+ var outerClass = theClass.getEnclosingClass();
+ if (outerClass != null && object instanceof Class) {
MetaClass omc = registry.getMetaClass(outerClass);
- return omc.invokeStaticMethod(outerClass, methodName, arguments);
- } catch (MissingMethodException mme) {
- missing.addSuppressed(mme);
+ try {
+ return omc.invokeStaticMethod(outerClass, methodName, arguments);
+ } catch (MissingMethodException mme) {
+ missing.addSuppressed(mme);
+ }
}
}
throw missing;
@@ -1928,13 +1930,15 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
// try $static_propertyMissing / throw MissingPropertyException
return invokeStaticMissingProperty(receiver, name, null, true);
} catch (MissingPropertyException missing) {
- Class<?> outerClass = theClass.getEnclosingClass();
- if (outerClass != null && sender.isNestmateOf(outerClass)) {
- try {
- MetaClass omc = registry.getMetaClass(outerClass);
- return omc.getProperty(sender, outerClass, name, false, false);
- } catch (MissingPropertyException mpe) {
- missing.addSuppressed(mpe);
+ if (isGroovyObject()) { // GROOVY-11823, et al.
+ var outerClass = theClass.getEnclosingClass();
+ if (outerClass != null && sender.isNestmateOf(outerClass)) {
+ try {
+ MetaClass omc = registry.getMetaClass(outerClass);
+ return omc.getProperty(sender, outerClass, name, false, false);
+ } catch (MissingPropertyException mpe) {
+ missing.addSuppressed(mpe);
+ }
}
}
throw missing;
@@ -2082,7 +2086,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
} catch (NoSuchFieldException e) {
}
}
- return omc.getProperty(outerClass, outer, name, false, false);
+ return omc.getProperty(outerClass, outer, getName(), false, false);
} catch (MissingPropertyException suppressed) {
mpe.addSuppressed(suppressed);
} catch (Throwable t) {
@@ -2837,14 +2841,16 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
try {
invokeStaticMissingProperty(object, name, newValue, false);
} catch (MissingPropertyException missing) {
- Class<?> outerClass = theClass.getEnclosingClass();
- if (outerClass != null && sender.isNestmateOf(outerClass)) {
- try {
- MetaClass omc = registry.getMetaClass(outerClass);
- omc.setProperty(sender, outerClass, name, newValue, false, false);
- return;
- } catch (MissingPropertyException mpe) {
- missing.addSuppressed(mpe);
+ if (isGroovyObject()) { // GROOVY-11823, et al.
+ var outerClass = theClass.getEnclosingClass();
+ if (outerClass != null && sender.isNestmateOf(outerClass)) {
+ try {
+ MetaClass omc = registry.getMetaClass(outerClass);
+ omc.setProperty(sender, outerClass, name, newValue, false, false);
+ return;
+ } catch (MissingPropertyException mpe) {
+ missing.addSuppressed(mpe);
+ }
}
}
throw missing;
diff --git a/src/main/java/org/codehaus/groovy/classgen/EnumCompletionVisitor.java b/src/main/java/org/codehaus/groovy/classgen/EnumCompletionVisitor.java
index 87b8fa71d3..cd12c3b993 100644
--- a/src/main/java/org/codehaus/groovy/classgen/EnumCompletionVisitor.java
+++ b/src/main/java/org/codehaus/groovy/classgen/EnumCompletionVisitor.java
@@ -53,7 +53,6 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.params;
import static org.codehaus.groovy.transform.sc.StaticCompilationVisitor.isStaticallyCompiled;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
-import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
/**
@@ -222,19 +221,6 @@ public class EnumCompletionVisitor extends ClassCodeVisitorSupport {
}
);
- visitor.addMissingHandler(
- innerClass,
- "$static_methodMissing",
- ACC_PUBLIC | ACC_STATIC,
- OBJECT_TYPE,
- params(param(STRING_TYPE, "name"), param(OBJECT_TYPE, "args")),
- (methodBody, parameters) -> {
- InnerClassVisitorHelper.setMethodDispatcherCode(methodBody, classX(outerClass), parameters);
- }
- );
-
- //
-
visitor.addMissingHandler(
innerClass,
"propertyMissing",
@@ -246,19 +232,6 @@ public class EnumCompletionVisitor extends ClassCodeVisitorSupport {
}
);
- visitor.addMissingHandler(
- innerClass,
- "$static_propertyMissing",
- ACC_PUBLIC | ACC_STATIC,
- OBJECT_TYPE,
- params(param(STRING_TYPE, "name")),
- (methodBody, parameters) -> {
- InnerClassVisitorHelper.setPropertyGetterDispatcher(methodBody, classX(outerClass), parameters);
- }
- );
-
- //
-
visitor.addMissingHandler(
innerClass,
"propertyMissing",
@@ -269,16 +242,5 @@ public class EnumCompletionVisitor extends ClassCodeVisitorSupport {
InnerClassVisitorHelper.setPropertySetterDispatcher(methodBody, classX(outerClass), parameters);
}
);
-
- visitor.addMissingHandler(
- innerClass,
- "$static_propertyMissing",
- ACC_PUBLIC | ACC_STATIC,
- VOID_TYPE,
- params(param(STRING_TYPE, "name"), param(OBJECT_TYPE, "value")),
- (methodBody, parameters) -> {
- InnerClassVisitorHelper.setPropertySetterDispatcher(methodBody, classX(outerClass), parameters);
- }
- );
}
}
diff --git a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
index 0ea8845ba2..3217289209 100644
--- a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
+++ b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
@@ -54,6 +54,7 @@ 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;
@@ -64,7 +65,6 @@ 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.propX;
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;
@@ -110,7 +110,7 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper {
}
if (node instanceof InnerClassNode innerClass) {
- thisField = node.getField("this$0");
+ 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);
@@ -319,14 +319,12 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper {
Parameter catchParam = param(OBJECT_TYPE, "notFound"); // dummy type
ClassNode exceptionT;
Expression newException;
- Expression selfType = varX("this");
- if ((modifiers & ACC_STATIC) == 0) selfType = callX(selfType, "getClass");
if (methodName.endsWith("methodMissing")) {
exceptionT = ClassHelper.make(groovy.lang.MissingMethodException.class);
- newException = ctorX(exceptionT, args(propX(varX(catchParam),"method"), selfType, propX(varX(catchParam),"arguments")));
+ 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(propX(varX(catchParam), "property"), selfType, propX(varX(catchParam), "cause")));
+ newException = ctorX(exceptionT, args(callX(varX(catchParam),"getProperty"), callThisX("getClass"), callX(varX(catchParam),"getCause")));
}
catchParam.setType(exceptionT);
catchParam.setOriginType(exceptionT);
diff --git a/src/test/groovy/bugs/Groovy7938Bug.groovy b/src/test/groovy/bugs/Groovy7938Bug.groovy
deleted file mode 100644
index a7476f4f60..0000000000
--- a/src/test/groovy/bugs/Groovy7938Bug.groovy
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package bugs
-
-import groovy.test.GroovyTestCase
-
-class Groovy7938Bug extends GroovyTestCase {
- void testClassUsageInInterfaceDef() {
- assertScript """
- class Outer {
- static Integer fooCount = 0
- Integer barCount = 0
- static void incFoo() { fooCount++ }
- void incBar() { barCount++ }
- static class Nested {
- static void nestedIncFoo() { incFoo() }
- static class NestedNested {
- static void nestedNestedIncFoo() { incFoo() }
- }
- }
- Inner innerFactory() { new Inner() }
- class Inner {
- InnerInner innerInnerFactory() { new InnerInner() }
- void innerIncFoo() { incFoo() }
- static void staticInnerIncFoo() { incFoo() }
- class InnerInner {
- void innerInnerIncFoo() { incFoo() }
- static void staticInnerInnerIncFoo() { incFoo() }
- }
- }
- }
- Outer.incFoo()
- Outer.Nested.nestedIncFoo()
- Outer.Nested.NestedNested.nestedNestedIncFoo()
- assert Outer.fooCount == 3
- new Outer().with {
- incBar()
- incFoo()
- innerFactory().with {
- incBar()
- innerIncFoo()
- staticInnerIncFoo()
- innerInnerFactory().with {
- incBar()
- innerInnerIncFoo()
- staticInnerInnerIncFoo()
- }
- }
- assert barCount == 3
- assert fooCount == 8
- }
- """
- }
-}
diff --git a/src/test/groovy/gls/enums/EnumTest.groovy b/src/test/groovy/gls/enums/EnumTest.groovy
index d0e1255d3d..c490ea04d6 100644
--- a/src/test/groovy/gls/enums/EnumTest.groovy
+++ b/src/test/groovy/gls/enums/EnumTest.groovy
@@ -338,6 +338,23 @@ class EnumTest extends CompilableTestSupport {
'''
}
+ void testInnerEnumInitWithUnqualifiedOuterClassValue() {
+ assertScript '''
+ class C {
+ private static int ONE() { 1 }
+ enum E {
+ FOO(1 + ONE())
+ final value
+ E(value) {
+ this.value = value
+ }
+ }
+ }
+
+ assert C.E.FOO.value == 2
+ '''
+ }
+
// GROOVY-3693
void testStaticFieldInitValuesInAStaticBlock() {
// trigger enum class load to test it - asserts are present in the enum
diff --git a/src/test/groovy/gls/innerClass/InnerClassTest.groovy b/src/test/groovy/gls/innerClass/InnerClassTest.groovy
index 2aac220e3f..a4fdb45a7d 100644
--- a/src/test/groovy/gls/innerClass/InnerClassTest.groovy
+++ b/src/test/groovy/gls/innerClass/InnerClassTest.groovy
@@ -1244,6 +1244,59 @@ final class InnerClassTest {
'''
}
+ // GROOVY-7938
+ @Test
+ void testUsageOfOuterMethod9() {
+ assertScript '''
+ class Outer {
+ Integer barCount = 0
+ static Integer fooCount = 0
+ void incBar() { barCount++ }
+ static void incFoo() { fooCount++ }
+ Inner innerFactory() { new Inner() }
+
+ static class Nested {
+ static void nestedIncFoo() { incFoo() }
+ static class NestedNested {
+ static void nestedNestedIncFoo() { incFoo() }
+ }
+ }
+ class Inner {
+ void innerIncFoo() { incFoo() }
+ static void staticInnerIncFoo() { incFoo() }
+ InnerInner innerInnerFactory() { new InnerInner() }
+
+ class InnerInner {
+ void innerInnerIncFoo() { incFoo() }
+ static void staticInnerInnerIncFoo() { incFoo() }
+ }
+ }
+ }
+
+ Outer.incFoo()
+ Outer.Nested.nestedIncFoo()
+ Outer.Nested.NestedNested.nestedNestedIncFoo()
+ assert Outer.fooCount == 3
+
+ new Outer().with {
+ incBar()
+ incFoo()
+ innerFactory().with {
+ incBar()
+ innerIncFoo()
+ staticInnerIncFoo()
+ innerInnerFactory().with {
+ incBar()
+ innerInnerIncFoo()
+ staticInnerInnerIncFoo()
+ }
+ }
+ assert barCount == 3
+ assert fooCount == 8
+ }
+ '''
+ }
+
@Test
void testUsageOfOuterMethodOverridden() {
assertScript '''