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 2018/05/03 23:49:41 UTC

groovy git commit: GROOVY-3867: Allow methodMissing/propertyMissing to be defined through category(closes #693)

Repository: groovy
Updated Branches:
  refs/heads/master c2404180a -> ad664b181


GROOVY-3867: Allow methodMissing/propertyMissing to be defined through category(closes #693)


Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/ad664b18
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/ad664b18
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/ad664b18

Branch: refs/heads/master
Commit: ad664b181249c4b8e8821003d7e750632943ce36
Parents: c240418
Author: Ruben Laguna <ru...@gmail.com>
Authored: Fri May 4 07:46:14 2018 +0800
Committer: sunlan <su...@apache.org>
Committed: Fri May 4 07:48:54 2018 +0800

----------------------------------------------------------------------
 src/main/groovy/groovy/lang/MetaClassImpl.java | 35 ++++++++++++++-
 src/test/groovy/CategoryTest.groovy            | 50 ++++++++++++++++++++-
 2 files changed, 83 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/ad664b18/src/main/groovy/groovy/lang/MetaClassImpl.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/MetaClassImpl.java b/src/main/groovy/groovy/lang/MetaClassImpl.java
index c798f76..66abb2d 100644
--- a/src/main/groovy/groovy/lang/MetaClassImpl.java
+++ b/src/main/groovy/groovy/lang/MetaClassImpl.java
@@ -945,6 +945,14 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
                 onInvokeMethodFoundInHierarchy(method);
                 return method.invoke(instance, invokeMethodArgs);
             }
+
+            // last resort look in the category
+            if (method == null && GroovyCategorySupport.hasCategoryInCurrentThread()) {
+                method = getCategoryMethodMissing(instanceKlazz);
+                if (method != null) {
+                    return method.invoke(instance, new Object[]{methodName, arguments});
+                }
+            }
         }
 
         if (methodMissing != null) {
@@ -1866,11 +1874,18 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
             }
         }
 
+        // check for propertyMissing provided through a category
+        Object[] arguments = EMPTY_ARGUMENTS;
+        if (method == null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInCurrentThread()) {
+            method = getCategoryMethodGetter(sender, "propertyMissing", true);
+            if (method != null) arguments = new Object[]{name};
+        }
+
+
         //----------------------------------------------------------------------
         // generic get method
         //----------------------------------------------------------------------
         // check for a generic get method provided through a category
-        Object[] arguments = EMPTY_ARGUMENTS;
         if (method == null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInCurrentThread()) {
             method = getCategoryMethodGetter(sender, "get", true);
             if (method != null) arguments = new Object[]{name};
@@ -2097,6 +2112,24 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
         return new Tuple2<MetaMethod, MetaProperty>(method, mp);
     }
 
+
+    private static MetaMethod getCategoryMethodMissing(Class sender) {
+        List possibleGenericMethods = GroovyCategorySupport.getCategoryMethods("methodMissing");
+        if (possibleGenericMethods != null) {
+            for (Iterator iter = possibleGenericMethods.iterator(); iter.hasNext();) {
+                MetaMethod mmethod = (MetaMethod) iter.next();
+                if (!mmethod.getDeclaringClass().getTheClass().isAssignableFrom(sender))
+                    continue;
+
+                CachedClass[] paramTypes = mmethod.getParameterTypes();
+                if (paramTypes.length == 2 && paramTypes[0].getTheClass() == String.class) {
+                    return mmethod;
+                }
+            }
+        }
+        return null;
+    }
+
     private static MetaMethod getCategoryMethodGetter(Class sender, String name, boolean useLongVersion) {
         List possibleGenericMethods = GroovyCategorySupport.getCategoryMethods(name);
         if (possibleGenericMethods != null) {

http://git-wip-us.apache.org/repos/asf/groovy/blob/ad664b18/src/test/groovy/CategoryTest.groovy
----------------------------------------------------------------------
diff --git a/src/test/groovy/CategoryTest.groovy b/src/test/groovy/CategoryTest.groovy
index b6cb8e8..efec30c 100644
--- a/src/test/groovy/CategoryTest.groovy
+++ b/src/test/groovy/CategoryTest.groovy
@@ -228,12 +228,60 @@ class CategoryTest extends GroovyTestCase {
             }
         '''
     }
+    // GROOVY-3867
+    void testPropertyMissing() {
+        def x = new X()
+
+        shouldFail(MissingPropertyException) {
+            assert x.baz != "works" // accessing x.baz should throw MPE
+        }
+
+        use(XCat4) {
+            assert x.baz == "works"
+        }
+
+        shouldFail(MissingPropertyException) {
+            assert x.baz != "works" // accessing x.baz should throw MPE
+        }
+    }
+
+    // GROOVY-3867
+    void testMethodMissing() {
+        def x = new X()
+        assert foo(x) == 1
+        use (XCat3) {
+            assert foo(x) == 1 // regular foo() is not affected by methodMissing in category
+            assert x.baz() == 4 // XCat3.methodMissing is called
+        }
+        assert foo(x) == 1
+        def t = Thread.start {use (XCat3){assert x.baz()==4}}
+        t.join()
+        assert foo(x) == 1
+        shouldFail(MissingMethodException) {
+            x.baz()
+        }
+    }
+
+    // GROOVY-3867
+    void testMethodMissingNoStatic() {
+        def x = new X()
+        use (XCat3) {
+            assert x.baz() == 4 // XCat3.methodMissing is called for instance
+            shouldFail(MissingMethodException) {
+                assert X.baz() != 4 // XCat3.methodMissing should not be called for static method of X
+            }
+        }
+    }
+
+
 
 }
 
 class X{ def bar(){1}}
 class XCat{ static bar(X x){2}}
 class XCat2{ static bar(X x){3}}
+class XCat3{ static methodMissing(X x, String name, args) {4}}
+class XCat4{ static propertyMissing(X x, String name) {"works"}}
 
 class StringCategory {
     static String lower(String string) {
@@ -261,4 +309,4 @@ class CategoryTestHelperPropertyReplacer {
     private static aVal = "anotherValue"
     static getaProperty(CategoryTestHelper self) { return aVal }
     static void setaProperty(CategoryTestHelper self, newValue) { aVal = newValue }
-}
+}
\ No newline at end of file