8022066: Evaluation of method reference to signature polymorphic method crashes VM
authortwisti
Mon, 12 Aug 2013 13:47:21 -0700
changeset 20841 2b188911d712
parent 20840 9ef02837256d
child 20842 276a62b19d45
8022066: Evaluation of method reference to signature polymorphic method crashes VM Reviewed-by: jrose
jdk/src/share/classes/java/lang/invoke/MethodHandles.java
jdk/test/java/lang/invoke/MethodHandleConstants.java
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandles.java	Mon Aug 12 19:57:21 2013 +0400
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandles.java	Mon Aug 12 13:47:21 2013 -0700
@@ -1289,6 +1289,10 @@
                         : resolveOrFail(refKind, defc, name, (Class<?>) type);
                 return getDirectField(refKind, defc, field);
             } else if (MethodHandleNatives.refKindIsMethod(refKind)) {
+                if (defc == MethodHandle.class && refKind == REF_invokeVirtual) {
+                    MethodHandle mh = findVirtualForMH(name, (MethodType) type);
+                    if (mh != null)  return mh;
+                }
                 MemberName method = (resolved != null) ? resolved
                         : resolveOrFail(refKind, defc, name, (MethodType) type);
                 return getDirectMethod(refKind, defc, method, lookupClass);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/MethodHandleConstants.java	Mon Aug 12 13:47:21 2013 -0700
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 8022066
+ * @summary smoke test for method handle constants
+ * @build indify.Indify
+ * @compile MethodHandleConstants.java
+ * @run main/othervm
+ *      indify.Indify
+ *      --verify-specifier-count=0
+ *      --expand-properties --classpath ${test.classes}
+ *      --java test.java.lang.invoke.MethodHandleConstants --check-output
+ * @run main/othervm
+ *      indify.Indify
+ *      --expand-properties --classpath ${test.classes}
+ *      --java test.java.lang.invoke.MethodHandleConstants --security-manager
+ */
+
+package test.java.lang.invoke;
+
+import java.util.*;
+import java.io.*;
+import java.lang.invoke.*;
+import java.security.*;
+
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+
+public class MethodHandleConstants {
+    public static void main(String... av) throws Throwable {
+        if (av.length > 0 && av[0].equals("--check-output"))  openBuf();
+        if (av.length > 0 && av[0].equals("--security-manager"))  setSM();
+        System.out.println("Obtaining method handle constants:");
+        testCase(MH_String_replace_C2(), String.class, "replace", String.class, String.class, char.class, char.class);
+        testCase(MH_MethodHandle_invokeExact_SC2(), MethodHandle.class, "invokeExact", String.class, MethodHandle.class, String.class, char.class, char.class);
+        testCase(MH_MethodHandle_invoke_SC2(), MethodHandle.class, "invoke", String.class, MethodHandle.class, String.class, char.class, char.class);
+        testCase(MH_Class_forName_S(), Class.class, "forName", Class.class, String.class);
+        testCase(MH_Class_forName_SbCL(), Class.class, "forName", Class.class, String.class, boolean.class, ClassLoader.class);
+        System.out.println("Done.");
+        closeBuf();
+    }
+
+    private static void testCase(MethodHandle mh, Class<?> defc, String name, Class<?> rtype, Class<?>... ptypes) throws Throwable {
+        System.out.println(mh);
+        // we include defc, because we assume it is a non-static MH:
+        MethodType mt = methodType(rtype, ptypes);
+        assertEquals(mh.type(), mt);
+        // FIXME: Use revealDirect to find out more
+    }
+    private static void assertEquals(Object exp, Object act) {
+        if (exp == act || (exp != null && exp.equals(act)))  return;
+        throw new AssertionError("not equal: "+exp+", "+act);
+    }
+
+    private static void setSM() {
+        Policy.setPolicy(new TestPolicy());
+        System.setSecurityManager(new SecurityManager());
+    }
+
+    private static PrintStream oldOut;
+    private static ByteArrayOutputStream buf;
+    private static void openBuf() {
+        oldOut = System.out;
+        buf = new ByteArrayOutputStream();
+        System.setOut(new PrintStream(buf));
+    }
+    private static void closeBuf() {
+        if (buf == null)  return;
+        System.out.flush();
+        System.setOut(oldOut);
+        String[] haveLines = new String(buf.toByteArray()).split("[\n\r]+");
+        for (String line : haveLines)  System.out.println(line);
+        Iterator<String> iter = Arrays.asList(haveLines).iterator();
+        for (String want : EXPECT_OUTPUT) {
+            String have = iter.hasNext() ? iter.next() : "[EOF]";
+            if (want.equals(have))  continue;
+            System.err.println("want line: "+want);
+            System.err.println("have line: "+have);
+            throw new AssertionError("unexpected output: "+have);
+        }
+        if (iter.hasNext())
+            throw new AssertionError("unexpected output: "+iter.next());
+    }
+    private static final String[] EXPECT_OUTPUT = {
+        "Obtaining method handle constants:",
+        "MethodHandle(String,char,char)String",
+        "MethodHandle(MethodHandle,String,char,char)String",
+        "MethodHandle(MethodHandle,String,char,char)String",
+        "MethodHandle(String)Class",
+        "MethodHandle(String,boolean,ClassLoader)Class",
+        "Done."
+    };
+
+    // String.replace(String, char, char)
+    private static MethodType MT_String_replace_C2() {
+        shouldNotCallThis();
+        return methodType(String.class, char.class, char.class);
+    }
+    private static MethodHandle MH_String_replace_C2() throws ReflectiveOperationException {
+        shouldNotCallThis();
+        return lookup().findVirtual(String.class, "replace", MT_String_replace_C2());
+    }
+
+    // MethodHandle.invokeExact(...)
+    private static MethodType MT_MethodHandle_invokeExact_SC2() {
+        shouldNotCallThis();
+        return methodType(String.class, String.class, char.class, char.class);
+    }
+    private static MethodHandle MH_MethodHandle_invokeExact_SC2() throws ReflectiveOperationException {
+        shouldNotCallThis();
+        return lookup().findVirtual(MethodHandle.class, "invokeExact", MT_MethodHandle_invokeExact_SC2());
+    }
+
+    // MethodHandle.invoke(...)
+    private static MethodType MT_MethodHandle_invoke_SC2() {
+        shouldNotCallThis();
+        return methodType(String.class, String.class, char.class, char.class);
+    }
+    private static MethodHandle MH_MethodHandle_invoke_SC2() throws ReflectiveOperationException {
+        shouldNotCallThis();
+        return lookup().findVirtual(MethodHandle.class, "invoke", MT_MethodHandle_invoke_SC2());
+    }
+
+    // Class.forName(String)
+    private static MethodType MT_Class_forName_S() {
+        shouldNotCallThis();
+        return methodType(Class.class, String.class);
+    }
+    private static MethodHandle MH_Class_forName_S() throws ReflectiveOperationException {
+        shouldNotCallThis();
+        return lookup().findStatic(Class.class, "forName", MT_Class_forName_S());
+    }
+
+    // Class.forName(String, boolean, ClassLoader)
+    private static MethodType MT_Class_forName_SbCL() {
+        shouldNotCallThis();
+        return methodType(Class.class, String.class, boolean.class, ClassLoader.class);
+    }
+    private static MethodHandle MH_Class_forName_SbCL() throws ReflectiveOperationException {
+        shouldNotCallThis();
+        return lookup().findStatic(Class.class, "forName", MT_Class_forName_SbCL());
+    }
+
+    private static void shouldNotCallThis() {
+        // if this gets called, the transformation has not taken place
+        if (System.getProperty("MethodHandleConstants.allow-untransformed") != null)  return;
+        throw new AssertionError("this code should be statically transformed away by Indify");
+    }
+
+    static class TestPolicy extends Policy {
+        final PermissionCollection permissions = new Permissions();
+        TestPolicy() {
+            permissions.add(new java.io.FilePermission("<<ALL FILES>>", "read"));
+        }
+        public PermissionCollection getPermissions(ProtectionDomain domain) {
+            return permissions;
+        }
+
+        public PermissionCollection getPermissions(CodeSource codesource) {
+            return permissions;
+        }
+
+        public boolean implies(ProtectionDomain domain, Permission perm) {
+            return permissions.implies(perm);
+        }
+    }
+}