You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2020/06/24 00:21:11 UTC
[groovy] branch master updated: GROOVY-8274: allow user-supplied
methodMissing in non-static inner class
This is an automated email from the ASF dual-hosted git repository.
sunlan 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 bcce7be GROOVY-8274: allow user-supplied methodMissing in non-static inner class
bcce7be is described below
commit bcce7bee5e69fa3ae945bfec0283e8fdb03a5cda
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun Jun 21 22:31:29 2020 -0500
GROOVY-8274: allow user-supplied methodMissing in non-static inner class
---
.../classgen/InnerClassCompletionVisitor.java | 357 ++++++++-------------
src/test/gls/innerClass/InnerClassTest.groovy | 24 ++
2 files changed, 164 insertions(+), 217 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
index 077d84a..231dcae 100644
--- a/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
+++ b/src/main/java/org/codehaus/groovy/classgen/InnerClassCompletionVisitor.java
@@ -18,14 +18,12 @@
*/
package org.codehaus.groovy.classgen;
-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.ClassExpression;
import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.TupleExpression;
@@ -38,21 +36,28 @@ import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.util.List;
+import java.util.function.BiConsumer;
import static org.apache.groovy.ast.tools.ClassNodeUtils.addGeneratedConstructor;
-import static org.apache.groovy.ast.tools.MethodNodeUtils.getCodeAsBlock;
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.block;
+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.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.varX;
public class InnerClassCompletionVisitor extends InnerClassVisitorHelper implements Opcodes {
- private final SourceUnit sourceUnit;
private ClassNode classNode;
- private FieldNode thisField = null;
+ private FieldNode thisField;
+ private final SourceUnit sourceUnit;
private static final String
CLOSURE_INTERNAL_NAME = BytecodeHelper.getClassInternalName(CLOSURE_TYPE),
@@ -69,7 +74,7 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper impleme
@Override
public void visitClass(ClassNode node) {
- this.classNode = node;
+ classNode = node;
thisField = null;
InnerClassNode innerClass = null;
if (!node.isEnum() && !node.isInterface() && node instanceof InnerClassNode) {
@@ -85,7 +90,7 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper impleme
if (node.getInnerClasses().hasNext()) addDispatcherMethods(node);
if (innerClass == null) return;
super.visitClass(node);
- addDefaultMethods(innerClass);
+ addMopMethods(innerClass);
}
@Override
@@ -109,57 +114,40 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper impleme
// add the dispatcher methods
// add method dispatcher
- Parameter[] parameters = new Parameter[]{
- new Parameter(ClassHelper.STRING_TYPE, "name"),
- new Parameter(ClassHelper.OBJECT_TYPE, "args")
- };
+ BlockStatement block = new BlockStatement();
MethodNode method = classNode.addSyntheticMethod(
"this$dist$invoke$" + objectDistance,
- ACC_PUBLIC + ACC_SYNTHETIC,
- ClassHelper.OBJECT_TYPE,
- parameters,
+ ACC_PUBLIC,
+ OBJECT_TYPE,
+ params(param(STRING_TYPE, "name"), param(OBJECT_TYPE, "args")),
ClassNode.EMPTY_ARRAY,
- null
+ block
);
-
- BlockStatement block = new BlockStatement();
- setMethodDispatcherCode(block, VariableExpression.THIS_EXPRESSION, parameters);
- method.setCode(block);
+ setMethodDispatcherCode(block, VariableExpression.THIS_EXPRESSION, method.getParameters());
// add property setter
- parameters = new Parameter[]{
- new Parameter(ClassHelper.STRING_TYPE, "name"),
- new Parameter(ClassHelper.OBJECT_TYPE, "value")
- };
+ block = new BlockStatement();
method = classNode.addSyntheticMethod(
"this$dist$set$" + objectDistance,
- ACC_PUBLIC + ACC_SYNTHETIC,
- ClassHelper.VOID_TYPE,
- parameters,
+ ACC_PUBLIC,
+ VOID_TYPE,
+ params(param(STRING_TYPE, "name"), param(OBJECT_TYPE, "value")),
ClassNode.EMPTY_ARRAY,
- null
+ block
);
-
- block = new BlockStatement();
- setPropertySetterDispatcher(block, VariableExpression.THIS_EXPRESSION, parameters);
- method.setCode(block);
+ setPropertySetterDispatcher(block, VariableExpression.THIS_EXPRESSION, method.getParameters());
// add property getter
- parameters = new Parameter[]{
- new Parameter(ClassHelper.STRING_TYPE, "name")
- };
+ block = new BlockStatement();
method = classNode.addSyntheticMethod(
"this$dist$get$" + objectDistance,
- ACC_PUBLIC + ACC_SYNTHETIC,
- ClassHelper.OBJECT_TYPE,
- parameters,
+ ACC_PUBLIC,
+ OBJECT_TYPE,
+ params(param(STRING_TYPE, "name")),
ClassNode.EMPTY_ARRAY,
- null
+ block
);
-
- block = new BlockStatement();
- setPropertyGetterDispatcher(block, VariableExpression.THIS_EXPRESSION, parameters);
- method.setCode(block);
+ setPropertyGetterDispatcher(block, VariableExpression.THIS_EXPRESSION, method.getParameters());
}
private void getThis(MethodVisitor mv, String classInternalName, String outerClassDescriptor, String innerClassInternalName) {
@@ -173,201 +161,137 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper impleme
}
}
- private void addDefaultMethods(InnerClassNode node) {
+ private void addMopMethods(final InnerClassNode node) {
final boolean isStatic = isStatic(node);
-
- ClassNode outerClass = node.getOuterClass();
+ 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);
- final int objectDistance = getObjectDistance(outerClass);
-
- // add missing method dispatcher
- Parameter[] parameters = new Parameter[]{
- new Parameter(ClassHelper.STRING_TYPE, "name"),
- new Parameter(ClassHelper.OBJECT_TYPE, "args")
- };
-
- String methodName = "methodMissing";
- if (isStatic)
- addCompilationErrorOnCustomMethodNode(node, methodName, parameters);
-
- MethodNode method = node.addSyntheticMethod(
- methodName,
- Opcodes.ACC_PUBLIC,
- ClassHelper.OBJECT_TYPE,
- parameters,
- ClassNode.EMPTY_ARRAY,
- null
- );
- BlockStatement block = new BlockStatement();
- if (isStatic) {
- setMethodDispatcherCode(block, new ClassExpression(outerClass), parameters);
- } else {
- block.addStatement(
- new BytecodeSequence(new BytecodeInstruction() {
- public void visit(MethodVisitor mv) {
- getThis(mv,classInternalName,outerClassDescriptor,outerClassInternalName);
- mv.visitVarInsn(ALOAD, 1);
- mv.visitVarInsn(ALOAD, 2);
- mv.visitMethodInsn(INVOKEVIRTUAL, outerClassInternalName, "this$dist$invoke$" + objectDistance, "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;", false);
- mv.visitInsn(ARETURN);
- }
- })
- );
- }
- method.setCode(block);
-
- // add static missing method dispatcher
- methodName = "$static_methodMissing";
- if (isStatic)
- addCompilationErrorOnCustomMethodNode(node, methodName, parameters);
-
- method = node.addSyntheticMethod(
- methodName,
- Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,
- ClassHelper.OBJECT_TYPE,
- parameters,
- ClassNode.EMPTY_ARRAY,
- null
+ addSyntheticMethod(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);
+ }
+ })
+ );
+ }
+ }
);
- block = new BlockStatement();
- setMethodDispatcherCode(block, new ClassExpression(outerClass), parameters);
- method.setCode(block);
-
- // add property setter dispatcher
- parameters = new Parameter[]{
- new Parameter(ClassHelper.STRING_TYPE, "name"),
- new Parameter(ClassHelper.OBJECT_TYPE, "val")
- };
-
- methodName = "propertyMissing";
- if (isStatic)
- addCompilationErrorOnCustomMethodNode(node, methodName, parameters);
-
- method = node.addSyntheticMethod(
- methodName,
- Opcodes.ACC_PUBLIC,
- ClassHelper.VOID_TYPE,
- parameters,
- ClassNode.EMPTY_ARRAY,
- null
+ addSyntheticMethod(node,
+ "$static_methodMissing",
+ ACC_PUBLIC | ACC_STATIC,
+ OBJECT_TYPE,
+ params(param(STRING_TYPE, "name"), param(OBJECT_TYPE, "args")),
+ (methodBody, parameters) -> {
+ setMethodDispatcherCode(methodBody, classX(outerClass), parameters);
+ }
);
- block = new BlockStatement();
- if (isStatic) {
- setPropertySetterDispatcher(block, new ClassExpression(outerClass), parameters);
- } else {
- block.addStatement(
- new BytecodeSequence(new BytecodeInstruction() {
- public void visit(MethodVisitor mv) {
- getThis(mv,classInternalName,outerClassDescriptor,outerClassInternalName);
- mv.visitVarInsn(ALOAD, 1);
- mv.visitVarInsn(ALOAD, 2);
- mv.visitMethodInsn(INVOKEVIRTUAL, outerClassInternalName, "this$dist$set$" + objectDistance, "(Ljava/lang/String;Ljava/lang/Object;)V", false);
- mv.visitInsn(RETURN);
- }
- })
- );
- }
- method.setCode(block);
-
- // add static property missing setter dispatcher
- methodName = "$static_propertyMissing";
- if (isStatic)
- addCompilationErrorOnCustomMethodNode(node, methodName, parameters);
-
- method = node.addSyntheticMethod(
- methodName,
- Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,
- ClassHelper.VOID_TYPE,
- parameters,
- ClassNode.EMPTY_ARRAY,
- null
+ addSyntheticMethod(node,
+ "propertyMissing",
+ ACC_PUBLIC,
+ VOID_TYPE,
+ 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);
+ mv.visitInsn(RETURN);
+ }
+ })
+ );
+ }
+ }
);
- block = new BlockStatement();
- setPropertySetterDispatcher(block, new ClassExpression(outerClass), parameters);
- method.setCode(block);
-
- // add property getter dispatcher
- parameters = new Parameter[]{
- new Parameter(ClassHelper.STRING_TYPE, "name")
- };
-
- methodName = "propertyMissing";
- if (isStatic)
- addCompilationErrorOnCustomMethodNode(node, methodName, parameters);
-
- method = node.addSyntheticMethod(
- methodName,
- Opcodes.ACC_PUBLIC,
- ClassHelper.OBJECT_TYPE,
- parameters,
- ClassNode.EMPTY_ARRAY,
- null
+ addSyntheticMethod(node,
+ "$static_propertyMissing",
+ ACC_PUBLIC | ACC_STATIC,
+ VOID_TYPE,
+ params(param(STRING_TYPE, "name"), param(OBJECT_TYPE, "value")),
+ (methodBody, parameters) -> {
+ setPropertySetterDispatcher(methodBody, classX(outerClass), parameters);
+ }
);
- block = new BlockStatement();
- if (isStatic) {
- setPropertyGetterDispatcher(block, new ClassExpression(outerClass), parameters);
- } else {
- block.addStatement(
- new BytecodeSequence(new BytecodeInstruction() {
- public void visit(MethodVisitor mv) {
- getThis(mv,classInternalName,outerClassDescriptor,outerClassInternalName);
- mv.visitVarInsn(ALOAD, 1);
- mv.visitMethodInsn(INVOKEVIRTUAL, outerClassInternalName, "this$dist$get$" + objectDistance, "(Ljava/lang/String;)Ljava/lang/Object;", false);
- mv.visitInsn(ARETURN);
- }
- })
- );
- }
- method.setCode(block);
-
- // add static property missing getter dispatcher
- methodName = "$static_propertyMissing";
- if (isStatic)
- addCompilationErrorOnCustomMethodNode(node, methodName, parameters);
-
- method = node.addSyntheticMethod(
- methodName,
- Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,
- ClassHelper.OBJECT_TYPE,
- parameters,
- ClassNode.EMPTY_ARRAY,
- null
+ addSyntheticMethod(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);
+ }
+ })
+ );
+ }
+ }
);
- block = new BlockStatement();
- setPropertyGetterDispatcher(block, new ClassExpression(outerClass), parameters);
- method.setCode(block);
+ addSyntheticMethod(node,
+ "$static_propertyMissing",
+ ACC_PUBLIC | ACC_STATIC,
+ OBJECT_TYPE,
+ params(param(STRING_TYPE, "name")),
+ (methodBody, parameters) -> {
+ setPropertyGetterDispatcher(methodBody, classX(outerClass), parameters);
+ }
+ );
}
- /**
- * Adds a compilation error if a {@link MethodNode} with the given <tt>methodName</tt> and
- * <tt>parameters</tt> exists in the {@link InnerClassNode}.
- */
- private void addCompilationErrorOnCustomMethodNode(InnerClassNode node, String methodName, Parameter[] parameters) {
- MethodNode existingMethodNode = node.getMethod(methodName, parameters);
- // if there is a user-defined methodNode, add compiler error msg and continue
- if (existingMethodNode != null && !isSynthetic(existingMethodNode)) {
- addError("\"" +methodName + "\" implementations are not supported on static inner classes as " +
+ private void addSyntheticMethod(final InnerClassNode node, final String methodName, final int modifiers,
+ final ClassNode returnType, final Parameter[] parameters, final BiConsumer<BlockStatement, Parameter[]> consumer) {
+ MethodNode method = node.getMethod(methodName, parameters);
+ if (method != null) {
+ // GROOVY-8914: pre-compiled classes lose synthetic boolean - TODO fix earlier as per GROOVY-4346 then remove extra check here
+ if (isStatic(node) && !method.isSynthetic() && (method.getModifiers() & ACC_SYNTHETIC) == 0) {
+ // if there is a user-defined methodNode, add compiler error and continue
+ 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.",
- existingMethodNode);
+ method);
+ }
+ return;
}
- }
- // GROOVY-8914: pre-compiled classes lose synthetic boolean - TODO fix earlier as per GROOVY-4346 then remove extra check here
- private boolean isSynthetic(MethodNode existingMethodNode) {
- return existingMethodNode.isSynthetic() || hasSyntheticModifier(existingMethodNode);
- }
-
- private boolean hasSyntheticModifier(MethodNode existingMethodNode) {
- return (existingMethodNode.getModifiers() & Opcodes.ACC_SYNTHETIC) != 0;
+ BlockStatement methodBody = block();
+ consumer.accept(methodBody, parameters);
+ node.addSyntheticMethod(methodName, modifiers, returnType, parameters, ClassNode.EMPTY_ARRAY, methodBody);
}
private void addThisReference(ConstructorNode node) {
@@ -440,5 +364,4 @@ public class InnerClassCompletionVisitor extends InnerClassVisitorHelper impleme
addError("unable to find a unique prefix name for synthetic this reference in inner class constructor", node);
return namePrefix;
}
-
}
diff --git a/src/test/gls/innerClass/InnerClassTest.groovy b/src/test/gls/innerClass/InnerClassTest.groovy
index a9ba77a..e82496d 100644
--- a/src/test/gls/innerClass/InnerClassTest.groovy
+++ b/src/test/gls/innerClass/InnerClassTest.groovy
@@ -1207,6 +1207,30 @@ final class InnerClassTest {
'''
}
+ @Test // GROOVY-8274
+ void testMissingMethodHandling() {
+ assertScript '''
+ class A {
+ class B {
+ def methodMissing(String name, args) {
+ return name
+ }
+ }
+
+ def test(Closure c) {
+ c.resolveStrategy = Closure.DELEGATE_ONLY
+ c.delegate = new B()
+ c.call()
+ }
+ }
+
+ def x = new A().test { ->
+ hello() // missing
+ }
+ assert x == 'hello'
+ '''
+ }
+
@Test // GROOVY-6831
void testNestedPropertyHandling() {
assertScript '''