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 2021/08/17 18:18:22 UTC
[groovy] 01/04: GROOVY-3015,
GROOVY-4610: add GroovyInterceptable check to closure resolve
strategy
This is an automated email from the ASF dual-hosted git repository.
emilles pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 57482cd27a8d496ce381fb8932899ed7ef99c214
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue May 18 08:41:04 2021 -0500
GROOVY-3015, GROOVY-4610: add GroovyInterceptable check to closure
resolve strategy
Conflicts:
src/main/java/org/codehaus/groovy/runtime/metaclass/TransformMetaMethod.java
---
.../groovy/runtime/metaclass/ClosureMetaClass.java | 11 +++
.../runtime/metaclass/TransformMetaMethod.java | 17 +++-
src/test/groovy/GroovyInterceptableTest.groovy | 106 ++++++++++++++++-----
3 files changed, 106 insertions(+), 28 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/runtime/metaclass/ClosureMetaClass.java b/src/main/java/org/codehaus/groovy/runtime/metaclass/ClosureMetaClass.java
index fafb786..e9e3b57 100644
--- a/src/main/java/org/codehaus/groovy/runtime/metaclass/ClosureMetaClass.java
+++ b/src/main/java/org/codehaus/groovy/runtime/metaclass/ClosureMetaClass.java
@@ -20,6 +20,7 @@ package org.codehaus.groovy.runtime.metaclass;
import groovy.lang.Closure;
import groovy.lang.ExpandoMetaClass;
+import groovy.lang.GroovyInterceptable;
import groovy.lang.GroovyObject;
import groovy.lang.GroovyRuntimeException;
import groovy.lang.MetaBeanProperty;
@@ -204,6 +205,16 @@ public final class ClosureMetaClass extends MetaClassImpl {
if (method != null) return method;
}
return null;
+ } else if (delegate instanceof GroovyInterceptable) {
+ MetaClass delegateMetaClass = lookupObjectMetaClass(delegate);
+ // GROOVY-3015: must route calls through GroovyObject#invokeMethod(String,Object)
+ MetaMethod interceptMethod = delegateMetaClass.pickMethod("invokeMethod", new Class[]{String.class, Object.class});
+ return new TransformMetaMethod(interceptMethod) {
+ @Override
+ public Object invoke(final Object object, final Object[] arguments) {
+ return super.invoke(object, new Object[]{methodName, arguments});
+ }
+ };
} else {
MetaClass delegateMetaClass = lookupObjectMetaClass(delegate);
MetaMethod method = delegateMetaClass.pickMethod(methodName, argClasses);
diff --git a/src/main/java/org/codehaus/groovy/runtime/metaclass/TransformMetaMethod.java b/src/main/java/org/codehaus/groovy/runtime/metaclass/TransformMetaMethod.java
index 408c53e..be0b288 100644
--- a/src/main/java/org/codehaus/groovy/runtime/metaclass/TransformMetaMethod.java
+++ b/src/main/java/org/codehaus/groovy/runtime/metaclass/TransformMetaMethod.java
@@ -25,10 +25,10 @@ import org.codehaus.groovy.reflection.CachedClass;
* A MetaMethod implementation useful for implementing coercion based invocations
*/
public class TransformMetaMethod extends MetaMethod {
-
+
private final MetaMethod metaMethod;
- public TransformMetaMethod(MetaMethod metaMethod) {
+ public TransformMetaMethod(final MetaMethod metaMethod) {
this.metaMethod = metaMethod;
setParametersTypes(metaMethod.getParameterTypes());
nativeParamTypes = metaMethod.getNativeParameterTypes();
@@ -50,7 +50,18 @@ public class TransformMetaMethod extends MetaMethod {
return metaMethod.getDeclaringClass();
}
- public Object invoke(Object object, Object[] arguments) {
+ @Override
+ public Object invoke(final Object object, final Object[] arguments) {
return metaMethod.invoke(object, arguments);
}
+
+ @Override
+ public Object doMethodInvoke(final Object object, final Object[] arguments) {
+ // no coerceArgumentsToClasses
+ try {
+ return invoke(object, arguments);
+ } catch (final Exception ex) {
+ throw processDoMethodInvokeException(ex, object, arguments);
+ }
+ }
}
diff --git a/src/test/groovy/GroovyInterceptableTest.groovy b/src/test/groovy/GroovyInterceptableTest.groovy
index a667bc0..0053a1d 100644
--- a/src/test/groovy/GroovyInterceptableTest.groovy
+++ b/src/test/groovy/GroovyInterceptableTest.groovy
@@ -21,44 +21,101 @@ package groovy
import groovy.test.GroovyTestCase
import org.codehaus.groovy.runtime.ReflectionMethodInvoker
-class GroovyInterceptableTest extends GroovyTestCase {
+final class GroovyInterceptableTest extends GroovyTestCase {
- void testMethodInterception() {
+ void testMethodIntercept1() {
def g = new GI()
assert g.someInt() == 2806
assert g.someUnexistingMethod() == 1
assert g.toString() == "invokeMethodToString"
}
- void testProperties() {
+ void testMethodIntercept2() {
def g = new GI()
assert g.foo == 89
g.foo = 90
assert g.foo == 90
- // should this be 1 or 90?
+ // Should this be 1 or 90?
assert g.getFoo() == 1
}
-
- void testCallMissingMethod() {
+
+ // GROOVY-3015
+ void testMethodIntercept3() {
+ String shared = '''\
+ import org.codehaus.groovy.runtime.InvokerHelper
+ import org.codehaus.groovy.runtime.StringBufferWriter
+ import static groovy.test.GroovyTestCase.assertEquals
+
+ class Traceable implements GroovyInterceptable {
+ private static int indent = 1
+ Writer writer = new PrintWriter(System.out)
+ Object invokeMethod(String name, Object args) {
+ writer.write('\\n' + (' ' * indent) + 'Enter ' + name)
+ indent += 1
+ def result = InvokerHelper.getMetaClass(this).invokeMethod(this, name, args)
+ indent -= 1
+ writer.write('\\n' + (' ' * indent) + 'Leave ' + name)
+ return result
+ }
+ }
+
+ class Whatever extends Traceable {
+ int inner() { return 1 }
+ int outer() { return inner() }
+ int shouldTraceOuterAndInnerMethod() { return outer() }
+ def shouldTraceOuterAndInnerClosure = { -> return outer() }
+ }
+
+ def log = new StringBuffer()
+ def obj = new Whatever(writer: new StringBufferWriter(log))
+ '''
+
+ assertScript shared + '''
+ obj.shouldTraceOuterAndInnerMethod()
+
+ assertEquals """
+ | Enter shouldTraceOuterAndInnerMethod
+ | Enter outer
+ | Enter inner
+ | Leave inner
+ | Leave outer
+ | Leave shouldTraceOuterAndInnerMethod""".stripMargin(), log.toString()
+ '''
+
+ assertScript shared + '''
+ obj.shouldTraceOuterAndInnerClosure()
+
+ assertEquals """
+ | Enter shouldTraceOuterAndInnerClosure
+ | Enter outer
+ | Enter inner
+ | Leave inner
+ | Leave outer
+ | Leave shouldTraceOuterAndInnerClosure""".stripMargin(), log.toString()
+ '''
+ }
+
+ void testMissingMethod1() {
def obj = new GI2()
shouldFail { obj.notAMethod() }
- assert 'missing' == obj.result
+ assert 'missing' == obj.result
}
-
- void testCallMissingMethodFromInstance() {
+
+ void testMissingMethod2() {
def obj = new GI2()
shouldFail { obj.method() }
assert 'missing' == obj.result
- }
+ }
}
-class GI implements GroovyInterceptable {
+//------------------------------------------------------------------------------
+class GI implements GroovyInterceptable {
def foo = 89
-
int someInt() { 2806 }
+ @Override
String toString() { "originalToString" }
-
+ @Override
Object invokeMethod(String name, Object args) {
if ("toString" == name)
return "invokeMethodToString"
@@ -69,17 +126,16 @@ class GI implements GroovyInterceptable {
}
}
-
class GI2 implements GroovyInterceptable {
- def result = ""
- def invokeMethod(String name, args) {
- def metaMethod = Foo.metaClass.getMetaMethod(name, args)
- if (metaMethod != null) return metaMethod.invoke(this, args)
- result += "missing"
- throw new MissingMethodException(name, Foo.class, args)
- }
-
- def method() {
- notAMethod()
- }
+ def result = ""
+ @Override
+ def invokeMethod(String name, args) {
+ def metaMethod = Foo.metaClass.getMetaMethod(name, args)
+ if (metaMethod != null) return metaMethod.invoke(this, args)
+ result += "missing"
+ throw new MissingMethodException(name, Foo.class, args)
+ }
+ def method() {
+ notAMethod()
+ }
}