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/05/10 17:35:27 UTC

[groovy] branch GROOVY-3015 created (now 5ff3cae)

This is an automated email from the ASF dual-hosted git repository.

emilles pushed a change to branch GROOVY-3015
in repository https://gitbox.apache.org/repos/asf/groovy.git.


      at 5ff3cae  GROOVY-3015: add GroovyInterceptable check to closure resolve strategy

This branch includes the following new commits:

     new 5ff3cae  GROOVY-3015: add GroovyInterceptable check to closure resolve strategy

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[groovy] 01/01: GROOVY-3015: add GroovyInterceptable check to closure resolve strategy

Posted by em...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch GROOVY-3015
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 5ff3caeb5a3fe768c45424f374d0eb6f71d4e875
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Mon May 10 12:34:34 2021 -0500

    GROOVY-3015: add GroovyInterceptable check to closure resolve strategy
---
 .../groovy/runtime/metaclass/ClosureMetaClass.java |  11 +++
 src/test/groovy/GroovyInterceptableTest.groovy     | 106 ++++++++++++++++-----
 2 files changed, 92 insertions(+), 25 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/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()
+    }
 }