This is an automated email from the ASF dual-hosted git repository. emilles pushed a commit to branch GROOVY-11823 in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 6b526a9def70971e98d22a9a1cfeb7da59a3f9ea Author: Eric Milles <[email protected]> AuthorDate: Sun Dec 28 19:36:56 2025 -0600 GROOVY-11823: try `super.propertyMissing(name)` before outer class --- src/main/java/groovy/lang/MetaClassImpl.java | 4 +- .../classgen/InnerClassCompletionVisitor.java | 50 +++++++++++++++++++--- .../groovy/gls/innerClass/InnerClassTest.groovy | 20 +++++++++ 3 files changed, 65 insertions(+), 9 deletions(-) diff --git a/src/main/java/groovy/lang/MetaClassImpl.java b/src/main/java/groovy/lang/MetaClassImpl.java index 767ad24801..541fcbd3fb 100644 --- a/src/main/java/groovy/lang/MetaClassImpl.java +++ b/src/main/java/groovy/lang/MetaClassImpl.java @@ -866,8 +866,8 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass { } catch (InvokerInvocationException iie) { boolean shouldHandle = isGetter && propertyMissingGet != null; if (!shouldHandle) shouldHandle = !isGetter && propertyMissingSet != null; - if (shouldHandle && iie.getCause() instanceof MissingPropertyException) { - throw (MissingPropertyException) iie.getCause(); + if (shouldHandle && iie.getCause() instanceof MissingPropertyException mpe) { + throw mpe; } throw iie; } diff --git a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java index 4c7ade06d9..71df5ddfdb 100644 --- a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java +++ b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java @@ -18,8 +18,6 @@ */ package org.codehaus.groovy.classgen; -import groovy.lang.MissingMethodException; -import groovy.lang.MissingPropertyException; import groovy.transform.CompileStatic; import groovy.transform.stc.POJO; import org.codehaus.groovy.ast.ClassHelper; @@ -29,11 +27,13 @@ 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.ConstantExpression; 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.Statement; import org.codehaus.groovy.ast.stmt.TryCatchStatement; import org.codehaus.groovy.classgen.asm.BytecodeHelper; import org.codehaus.groovy.control.CompilationUnit; @@ -56,17 +56,22 @@ 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.callSuperX; 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.constX; 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.ifS; +import static org.codehaus.groovy.ast.tools.GeneralUtils.notX; 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.returnS; 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; @@ -348,25 +353,56 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper { final ClassNode returnType, final Parameter[] parameters, final BiConsumer<BlockStatement, Parameter[]> consumer) { MethodNode method = innerClass.getDeclaredMethod(methodName, parameters); if (method == null) { + ClassNode mme = ClassHelper.make(groovy.lang.MissingMethodException.class); + ClassNode mpe = ClassHelper.make(groovy.lang.MissingPropertyException.class); + + // 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; Expression selfType = varX("this"); if ((modifiers & ACC_STATIC) == 0) selfType = callX(selfType, "getClass"); if (methodName.endsWith("methodMissing")) { - exceptionT = ClassHelper.make(MissingMethodException.class); + exceptionT = mme; newException = ctorX(exceptionT, args(propX(varX(catchParam),"method"), selfType, propX(varX(catchParam),"arguments"))); } else { - exceptionT = ClassHelper.make(MissingPropertyException.class); + exceptionT = mpe; newException = ctorX(exceptionT, args(propX(varX(catchParam), "property"), selfType, propX(varX(catchParam), "cause"))); } - catchParam.setType(exceptionT); catchParam.setOriginType(exceptionT); BlockStatement handleMissing = block(); consumer.accept(handleMissing, parameters); - TryCatchStatement methodBody = tryCatchS(handleMissing); - methodBody.addCatch(catchS(catchParam, throwS(newException))); + TryCatchStatement tryCatch = tryCatchS(handleMissing); + tryCatch.addCatch(catchS(catchParam, throwS(newException))); + + Statement methodBody = tryCatch; + if ((modifiers & ACC_STATIC) == 0) { // GROOVY-11823 + // if (!"methodMissing".equals(name)) + Expression noRecursion = notX(callX(constX(methodName), "equals", varX(parameters[0]))); + // try { + // return super.methodMissing(name,args) + // } catch (MissingMethodException ignore) { + // } catch (MissingPropertyException ignore) { + // } + BlockStatement trySuperClass = block(); + if (!ClassHelper.isPrimitiveVoid(returnType)) { + trySuperClass.addStatement(returnS(callSuperX(methodName, args(parameters)))); + } else { + trySuperClass.addStatement(stmt(callSuperX(methodName, args(parameters)))); + trySuperClass.addStatement(returnS(ConstantExpression.EMPTY_EXPRESSION)); + } + tryCatch = tryCatchS(trySuperClass); + tryCatch.addCatch(catchS(param(mme, "ignore"), block())); + if (methodName.equals("propertyMissing")) + tryCatch.addCatch(catchS(param(mpe, "ignore"), block())); + + methodBody = block(ifS(noRecursion, tryCatch), methodBody); + } innerClass.addSyntheticMethod(methodName, modifiers, returnType, parameters, ClassNode.EMPTY_ARRAY, methodBody); // if there is a user-defined method, add compiler error and continue diff --git a/src/test/groovy/gls/innerClass/InnerClassTest.groovy b/src/test/groovy/gls/innerClass/InnerClassTest.groovy index a4dfd08bc7..5d58874aab 100644 --- a/src/test/groovy/gls/innerClass/InnerClassTest.groovy +++ b/src/test/groovy/gls/innerClass/InnerClassTest.groovy @@ -2228,6 +2228,7 @@ final class InnerClassTest { def err = shouldFail """ class Upper { $returnType propertyMissing(String name, Object value) { + throw new MissingPropertyException(name, getClass()) } } class Outer { @@ -2239,6 +2240,25 @@ final class InnerClassTest { assert err =~ /No such property: missing for class: Outer.Inner/ } + // 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-7312 @Test void testInnerClassOfInterfaceIsStatic() {
