[groovy] branch GROOVY-9779 created (now 8fe50dc)

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

[groovy] branch GROOVY-9779 created (now 8fe50dc)

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

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


      at 8fe50dc  GROOVY-9779: before missing method, try "call" method of property value

This branch includes the following new commits:

     new 8fe50dc  GROOVY-9779: before missing method, try "call" method of property value

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.


Reply | Threaded
Open this post in threaded view
|

[groovy] 01/01: GROOVY-9779: before missing method, try "call" method of property value

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

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

commit 8fe50dc4a0404892fb598acb446cb36c79a45805
Author: Eric Milles <[hidden email]>
AuthorDate: Mon Oct 12 12:01:12 2020 -0500

    GROOVY-9779: before missing method, try "call" method of property value
   
    - this change expands upon the check for Script binding variable "call"
---
 src/main/java/groovy/lang/MetaClassImpl.java | 55 ++++++++++++++++------------
 src/test/groovy/bugs/Groovy9779.groovy       | 43 ++++++++++++++++++++++
 2 files changed, 74 insertions(+), 24 deletions(-)

diff --git a/src/main/java/groovy/lang/MetaClassImpl.java b/src/main/java/groovy/lang/MetaClassImpl.java
index 4723d59..1d678a0 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -132,10 +132,11 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
     protected static final String PROPERTY_MISSING = "propertyMissing";
     protected static final String INVOKE_METHOD_METHOD = "invokeMethod";
 
-    private static final String CLOSURE_CALL_METHOD = "call";
-    private static final String CLOSURE_DO_CALL_METHOD = "doCall";
+    private static final String CALL_METHOD = "call";
+    private static final String DO_CALL_METHOD = "doCall";
     private static final String GET_PROPERTY_METHOD = "getProperty";
     private static final String SET_PROPERTY_METHOD = "setProperty";
+
     private static final Class[] METHOD_MISSING_ARGS = new Class[]{String.class, Object.class};
     private static final Class[] GETTER_MISSING_ARGS = new Class[]{String.class};
     private static final Class[] SETTER_MISSING_ARGS = METHOD_MISSING_ARGS;
@@ -1137,7 +1138,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
             final Closure closure = (Closure) object;
             final Object owner = closure.getOwner();
 
-            if (CLOSURE_CALL_METHOD.equals(methodName) || CLOSURE_DO_CALL_METHOD.equals(methodName)) {
+            if (CALL_METHOD.equals(methodName) || DO_CALL_METHOD.equals(methodName)) {
                 final Class objectClass = object.getClass();
                 if (objectClass == MethodClosure.class) {
                     return this.invokeMethodClosure(object, arguments);
@@ -1274,8 +1275,8 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
 
     private MetaMethod getMetaMethod(Class sender, Object object, String methodName, boolean isCallToSuper, Object... arguments) {
         MetaMethod method = null;
-        if (CLOSURE_CALL_METHOD.equals(methodName) && object instanceof GeneratedClosure) {
-            method = getMethodWithCaching(sender, "doCall", arguments, isCallToSuper);
+        if (CALL_METHOD.equals(methodName) && object instanceof GeneratedClosure) {
+            method = getMethodWithCaching(sender, DO_CALL_METHOD, arguments, isCallToSuper);
         }
         if (method == null) {
             method = getMethodWithCaching(sender, methodName, arguments, isCallToSuper);
@@ -1312,30 +1313,36 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
         };
     }
 
-    private Object invokePropertyOrMissing(Object object, String methodName, Object[] originalArguments, boolean fromInsideClass, boolean isCallToSuper) {
-        // if no method was found, try to find a closure defined as a field of the class and run it
+    /**
+     * Tries to find a callable property and make the call.
+     */
+    private Object invokePropertyOrMissing(final Object object, final String methodName, final Object[] originalArguments, final boolean fromInsideClass, final boolean isCallToSuper) {
+        MetaProperty metaProperty = this.getMetaProperty(methodName, false);
+
         Object value = null;
-        final MetaProperty metaProperty = this.getMetaProperty(methodName, false);
         if (metaProperty != null) {
             value = metaProperty.getProperty(object);
-        } else {
-            if (object instanceof Map)
-                value = ((Map) object).get(methodName);
+        } else if (object instanceof Map) {
+            value = ((Map<?, ?>) object).get(methodName);
+        } else if (object instanceof Script) {
+            value = ((Script) object).getBinding().getVariables().get(methodName);
         }
 
-        if (value instanceof Closure) {  // This test ensures that value != this If you ever change this ensure that value != this
-            Closure closure = (Closure) value;
-            MetaClass delegateMetaClass = closure.getMetaClass();
-            return delegateMetaClass.invokeMethod(closure.getClass(), closure, CLOSURE_DO_CALL_METHOD, originalArguments, false, fromInsideClass);
+        if (value instanceof Closure) {
+            Closure<?> closure = (Closure<?>) value;
+            MetaClass metaClass = closure.getMetaClass();
+            return metaClass.invokeMethod(closure.getClass(), closure, DO_CALL_METHOD, originalArguments, false, fromInsideClass);
         }
 
-        if (object instanceof Script) {
-            Object bindingVar = ((Script) object).getBinding().getVariables().get(methodName);
-            if (bindingVar != null) {
-                MetaClass bindingVarMC = ((MetaClassRegistryImpl) registry).getMetaClass(bindingVar);
-                return bindingVarMC.invokeMethod(bindingVar, CLOSURE_CALL_METHOD, originalArguments);
+        if (value != null && !(value instanceof Map)) {
+            try {
+                MetaClass metaClass = ((MetaClassRegistryImpl) registry).getMetaClass(value);
+                return metaClass.invokeMethod(value, CALL_METHOD, originalArguments); // delegate to call method of property value
+            } catch (MissingMethodException mme) {
+                // ignore
             }
         }
+
         return invokeMissingMethod(object, methodName, originalArguments, null, isCallToSuper);
     }
 
@@ -1557,7 +1564,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
 
         if (prop != null) {
             MetaClass propMC = registry.getMetaClass(prop.getClass());
-            return propMC.invokeMethod(prop, CLOSURE_CALL_METHOD, arguments);
+            return propMC.invokeMethod(prop, CALL_METHOD, arguments);
         }
 
         return invokeStaticMissingMethod(sender, methodName, arguments);
@@ -1566,7 +1573,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
     private static Object invokeStaticClosureProperty(Object[] originalArguments, Object prop) {
         Closure closure = (Closure) prop;
         MetaClass delegateMetaClass = closure.getMetaClass();
-        return delegateMetaClass.invokeMethod(closure.getClass(), closure, CLOSURE_DO_CALL_METHOD, originalArguments, false, false);
+        return delegateMetaClass.invokeMethod(closure.getClass(), closure, DO_CALL_METHOD, originalArguments, false, false);
     }
 
     private Object invokeStaticMissingMethod(Class sender, String methodName, Object[] arguments) {
@@ -3574,13 +3581,13 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
         if (!GroovyCategorySupport.hasCategoryInCurrentThread() && !(this instanceof AdaptingMetaClass)) {
             Class[] params = MetaClassHelper.convertToTypeArray(args);
             CallSite tempSite = site;
-            if (site.getName().equals("call") && GeneratedClosure.class.isAssignableFrom(theClass)) {
+            if (site.getName().equals(CALL_METHOD) && GeneratedClosure.class.isAssignableFrom(theClass)) {
                 // here, we want to point to a method named "doCall" instead of "call"
                 // but we don't want to replace the original call site name, otherwise
                 // we loose the fact that the original method name was "call" so instead
                 // we will point to a metamethod called "doCall"
                 // see GROOVY-5806 for details
-                tempSite = new AbstractCallSite(site.getArray(), site.getIndex(), "doCall");
+                tempSite = new AbstractCallSite(site.getArray(), site.getIndex(), DO_CALL_METHOD);
             }
             MetaMethod metaMethod = getMethodWithCachingInternal(theClass, tempSite, params);
             if (metaMethod != null)
diff --git a/src/test/groovy/bugs/Groovy9779.groovy b/src/test/groovy/bugs/Groovy9779.groovy
new file mode 100644
index 0000000..c2b0623
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy9779.groovy
@@ -0,0 +1,43 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.bugs
+
+import groovy.transform.CompileStatic
+import org.junit.Test
+
+import static groovy.test.GroovyAssert.assertScript
+
+@CompileStatic
+final class Groovy9779 {
+    @Test
+    void testCallOperatorOnDynamicProperties() {
+        assertScript '''
+            class C {
+                def call() { return 42 }
+            }
+            class D {
+                static final x = new C()
+                       final y = new C()
+            }
+            assert D.x() == 42
+            assert new D().x() == 42
+            assert new D().y() == 42
+        '''
+    }
+}