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 18:22:41 UTC

[groovy] branch GROOVY-3015 updated (5ff3cae -> 11447d5)

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.


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

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (5ff3cae)
            \
             N -- N -- N   refs/heads/GROOVY-3015 (11447d5)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

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.


Summary of changes:
 .../groovy/runtime/metaclass/TransformMetaMethod.java    | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

[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 11447d57a56a0a837ed590c342c331d6f829414f
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 +++
 .../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()
+    }
 }