You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2020/02/25 02:04:14 UTC

[groovy] 02/02: GROOVY-9211: BUG! UNCAUGHT EXCEPTION on OpenJDK14-ea+8 (adjust for better JDK8 lookup)

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

paulk pushed a commit to branch GROOVY_2_5_X
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 78430bae4ece202d764a18547669a6f9bf740a24
Author: Paul King <pa...@asert.com.au>
AuthorDate: Mon Feb 24 10:09:14 2020 +1000

    GROOVY-9211: BUG! UNCAUGHT EXCEPTION on OpenJDK14-ea+8 (adjust for better JDK8 lookup)
---
 .../org/codehaus/groovy/vmplugin/v8/Java8.java     | 72 ++++++++++++++++++++++
 .../groovy/runtime/InterfaceConversionTest.groovy  |  2 -
 2 files changed, 72 insertions(+), 2 deletions(-)

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 f4bd201..3c1bc32 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
@@ -18,12 +18,17 @@
  */
 package org.codehaus.groovy.vmplugin.v8;
 
+import groovy.lang.GroovyRuntimeException;
 import org.codehaus.groovy.ast.AnnotationNode;
 import org.codehaus.groovy.ast.CompileUnit;
 import org.codehaus.groovy.ast.Parameter;
 import org.codehaus.groovy.vmplugin.v7.Java7;
 
 import java.lang.annotation.ElementType;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Type;
 import java.util.ArrayList;
@@ -83,4 +88,71 @@ public class Java8 extends Java7 {
         }
         return params;
     }
+
+    private static class LookupHolder {
+        private static final Method PRIVATE_LOOKUP;
+        private static final Constructor<MethodHandles.Lookup> LOOKUP_Constructor;
+
+        static {
+            Constructor<MethodHandles.Lookup> lookup = null;
+            Method privateLookup = null;
+            try { // java 9+ friendly
+                privateLookup = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
+            } catch (final NoSuchMethodException | RuntimeException e) { // java 8 or fallback if anything else goes wrong
+                try {
+                    lookup = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, Integer.TYPE);
+                    if (!lookup.isAccessible()) {
+                        trySetAccessible(lookup);
+                    }
+                } catch (final NoSuchMethodException ex) {
+                    throw new IllegalStateException("Incompatible JVM", e);
+                }
+            }
+            PRIVATE_LOOKUP = privateLookup;
+            LOOKUP_Constructor = lookup;
+        }
+    }
+
+    private static boolean trySetAccessible(AccessibleObject ao) {
+        try {
+            ao.setAccessible(true);
+            return true;
+        } catch (SecurityException e) {
+            throw e;
+        } catch (Throwable t) {
+            return false;
+        }
+    }
+
+    private static Constructor<MethodHandles.Lookup> getLookupConstructor() {
+        return LookupHolder.LOOKUP_Constructor;
+    }
+
+    private static Method getPrivateLookup() {
+        return LookupHolder.PRIVATE_LOOKUP;
+    }
+
+    public static MethodHandles.Lookup of(final Class<?> declaringClass) {
+        try {
+            final Method privateLookup = getPrivateLookup();
+            if (privateLookup != null) {
+                return (MethodHandles.Lookup) privateLookup.invoke(null, declaringClass, MethodHandles.lookup());
+            }
+            return getLookupConstructor().newInstance(declaringClass, MethodHandles.Lookup.PRIVATE).in(declaringClass);
+        } catch (final IllegalAccessException | InstantiationException e) {
+            throw new IllegalArgumentException(e);
+        } catch (final InvocationTargetException e) {
+            throw new GroovyRuntimeException(e);
+        }
+    }
+
+    @Override
+    public Object getInvokeSpecialHandle(Method method, Object receiver) {
+        final Class<?> receiverType = receiver.getClass();
+        try {
+            return of(receiverType).unreflectSpecial(method, receiverType).bindTo(receiver);
+        } catch (ReflectiveOperationException e) {
+            return super.getInvokeSpecialHandle(method, receiver);
+        }
+    }
 }
diff --git a/src/test/org/codehaus/groovy/runtime/InterfaceConversionTest.groovy b/src/test/org/codehaus/groovy/runtime/InterfaceConversionTest.groovy
index be6a385..30bcc93 100644
--- a/src/test/org/codehaus/groovy/runtime/InterfaceConversionTest.groovy
+++ b/src/test/org/codehaus/groovy/runtime/InterfaceConversionTest.groovy
@@ -45,8 +45,6 @@ class InterfaceConversionTest extends GroovyTestCase {
     void testDefaultInterfaceMethodCallOnProxy() {
         // reversed is a default method within the Comparator interface for 1.8+
         if (!isAtLeastJdk("1.8")) return
-        // broken on JDK14, TODO: FIX
-        if (isAtLeastJdk("14.0")) return
         Comparator c1 = { a, b -> a <=> b }
         assert c1.compare("a", "b") == -1
         def c2 = c1.reversed()