This is an automated email from the ASF dual-hosted git repository. emilles pushed a commit to branch GROOVY_4_0_X in repository https://gitbox.apache.org/repos/asf/groovy.git
commit bd451f6ec108db14b0b46dae4c58e318cf19baa3 Author: Eric Milles <[email protected]> AuthorDate: Wed Feb 22 18:24:29 2023 -0600 GROOVY-10935: throw missing exception from inner class --- .../classgen/InnerClassCompletionVisitor.java | 45 +++++++++++++++++----- src/test/gls/innerClass/InnerClassTest.groovy | 39 ++++++++++++------- src/test/groovy/bugs/Groovy8678.groovy | 8 ++-- 3 files changed, 65 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java index 7b01bfeab4..37ea270490 100644 --- a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java +++ b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java @@ -18,6 +18,8 @@ */ 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; @@ -32,6 +34,7 @@ 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; @@ -48,15 +51,22 @@ 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.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.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; import static org.codehaus.groovy.ast.tools.GeneralUtils.varX; import static org.objectweb.asm.Opcodes.ACC_PUBLIC; import static org.objectweb.asm.Opcodes.ACC_STATIC; @@ -212,7 +222,7 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper { final String outerClassInternalName = getInternalName(outerClass, isStatic); final String outerClassDescriptor = getTypeDescriptor(outerClass, isStatic); - addSyntheticMethod(node, + addMissingHandler(node, "methodMissing", ACC_PUBLIC, OBJECT_TYPE, @@ -237,7 +247,7 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper { } ); - addSyntheticMethod(node, + addMissingHandler(node, "$static_methodMissing", ACC_PUBLIC | ACC_STATIC, OBJECT_TYPE, @@ -247,7 +257,7 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper { } ); - addSyntheticMethod(node, + addMissingHandler(node, "propertyMissing", ACC_PUBLIC, VOID_TYPE, @@ -272,7 +282,7 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper { } ); - addSyntheticMethod(node, + addMissingHandler(node, "$static_propertyMissing", ACC_PUBLIC | ACC_STATIC, VOID_TYPE, @@ -282,7 +292,7 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper { } ); - addSyntheticMethod(node, + addMissingHandler(node, "propertyMissing", ACC_PUBLIC, OBJECT_TYPE, @@ -306,7 +316,7 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper { } ); - addSyntheticMethod(node, + addMissingHandler(node, "$static_propertyMissing", ACC_PUBLIC | ACC_STATIC, OBJECT_TYPE, @@ -317,12 +327,29 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper { ); } - private void addSyntheticMethod(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) { - BlockStatement methodBody = block(); - consumer.accept(methodBody, parameters); + 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); + newException = ctorX(exceptionT, args(propX(varX(catchParam),"method"), selfType, propX(varX(catchParam),"arguments"))); + } else { + exceptionT = ClassHelper.make(MissingPropertyException.class); + 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))); 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/gls/innerClass/InnerClassTest.groovy b/src/test/gls/innerClass/InnerClassTest.groovy index f21681560e..102f148469 100644 --- a/src/test/gls/innerClass/InnerClassTest.groovy +++ b/src/test/gls/innerClass/InnerClassTest.groovy @@ -1937,8 +1937,8 @@ final class InnerClassTest { @Test // GROOVY-8274 void testMissingMethodHandling() { assertScript ''' - class A { - class B { + class Outer { + class Inner { def methodMissing(String name, args) { return name } @@ -1946,12 +1946,12 @@ final class InnerClassTest { def test(Closure c) { c.resolveStrategy = Closure.DELEGATE_ONLY - c.delegate = new B() + c.delegate = new Inner() c.call() } } - def x = new A().test { -> + def x = new Outer().test { -> hello() // missing } assert x == 'hello' @@ -1964,21 +1964,32 @@ final class InnerClassTest { class Outer { private static List items = [] void add() { items.add('Outer') } - static class Nested { - void add() { items.add('Nested') } - static class NestedNested { - void add() { items.add('NestedNested') } - void set() { items = ['Overridden'] } + static class Inner { + void add() { items.add('Inner') } + static class InnerInner { + void add() { items.add('InnerInner') } + void set() { items = ['Overwritten'] } } } } new Outer().add() - new Outer.Nested().add() - new Outer.Nested.NestedNested().add() - assert Outer.items == ["Outer", "Nested", "NestedNested"] - new Outer.Nested.NestedNested().set() - assert Outer.items == ["Overridden"] + new Outer.Inner().add() + new Outer.Inner.InnerInner().add() + assert Outer.items == ['Outer', 'Inner', 'InnerInner'] + new Outer.Inner.InnerInner().set() + assert Outer.items == ['Overwritten'] + ''' + } + + @Test // GROOVY-10935 + void testNestedPropertyHandling2() { + def err = shouldFail ''' + class Outer { + static class Inner {} + } + new Outer.Inner().missing ''' + assert err =~ /No such property: missing for class: Outer.Inner/ } @Test // GROOVY-7312 diff --git a/src/test/groovy/bugs/Groovy8678.groovy b/src/test/groovy/bugs/Groovy8678.groovy index 4657789959..014e2a0f0e 100644 --- a/src/test/groovy/bugs/Groovy8678.groovy +++ b/src/test/groovy/bugs/Groovy8678.groovy @@ -81,16 +81,16 @@ final class Groovy8678 { def x = new WithMethods() def throwable = shouldFail () -> x.'42' assert throwable.class == MissingPropertyException.class - assert throwable.message == 'No such property: 42 for class: groovy.bugs.Groovy8678' + assert throwable.message.startsWith('No such property: 42 for class: groovy.bugs.Groovy8678$WithMethods') throwable = shouldFail () -> x.'42d' assert throwable.class == MissingPropertyException.class - assert throwable.message == 'No such property: 42d for class: groovy.bugs.Groovy8678' + assert throwable.message.startsWith('No such property: 42d for class: groovy.bugs.Groovy8678$WithMethods') throwable = shouldFail () -> x.'84'() assert throwable.class == MissingMethodException.class - assert throwable.message.startsWith('No signature of method: groovy.bugs.Groovy8678.84() is applicable for argument types: () values: []') + assert throwable.message.startsWith('No signature of method: groovy.bugs.Groovy8678$WithMethods.84() is applicable for argument types: () values: []') throwable = shouldFail () -> x.'84f'() assert throwable.class == MissingMethodException.class - assert throwable.message.startsWith('No signature of method: groovy.bugs.Groovy8678.84f() is applicable for argument types: () values: []') + assert throwable.message.startsWith('No signature of method: groovy.bugs.Groovy8678$WithMethods.84f() is applicable for argument types: () values: []') } @Test
