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 2fc8212069 GROOVY-11822: allow override `Object
propertyMissing(String, Object)`
2fc8212069 is described below
commit 2fc8212069c53713f8d07742629a57ad3b1cd207
Author: Eric Milles <[email protected]>
AuthorDate: Sun Dec 28 15:41:32 2025 -0600
GROOVY-11822: allow override `Object propertyMissing(String, Object)`
---
src/main/java/org/codehaus/groovy/ast/ClassNode.java | 18 +++++++++++-------
.../groovy/classgen/InnerClassCompletionVisitor.java | 16 ++++++++++++----
src/test/groovy/gls/innerClass/InnerClassTest.groovy | 20 ++++++++++++++++++++
3 files changed, 43 insertions(+), 11 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/ast/ClassNode.java
b/src/main/java/org/codehaus/groovy/ast/ClassNode.java
index a5a789d4d4..96511c4b52 100644
--- a/src/main/java/org/codehaus/groovy/ast/ClassNode.java
+++ b/src/main/java/org/codehaus/groovy/ast/ClassNode.java
@@ -31,7 +31,6 @@ import org.codehaus.groovy.ast.stmt.ExpressionStatement;
import org.codehaus.groovy.ast.stmt.Statement;
import org.codehaus.groovy.ast.tools.ParameterUtils;
import org.codehaus.groovy.control.CompilePhase;
-import org.codehaus.groovy.runtime.ArrayGroovyMethods;
import org.codehaus.groovy.transform.ASTTransformation;
import org.codehaus.groovy.transform.GroovyASTTransformation;
import org.codehaus.groovy.vmplugin.VMPluginFactory;
@@ -54,6 +53,7 @@ import java.util.stream.Stream;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
import static org.apache.groovy.ast.tools.MethodNodeUtils.getCodeAsBlock;
+import static org.codehaus.groovy.runtime.ArrayGroovyMethods.asBoolean;
import static
org.codehaus.groovy.transform.RecordTypeASTTransformation.recordNative;
import static org.codehaus.groovy.transform.trait.Traits.isTrait;
import static org.objectweb.asm.Opcodes.ACC_ABSTRACT;
@@ -911,10 +911,11 @@ public class ClassNode extends AnnotatedNode {
* @return method node or null
*/
public MethodNode getDeclaredMethod(String name, Parameter[] parameters) {
- boolean zeroParameters = !ArrayGroovyMethods.asBoolean(parameters);
+ boolean zeroParameters = !asBoolean(parameters);
for (MethodNode method : getDeclaredMethods(name)) {
- if (zeroParameters ? method.getParameters().length == 0
- : parametersEqual(method.getParameters(), parameters)) {
+ Parameter[] methodParameters = method.getParameters();
+ if (zeroParameters ? methodParameters.length == 0
+ : parametersEqual(methodParameters, parameters)) {
return method;
}
}
@@ -928,8 +929,11 @@ public class ClassNode extends AnnotatedNode {
* @return method node or null
*/
public MethodNode getMethod(String name, Parameter[] parameters) {
+ boolean zeroParameters = !asBoolean(parameters);
for (MethodNode method : getMethods(name)) {
- if (parametersEqual(method.getParameters(), parameters)) {
+ Parameter[] methodParameters = method.getParameters();
+ if (zeroParameters ? methodParameters.length == 0
+ : parametersEqual(methodParameters, parameters)) {
return method;
}
}
@@ -1088,7 +1092,7 @@ public class ClassNode extends AnnotatedNode {
}
}
// GROOVY-11381:
- if (getterMethod == null &&
ArrayGroovyMethods.asBoolean(getInterfaces())) {
+ if (getterMethod == null && asBoolean(getInterfaces())) {
for (ClassNode anInterface : getAllInterfaces()) {
MethodNode method =
anInterface.getDeclaredMethod(getterName, Parameter.EMPTY_ARRAY);
if (method != null && method.isDefault() &&
(booleanReturnOnly ? ClassHelper.isPrimitiveBoolean(method.getReturnType()) :
!method.isVoidMethod())) {
@@ -1189,7 +1193,7 @@ public class ClassNode extends AnnotatedNode {
}
}
-faces: if (method == null && ArrayGroovyMethods.asBoolean(getInterfaces())) {
// GROOVY-11323
+faces: if (method == null && asBoolean(getInterfaces())) { // GROOVY-11323
for (ClassNode cn : getAllInterfaces()) {
for (MethodNode mn : cn.getDeclaredMethods(name)) {
if (mn.isPublic() && !mn.isStatic() &&
hasCompatibleNumberOfArgs(mn, nArgs) && (nArgs == 0
diff --git
a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
index 92186e9473..4c7ade06d9 100644
---
a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
+++
b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
@@ -40,12 +40,14 @@ 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;
@@ -75,12 +77,12 @@ 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;
-import static org.objectweb.asm.Opcodes.RETURN;
public class InnerClassCompletionVisitor extends InnerClassVisitorHelper {
@@ -266,10 +268,15 @@ public class InnerClassCompletionVisitor extends
InnerClassVisitorHelper {
}
);
+ 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,
- VOID_TYPE,
+ returnType,
params(param(STRING_TYPE, "name"), param(OBJECT_TYPE,
"value")),
(methodBody, parameters) -> {
if (isStatic) {
@@ -283,7 +290,8 @@ public class InnerClassCompletionVisitor extends
InnerClassVisitorHelper {
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL,
outerClassInternalName, "this$dist$set$" + outerClassDistance,
"(Ljava/lang/String;Ljava/lang/Object;)V", false);
- mv.visitInsn(RETURN);
+ if
(!ClassHelper.isPrimitiveVoid(returnType)) mv.visitInsn(ACONST_NULL);
+ BytecodeHelper.doReturn(mv,
returnType);
}
})
);
@@ -336,7 +344,7 @@ public class InnerClassCompletionVisitor extends
InnerClassVisitorHelper {
);
}
- void addMissingHandler(final InnerClassNode innerClass, final
String methodName, final int modifiers,
+ /* */ 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 ed3645535d..a4dfd08bc7 100644
--- a/src/test/groovy/gls/innerClass/InnerClassTest.groovy
+++ b/src/test/groovy/gls/innerClass/InnerClassTest.groovy
@@ -23,6 +23,8 @@ import org.codehaus.groovy.control.CompilationFailedException
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit
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
@@ -2219,6 +2221,24 @@ final class InnerClassTest {
'''
}
+ // GROOVY-11822
+ @ParameterizedTest
+ @ValueSource(strings=['void','Void','def','Object'])
+ void testNestedPropertyHandling4(String returnType) {
+ def err = shouldFail """
+ class Upper {
+ $returnType propertyMissing(String name, Object value) {
+ }
+ }
+ class Outer {
+ static class Inner extends Upper {
+ }
+ }
+ new Outer.Inner().missing = 42
+ """
+ assert err =~ /No such property: missing for class: Outer.Inner/
+ }
+
// GROOVY-7312
@Test
void testInnerClassOfInterfaceIsStatic() {