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 2021/06/16 02:18:15 UTC

[groovy] 01/01: Fix calling method of super type

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

sunlan pushed a commit to branch danielsun/tweak-build
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 9049616ef2277b62abde8e44bb4a0b2f88550b1e
Author: Daniel Sun <su...@apache.org>
AuthorDate: Wed Jun 16 10:17:55 2021 +0800

    Fix calling method of super type
---
 .../org/apache/groovy/lang/GroovyObjectHelper.java | 63 ++++++++++++++++++++++
 .../org/codehaus/groovy/classgen/Verifier.java     |  1 +
 .../groovy/runtime/ScriptBytecodeAdapter.java      | 39 ++++++++++++++
 .../org/codehaus/groovy/vmplugin/v16/Java16.java   |  6 +--
 .../org/codehaus/groovy/vmplugin/v8/Java8.java     | 23 +++++++-
 5 files changed, 127 insertions(+), 5 deletions(-)

diff --git a/src/main/java/org/apache/groovy/lang/GroovyObjectHelper.java b/src/main/java/org/apache/groovy/lang/GroovyObjectHelper.java
new file mode 100644
index 0000000..62df66d
--- /dev/null
+++ b/src/main/java/org/apache/groovy/lang/GroovyObjectHelper.java
@@ -0,0 +1,63 @@
+/*
+ *  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 org.apache.groovy.lang;
+
+import groovy.lang.GroovyObject;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Modifier;
+import java.util.Optional;
+
+/**
+ * Helper for {@link groovy.lang.GroovyObject}
+ *
+ * @since 4.0.0
+ */
+public class GroovyObjectHelper {
+    /**
+     * Get the {@link Lookup} instance of the {@link GroovyObject} instance
+     *
+     * @param groovyObject the {@link GroovyObject} instance
+     * @return the {@link Lookup} instance
+     * @since 4.0.0
+     */
+    public static Optional<Lookup> lookup(GroovyObject groovyObject) {
+        MethodHandles.Lookup lookup;
+        try {
+            if (groovyObject.getClass().isMemberClass() && !Modifier.isStatic(groovyObject.getClass().getModifiers())) {
+                lookup = (MethodHandles.Lookup) MethodHandles.lookup()
+                        .findVirtual(groovyObject.getClass(), "$getLookup", MethodType.methodType(MethodHandles.Lookup.class, new Class[0]))
+                        .bindTo(groovyObject)
+                        .invokeExact();
+            } else {
+                lookup = (MethodHandles.Lookup) MethodHandles.lookup()
+                        .findStatic(groovyObject.getClass(), "$getLookup", MethodType.methodType(MethodHandles.Lookup.class, new Class[0]))
+                        .invokeExact();
+            }
+        } catch (Throwable e) {
+            lookup = null;
+        }
+
+        return Optional.ofNullable(lookup);
+    }
+
+    private GroovyObjectHelper() {}
+}
diff --git a/src/main/java/org/codehaus/groovy/classgen/Verifier.java b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
index 8a98652..f454ee7 100644
--- a/src/main/java/org/codehaus/groovy/classgen/Verifier.java
+++ b/src/main/java/org/codehaus/groovy/classgen/Verifier.java
@@ -375,6 +375,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
                 new BytecodeSequence(new BytecodeInstruction() {
                     @Override
                     public void visit(final MethodVisitor mv) {
+                        // TODO use privateLookup instead
                         mv.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodHandles", "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;", false);
                         mv.visitInsn(ARETURN);
                     }
diff --git a/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java b/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
index 9c19475..742c7ba 100644
--- a/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
+++ b/src/main/java/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
@@ -31,6 +31,8 @@ import groovy.lang.MissingPropertyException;
 import groovy.lang.NumberRange;
 import groovy.lang.ObjectRange;
 import groovy.lang.Tuple;
+import org.apache.groovy.lang.GroovyObjectHelper;
+import org.codehaus.groovy.reflection.ReflectionUtils;
 import org.codehaus.groovy.runtime.metaclass.MissingMethodExceptionNoStack;
 import org.codehaus.groovy.runtime.metaclass.MissingMethodExecutionFailed;
 import org.codehaus.groovy.runtime.metaclass.MissingPropertyExceptionNoStack;
@@ -39,10 +41,16 @@ import org.codehaus.groovy.runtime.wrappers.GroovyObjectWrapper;
 import org.codehaus.groovy.runtime.wrappers.PojoWrapper;
 import org.codehaus.groovy.runtime.wrappers.Wrapper;
 
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -142,12 +150,43 @@ public class ScriptBytecodeAdapter {
         Object result = null;
         try {
             result = metaClass.invokeMethod(senderClass, receiver, messageName, messageArguments, true, true);
+        } catch (MissingMethodException ex) {
+            try {
+                Optional<MethodHandles.Lookup> lookupOptional = GroovyObjectHelper.lookup(receiver);
+                if (!lookupOptional.isPresent()) {
+                    throw unwrap(ex);
+                }
+                Lookup lookup = lookupOptional.get();
+                Method superMethod = findSuperMethod(senderClass, messageName, messageArguments);
+                if (null == superMethod) {
+                    throw unwrap(ex);
+                }
+                MethodHandle superMethodHandle = lookup.unreflectSpecial(superMethod, receiver.getClass());
+                return superMethodHandle.bindTo(receiver).invokeWithArguments(messageArguments);
+            } catch (NoSuchMethodException | IllegalAccessException e) {
+                throw e;
+            }
         } catch (GroovyRuntimeException gre) {
             throw unwrap(gre);
         }
         return result;
     }
 
+    private static Method findSuperMethod(Class senderClass, String messageName, Object[] messageArguments) {
+        Class[] parameterTypes = MetaClassHelper.castArgumentsToClassArray(messageArguments);
+        for (Class<?> c = senderClass; null != c; c = c.getSuperclass()) {
+            List<Method> declaredMethodList = ReflectionUtils.getDeclaredMethods(c, messageName, parameterTypes);
+            if (!declaredMethodList.isEmpty()) {
+                Method superMethod = declaredMethodList.get(0);
+                if (Modifier.isAbstract(superMethod.getModifiers())) {
+                    continue;
+                }
+                return superMethod;
+            }
+        }
+        return null;
+    }
+
     public static Object invokeMethodOnSuperNSafe(Class senderClass, GroovyObject receiver, String messageName, Object[] messageArguments) throws Throwable {
         return invokeMethodOnSuperN(senderClass, receiver, messageName, messageArguments);
     }
diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v16/Java16.java b/src/main/java/org/codehaus/groovy/vmplugin/v16/Java16.java
index c0a9aa1..5eef73d 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/v16/Java16.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v16/Java16.java
@@ -78,14 +78,14 @@ public class Java16 extends Java10 {
 
     @Override
     public Object getInvokeSpecialHandle(Method method, Object receiver) {
-        final Class<?> receiverType = receiver.getClass();
         try {
+            final Class<?> receiverType = receiver.getClass();
             if (method.isDefault() && Proxy.isProxyClass(receiverType)) {
                 return new ProxyDefaultMethodHandle((Proxy) receiver, method);
             }
 
-            MethodHandles.Lookup lookup = newLookup(receiverType);
-            if (MethodHandles.Lookup.PRIVATE == lookup.lookupModes()) {
+            MethodHandles.Lookup lookup = getLookup(receiver);
+            if (0 != (MethodHandles.Lookup.PRIVATE & lookup.lookupModes())) {
                 return lookup.unreflectSpecial(method, receiverType).bindTo(receiver);
             }
             return lookup.unreflect(method).bindTo(receiver);
diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java b/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
index 3f54df0..71888ea 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
@@ -18,9 +18,11 @@
  */
 package org.codehaus.groovy.vmplugin.v8;
 
+import groovy.lang.GroovyObject;
 import groovy.lang.GroovyRuntimeException;
 import groovy.lang.MetaClass;
 import groovy.lang.MetaMethod;
+import org.apache.groovy.lang.GroovyObjectHelper;
 import org.codehaus.groovy.GroovyBugError;
 import org.codehaus.groovy.ast.AnnotatedNode;
 import org.codehaus.groovy.ast.AnnotationNode;
@@ -68,6 +70,7 @@ import java.security.AccessController;
 import java.security.Permission;
 import java.security.PrivilegedAction;
 import java.util.List;
+import java.util.Optional;
 
 /**
  * Java 8 based functions.
@@ -623,11 +626,27 @@ public class Java8 implements VMPlugin {
         IndyInterface.invalidateSwitchPoints();
     }
 
+    protected MethodHandles.Lookup getLookup(Object receiver) {
+        MethodHandles.Lookup lookup = null;
+        if (receiver instanceof GroovyObject) {
+            Optional<MethodHandles.Lookup> lookupOptional = GroovyObjectHelper.lookup((GroovyObject) receiver);
+            if (lookupOptional.isPresent()) {
+                lookup = lookupOptional.get();
+            }
+        }
+        final Class<?> receiverType = receiver.getClass();
+        if (null == lookup) {
+            lookup = newLookup(receiverType);
+        }
+        return lookup;
+    }
+
     @Override
     public Object getInvokeSpecialHandle(Method method, Object receiver) {
-        final Class<?> receiverType = receiver.getClass();
         try {
-            return newLookup(receiverType).unreflectSpecial(method, receiverType).bindTo(receiver);
+            MethodHandles.Lookup lookup = getLookup(receiver);
+            Class<?> receiverType = receiver.getClass();
+            return lookup.unreflectSpecial(method, receiverType).bindTo(receiver);
         } catch (ReflectiveOperationException e) {
             return getInvokeSpecialHandleFallback(method, receiver);
         }