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 9bce2df  GROOVY-3015, GROOVY-4610: add GroovyInterceptable check to 
closure resolve strategy
9bce2df is described below

commit 9bce2dff2068f6ee31c0681b30628987aab09a92
Author: Eric Milles <eric.mil...@thomsonreuters.com>
AuthorDate: Tue May 18 08:41:04 2021 -0500

    GROOVY-3015, GROOVY-4610: add GroovyInterceptable check to closure resolve 
strategy
---
 .../groovy/runtime/metaclass/ClosureMetaClass.java |  11 +++
 .../runtime/metaclass/TransformMetaMethod.java     |  16 +++-
 src/test/groovy/GroovyInterceptableTest.groovy     | 106 ++++++++++++++++-----
 3 files changed, 105 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 fa7fee1..b8ed7f4 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;
@@ -207,6 +208,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 203591c..64a1eb6 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();
@@ -55,7 +55,17 @@ public class TransformMetaMethod extends MetaMethod {
     }
 
     @Override
-    public Object invoke(Object object, Object[] arguments) {
+    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()
+    }
 }

Reply via email to