7196190: Improve method of handling MethodHandles
Summary: Bind callers to caller-sensitive methods.
Reviewed-by: twisti, jjh, vlivanov, ahgross
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java Wed Sep 19 21:42:21 2012 +0400
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleImpl.java Thu Sep 20 14:02:55 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2012, 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
@@ -25,13 +25,14 @@
package java.lang.invoke;
-import sun.invoke.util.VerifyType;
-
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import sun.invoke.empty.Empty;
import sun.invoke.util.ValueConversions;
+import sun.invoke.util.VerifyType;
import sun.invoke.util.Wrapper;
import static java.lang.invoke.LambdaForm.*;
import static java.lang.invoke.MethodHandleStatics.*;
@@ -781,4 +782,168 @@
return mh;
}
+ /**
+ * Create an alias for the method handle which, when called,
+ * appears to be called from the same class loader and protection domain
+ * as hostClass.
+ * This is an expensive no-op unless the method which is called
+ * is sensitive to its caller. A small number of system methods
+ * are in this category, including Class.forName and Method.invoke.
+ */
+ static
+ MethodHandle bindCaller(MethodHandle mh, Class<?> hostClass) {
+ return BindCaller.bindCaller(mh, hostClass);
+ }
+
+ // Put the whole mess into its own nested class.
+ // That way we can lazily load the code and set up the constants.
+ private static class BindCaller {
+ static
+ MethodHandle bindCaller(MethodHandle mh, Class<?> hostClass) {
+ // Do not use this function to inject calls into system classes.
+ if (hostClass == null) {
+ hostClass = C_Trampoline;
+ } else if (hostClass.isArray() ||
+ hostClass.isPrimitive() ||
+ hostClass.getName().startsWith("java.") ||
+ hostClass.getName().startsWith("sun.")) {
+ throw new InternalError(); // does not happen, and should not anyway
+ }
+ // For simplicity, convert mh to a varargs-like method.
+ MethodHandle vamh = prepareForInvoker(mh);
+ // Cache the result of makeInjectedInvoker once per argument class.
+ MethodHandle bccInvoker = CV_makeInjectedInvoker.get(hostClass);
+ return restoreToType(bccInvoker.bindTo(vamh), mh.type());
+ }
+
+ // This class ("Trampoline") is known to be inside a dead-end class loader.
+ // Inject all doubtful calls into this class.
+ private static Class<?> C_Trampoline;
+ static {
+ Class<?> tramp = null;
+ try {
+ final int FRAME_COUNT_ARG = 1; // [0] Reflection [1] Trampoline
+ java.lang.reflect.Method gcc = sun.reflect.Reflection.class.getMethod("getCallerClass", int.class);
+ tramp = (Class<?>) sun.reflect.misc.MethodUtil.invoke(gcc, null, new Object[]{ FRAME_COUNT_ARG });
+ if (tramp.getClassLoader() == BindCaller.class.getClassLoader())
+ throw new RuntimeException(tramp.getName()+" class loader");
+ } catch (Throwable ex) {
+ throw new InternalError(ex);
+ }
+ C_Trampoline = tramp;
+ }
+
+ private static MethodHandle makeInjectedInvoker(Class<?> hostClass) {
+ Class<?> bcc = UNSAFE.defineAnonymousClass(hostClass, T_BYTES, null);
+ if (hostClass.getClassLoader() != bcc.getClassLoader())
+ throw new InternalError(hostClass.getName()+" (CL)");
+ try {
+ if (hostClass.getProtectionDomain() != bcc.getProtectionDomain())
+ throw new InternalError(hostClass.getName()+" (PD)");
+ } catch (SecurityException ex) {
+ // Self-check was blocked by security manager. This is OK.
+ // In fact the whole try body could be turned into an assertion.
+ }
+ try {
+ MethodHandle init = IMPL_LOOKUP.findStatic(bcc, "init", MethodType.methodType(void.class));
+ init.invokeExact(); // force initialization of the class
+ } catch (Throwable ex) {
+ throw uncaughtException(ex);
+ }
+ MethodHandle bccInvoker;
+ try {
+ MethodType invokerMT = MethodType.methodType(Object.class, MethodHandle.class, Object[].class);
+ bccInvoker = IMPL_LOOKUP.findStatic(bcc, "invoke_V", invokerMT);
+ } catch (ReflectiveOperationException ex) {
+ throw uncaughtException(ex);
+ }
+ // Test the invoker, to ensure that it really injects into the right place.
+ try {
+ MethodHandle vamh = prepareForInvoker(MH_checkCallerClass);
+ Object ok = bccInvoker.invokeExact(vamh, new Object[]{hostClass, bcc});
+ } catch (Throwable ex) {
+ throw new InternalError(ex);
+ }
+ return bccInvoker;
+ }
+ private static ClassValue<MethodHandle> CV_makeInjectedInvoker = new ClassValue<MethodHandle>() {
+ @Override protected MethodHandle computeValue(Class<?> hostClass) {
+ return makeInjectedInvoker(hostClass);
+ }
+ };
+
+ // Adapt mh so that it can be called directly from an injected invoker:
+ private static MethodHandle prepareForInvoker(MethodHandle mh) {
+ mh = mh.asFixedArity();
+ MethodType mt = mh.type();
+ int arity = mt.parameterCount();
+ MethodHandle vamh = mh.asType(mt.generic());
+ vamh.internalForm().compileToBytecode(); // eliminate LFI stack frames
+ vamh = vamh.asSpreader(Object[].class, arity);
+ vamh.internalForm().compileToBytecode(); // eliminate LFI stack frames
+ return vamh;
+ }
+
+ // Undo the adapter effect of prepareForInvoker:
+ private static MethodHandle restoreToType(MethodHandle vamh, MethodType type) {
+ return vamh.asCollector(Object[].class, type.parameterCount()).asType(type);
+ }
+
+ private static final MethodHandle MH_checkCallerClass;
+ static {
+ final Class<?> THIS_CLASS = BindCaller.class;
+ assert(checkCallerClass(THIS_CLASS, THIS_CLASS));
+ try {
+ MH_checkCallerClass = IMPL_LOOKUP
+ .findStatic(THIS_CLASS, "checkCallerClass",
+ MethodType.methodType(boolean.class, Class.class, Class.class));
+ assert((boolean) MH_checkCallerClass.invokeExact(THIS_CLASS, THIS_CLASS));
+ } catch (Throwable ex) {
+ throw new InternalError(ex);
+ }
+ }
+
+ private static boolean checkCallerClass(Class<?> expected, Class<?> expected2) {
+ final int FRAME_COUNT_ARG = 2; // [0] Reflection [1] BindCaller [2] Expected
+ Class<?> actual = sun.reflect.Reflection.getCallerClass(FRAME_COUNT_ARG);
+ if (actual != expected && actual != expected2)
+ throw new InternalError("found "+actual.getName()+", expected "+expected.getName()
+ +(expected == expected2 ? "" : ", or else "+expected2.getName()));
+ return true;
+ }
+
+ private static final byte[] T_BYTES;
+ static {
+ final Object[] values = {null};
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ try {
+ Class<T> tClass = T.class;
+ String tName = tClass.getName();
+ String tResource = tName.substring(tName.lastIndexOf('.')+1)+".class";
+ java.net.URLConnection uconn = tClass.getResource(tResource).openConnection();
+ int len = uconn.getContentLength();
+ byte[] bytes = new byte[len];
+ try (java.io.InputStream str = uconn.getInputStream()) {
+ int nr = str.read(bytes);
+ if (nr != len) throw new java.io.IOException(tResource);
+ }
+ values[0] = bytes;
+ } catch (java.io.IOException ex) {
+ throw new InternalError(ex);
+ }
+ return null;
+ }
+ });
+ T_BYTES = (byte[]) values[0];
+ }
+
+ // The following class is used as a template for Unsafe.defineAnonymousClass:
+ private static class T {
+ static void init() { } // side effect: initializes this class
+ static Object invoke_V(MethodHandle vamh, Object[] args) throws Throwable {
+ return vamh.invokeExact(args);
+ }
+ }
+ }
}
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java Wed Sep 19 21:42:21 2012 +0400
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleNatives.java Thu Sep 20 14:02:55 2012 -0700
@@ -385,4 +385,101 @@
throw err;
}
}
+
+ /**
+ * Is this method a caller-sensitive method?
+ * I.e., does it call Reflection.getCallerClass or a similer method
+ * to ask about the identity of its caller?
+ */
+ // FIXME: Replace this pattern match by an annotation @sun.reflect.CallerSensitive.
+ static boolean isCallerSensitive(MemberName mem) {
+ assert(mem.isInvocable());
+ Class<?> defc = mem.getDeclaringClass();
+ switch (mem.getName()) {
+ case "doPrivileged":
+ return defc == java.security.AccessController.class;
+ case "getUnsafe":
+ return defc == sun.misc.Unsafe.class;
+ case "lookup":
+ return defc == java.lang.invoke.MethodHandles.class;
+ case "invoke":
+ return defc == java.lang.reflect.Method.class;
+ case "get":
+ case "getBoolean":
+ case "getByte":
+ case "getChar":
+ case "getShort":
+ case "getInt":
+ case "getLong":
+ case "getFloat":
+ case "getDouble":
+ case "set":
+ case "setBoolean":
+ case "setByte":
+ case "setChar":
+ case "setShort":
+ case "setInt":
+ case "setLong":
+ case "setFloat":
+ case "setDouble":
+ return defc == java.lang.reflect.Field.class;
+ case "newInstance":
+ if (defc == java.lang.reflect.Constructor.class) return true;
+ if (defc == java.lang.Class.class) return true;
+ break;
+ case "forName":
+ case "getClassLoader":
+ case "getClasses":
+ case "getFields":
+ case "getMethods":
+ case "getConstructors":
+ case "getDeclaredClasses":
+ case "getDeclaredFields":
+ case "getDeclaredMethods":
+ case "getDeclaredConstructors":
+ case "getField":
+ case "getMethod":
+ case "getConstructor":
+ case "getDeclaredField":
+ case "getDeclaredMethod":
+ case "getDeclaredConstructor":
+ return defc == java.lang.Class.class;
+ case "getConnection":
+ case "getDriver":
+ case "getDrivers":
+ case "deregisterDriver":
+ return defc == java.sql.DriverManager.class;
+ case "newUpdater":
+ if (defc == java.util.concurrent.atomic.AtomicIntegerFieldUpdater.class) return true;
+ if (defc == java.util.concurrent.atomic.AtomicLongFieldUpdater.class) return true;
+ if (defc == java.util.concurrent.atomic.AtomicReferenceFieldUpdater.class) return true;
+ break;
+ case "getContextClassLoader":
+ return defc == java.lang.Thread.class;
+ case "getPackage":
+ case "getPackages":
+ return defc == java.lang.Package.class;
+ case "getParent":
+ case "getSystemClassLoader":
+ return defc == java.lang.ClassLoader.class;
+ case "load":
+ case "loadLibrary":
+ if (defc == java.lang.Runtime.class) return true;
+ if (defc == java.lang.System.class) return true;
+ break;
+ case "getCallerClass":
+ if (defc == sun.reflect.Reflection.class) return true;
+ if (defc == java.lang.System.class) return true;
+ break;
+ case "getCallerClassLoader":
+ return defc == java.lang.ClassLoader.class;
+ case "getProxyClass":
+ case "newProxyInstance":
+ return defc == java.lang.reflect.Proxy.class;
+ case "getBundle":
+ case "clearCache":
+ return defc == java.util.ResourceBundle.class;
+ }
+ return false;
+ }
}
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandleStatics.java Wed Sep 19 21:42:21 2012 +0400
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandleStatics.java Thu Sep 20 14:02:55 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2012, 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
@@ -108,7 +108,7 @@
/*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object obj, Object obj2) {
return new IllegalArgumentException(message(message, obj, obj2));
}
- /*non-public*/ static Error uncaughtException(Exception ex) {
+ /*non-public*/ static Error uncaughtException(Throwable ex) {
throw new InternalError("uncaught exception", ex);
}
static Error NYI() {
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandles.java Wed Sep 19 21:42:21 2012 +0400
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandles.java Thu Sep 20 14:02:55 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2012, 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
@@ -329,6 +329,7 @@
* where {@code defcPkg} is the package of {@code defc}.
* </ul>
*/
+ // FIXME in MR1: clarify that the bytecode behavior of a caller-ID method (like Class.forName) is relative to the lookupClass used to create the method handle, not the dynamic caller of the method handle
public static final
class Lookup {
/** The class on behalf of whom the lookup is being performed. */
@@ -1209,6 +1210,7 @@
if (method.isMethodHandleInvoke())
return fakeMethodHandleInvoke(method);
MethodHandle mh = DirectMethodHandle.make(refc, method);
+ mh = maybeBindCaller(method, mh);
mh = mh.setVarargs(method);
if (doRestrict)
mh = restrictReceiver(method, mh, lookupClass());
@@ -1217,6 +1219,16 @@
private MethodHandle fakeMethodHandleInvoke(MemberName method) {
return throwException(method.getReturnType(), UnsupportedOperationException.class);
}
+ private MethodHandle maybeBindCaller(MemberName method, MethodHandle mh) throws IllegalAccessException {
+ if (allowedModes == TRUSTED || !MethodHandleNatives.isCallerSensitive(method))
+ return mh;
+ Class<?> hostClass = lookupClass;
+ if ((allowedModes & PRIVATE) == 0) // caller must use full-power lookup
+ hostClass = null;
+ MethodHandle cbmh = MethodHandleImpl.bindCaller(mh, hostClass);
+ // Note: caller will apply varargs after this step happens.
+ return cbmh;
+ }
private MethodHandle getDirectField(byte refKind, Class<?> refc, MemberName field) throws IllegalAccessException {
checkField(refKind, refc, field);
MethodHandle mh = DirectMethodHandle.make(refc, field);
@@ -1229,6 +1241,7 @@
private MethodHandle getDirectConstructor(Class<?> refc, MemberName ctor) throws IllegalAccessException {
assert(ctor.isConstructor());
checkAccess(REF_newInvokeSpecial, refc, ctor);
+ assert(!MethodHandleNatives.isCallerSensitive(ctor)); // maybeBindCaller not relevant here
return DirectMethodHandle.make(ctor).setVarargs(ctor);
}
--- a/jdk/src/share/classes/sun/invoke/anon/AnonymousClassLoader.java Wed Sep 19 21:42:21 2012 +0400
+++ b/jdk/src/share/classes/sun/invoke/anon/AnonymousClassLoader.java Thu Sep 20 14:02:55 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2012, 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
@@ -73,74 +73,14 @@
public class AnonymousClassLoader {
final Class<?> hostClass;
- // Note: Do not refactor the calls to checkHostClass unless you
- // also adjust this constant:
- private static int CHC_CALLERS = 3;
-
- public AnonymousClassLoader() {
- this.hostClass = checkHostClass(null);
- }
- public AnonymousClassLoader(Class<?> hostClass) {
- this.hostClass = checkHostClass(hostClass);
- }
-
- private static Class<?> getTopLevelClass(Class<?> clazz) {
- for(Class<?> outer = clazz.getDeclaringClass(); outer != null;
- outer = outer.getDeclaringClass()) {
- clazz = outer;
- }
- return clazz;
+ // Privileged constructor.
+ private AnonymousClassLoader(Class<?> hostClass) {
+ this.hostClass = hostClass;
}
- private static Class<?> checkHostClass(Class<?> hostClass) {
- // called only from the constructor
- // does a context-sensitive check on caller class
- // CC[0..3] = {Reflection, this.checkHostClass, this.<init>, caller}
- Class<?> caller = sun.reflect.Reflection.getCallerClass(CHC_CALLERS);
-
- if (caller == null) {
- // called from the JVM directly
- if (hostClass == null)
- return AnonymousClassLoader.class; // anything central will do
- return hostClass;
- }
-
- if (hostClass == null)
- hostClass = caller; // default value is caller itself
-
- // anonymous class will access hostClass on behalf of caller
- Class<?> callee = hostClass;
-
- if (caller == callee)
- // caller can always nominate itself to grant caller's own access rights
- return hostClass;
-
- // normalize caller and callee to their top-level classes:
- caller = getTopLevelClass(caller);
- callee = getTopLevelClass(callee);
- if (caller == callee)
- return caller;
-
- ClassLoader callerCL = caller.getClassLoader();
- if (callerCL == null) {
- // caller is trusted code, so accept the proposed hostClass
- return hostClass;
- }
-
- // %%% should do something with doPrivileged, because trusted
- // code should have a way to execute on behalf of
- // partially-trusted clients
-
- // Does the caller have the right to access the private
- // members of the callee? If not, raise an error.
- final int ACC_PRIVATE = 2;
- try {
- sun.reflect.Reflection.ensureMemberAccess(caller, callee, null, ACC_PRIVATE);
- } catch (IllegalAccessException ee) {
- throw new IllegalArgumentException(ee);
- }
-
- return hostClass;
+ public static AnonymousClassLoader make(sun.misc.Unsafe unsafe, Class<?> hostClass) {
+ if (unsafe == null) throw new NullPointerException();
+ return new AnonymousClassLoader(hostClass);
}
public Class<?> loadClass(byte[] classFile) {
@@ -249,7 +189,7 @@
private static int fakeNameCounter = 99999;
// ignore two warnings on this line:
- static sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
+ private static sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
// preceding line requires that this class be on the boot class path
static private final Method defineAnonymousClass;
--- a/jdk/src/share/classes/sun/invoke/util/ValueConversions.java Wed Sep 19 21:42:21 2012 +0400
+++ b/jdk/src/share/classes/sun/invoke/util/ValueConversions.java Thu Sep 20 14:02:55 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2012, 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
@@ -522,13 +522,19 @@
static {
MethodHandle mh = null;
try {
- java.lang.reflect.Method m = MethodHandles.class
+ final java.lang.reflect.Method m = MethodHandles.class
.getDeclaredMethod("collectArguments",
MethodHandle.class, int.class, MethodHandle.class);
- m.setAccessible(true);
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ @Override
+ public Void run() {
+ m.setAccessible(true);
+ return null;
+ }
+ });
mh = IMPL_LOOKUP.unreflect(m);
- } catch (ReflectiveOperationException | SecurityException ex) {
+ } catch (ReflectiveOperationException ex) {
throw new InternalError(ex);
}
COLLECT_ARGUMENTS = mh;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/7196190/ClassForNameTest.java Thu Sep 20 14:02:55 2012 -0700
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2012, 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 7196190
+ * @summary Improve method of handling MethodHandles
+ *
+ * @run main/othervm ClassForNameTest
+ */
+
+import java.lang.invoke.*;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+public class ClassForNameTest {
+ final static String NAME = ClassForNameTest.class.getName();
+
+ public static void main(String[] args) throws Throwable {
+ {
+ final MethodType mt = MethodType.methodType(Class.class, String.class);
+ final MethodHandle mh = MethodHandles.lookup()
+ .findStatic(Class.class, "forName", mt);
+
+ Class.forName(NAME);
+
+ mh.invoke(NAME);
+ mh.bindTo(NAME).invoke();
+ mh.invokeWithArguments(Arrays.asList(NAME));
+ mh.invokeWithArguments(NAME);
+ Class cls = (Class) mh.invokeExact(NAME);
+ }
+
+ {
+ final Method fnMethod = Class.class.getMethod("forName", String.class);
+ final MethodType mt = MethodType.methodType(Object.class, Object.class, Object[].class);
+ final MethodHandle mh = MethodHandles.lookup()
+ .findVirtual(Method.class, "invoke", mt)
+ .bindTo(fnMethod);
+
+ fnMethod.invoke(null, NAME);
+
+ mh.bindTo(null).bindTo(new Object[]{NAME}).invoke();
+ mh.invoke(null, new Object[]{NAME});
+ mh.invokeWithArguments(null, new Object[]{NAME});
+ mh.invokeWithArguments(Arrays.asList(null, new Object[]{NAME}));
+ Object obj = mh.invokeExact((Object) null, new Object[]{NAME});
+ }
+
+ {
+ final Method fnMethod = Class.class.getMethod("forName", String.class);
+ final MethodType mt = MethodType.methodType(Object.class, Object.class, Object[].class);
+
+ final MethodHandle mh = MethodHandles.lookup()
+ .bind(fnMethod, "invoke", mt);
+
+ mh.bindTo(null).bindTo(new Object[]{NAME}).invoke();
+ mh.invoke(null, new Object[]{NAME});
+ mh.invokeWithArguments(null, NAME);
+ mh.invokeWithArguments(Arrays.asList(null, NAME));
+ Object obj = mh.invokeExact((Object) null, new Object[]{NAME});
+ }
+
+ {
+ final Method fnMethod = Class.class.getMethod("forName", String.class);
+ final MethodHandle mh = MethodHandles.lookup().unreflect(fnMethod);
+
+ mh.bindTo(NAME).invoke();
+ mh.invoke(NAME);
+ mh.invokeWithArguments(NAME);
+ mh.invokeWithArguments(Arrays.asList(NAME));
+ Class cls = (Class) mh.invokeExact(NAME);
+ }
+
+ System.out.println("TEST PASSED");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/7196190/GetUnsafeTest.java Thu Sep 20 14:02:55 2012 -0700
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2012, 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 7196190
+ * @summary Improve method of handling MethodHandles
+ *
+ * @run main/othervm/policy=jtreg.security.policy/secure=java.lang.SecurityManager GetUnsafeTest
+ */
+
+import java.lang.invoke.*;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+public class GetUnsafeTest {
+ final static String NAME = "sun.misc.Unsafe";
+
+ private static boolean isTestFailed = false;
+
+ private static void fail() {
+ isTestFailed = true;
+ try { throw new Exception(); } catch (Throwable e) {
+ StackTraceElement frame = e.getStackTrace()[1];
+ System.out.printf("Failed at %s:%d\n", frame.getFileName(), frame.getLineNumber());
+ }
+ }
+
+ public static void main(String[] args) throws Throwable {
+ {
+ final MethodType mt = MethodType.methodType(Class.class, String.class);
+ final MethodHandle mh = MethodHandles.lookup()
+ .findStatic(Class.class, "forName", mt);
+
+ try { Class.forName(NAME); fail(); } catch (Throwable e) {}
+
+ try { mh.invoke(NAME); fail(); } catch (Throwable e) {}
+ try { mh.bindTo(NAME).invoke(); fail(); } catch (Throwable e) {}
+ try { mh.invokeWithArguments(Arrays.asList(NAME)); fail(); } catch (Throwable e) {}
+ try { mh.invokeWithArguments(NAME); fail(); } catch (Throwable e) {}
+ try { Class cls = (Class) mh.invokeExact(NAME); fail(); } catch (Throwable e) {}
+ }
+
+ {
+ final Method fnMethod = Class.class.getMethod("forName", String.class);
+ final MethodType mt = MethodType.methodType(Object.class, Object.class, Object[].class);
+ final MethodHandle mh = MethodHandles.lookup()
+ .findVirtual(Method.class, "invoke", mt)
+ .bindTo(fnMethod);
+
+ try { fnMethod.invoke(null, NAME); fail(); } catch (Throwable e) {}
+
+ try { mh.bindTo(null).bindTo(new Object[]{NAME}).invoke(); fail(); } catch (Throwable e) {}
+ try { mh.invoke(null, new Object[]{NAME}); fail(); } catch (Throwable e) {}
+ try { mh.invokeWithArguments(null, new Object[]{NAME}); fail(); } catch (Throwable e) {}
+ try { mh.invokeWithArguments(Arrays.asList(null, new Object[]{NAME})); fail(); } catch (Throwable e) {}
+ try { Object obj = mh.invokeExact((Object) null, new Object[]{NAME}); fail(); } catch (Throwable e) {}
+ }
+
+ {
+ final Method fnMethod = Class.class.getMethod("forName", String.class);
+ final MethodType mt = MethodType.methodType(Object.class, Object.class, Object[].class);
+
+ final MethodHandle mh = MethodHandles.lookup().bind(fnMethod, "invoke", mt);
+
+ try { mh.bindTo(null).bindTo(new Object[]{NAME}).invoke(); fail(); } catch (Throwable e) {}
+ try { mh.invoke(null, new Object[]{NAME}); fail(); } catch (Throwable e) {}
+ try { mh.invokeWithArguments(null, NAME); fail(); } catch (Throwable e) {}
+ try { mh.invokeWithArguments(Arrays.asList(null, NAME)); fail(); } catch (Throwable e) {}
+ try { Object obj = mh.invokeExact((Object) null, new Object[]{NAME}); fail(); } catch (Throwable e) {}
+ }
+
+ {
+ final Method fnMethod = Class.class.getMethod("forName", String.class);
+ final MethodHandle mh = MethodHandles.lookup().unreflect(fnMethod);
+
+ try { mh.bindTo(NAME).invoke(); fail(); } catch (Throwable e) {}
+ try { mh.invoke(NAME); fail(); } catch (Throwable e) {}
+ try { mh.invokeWithArguments(NAME); fail(); } catch (Throwable e) {}
+ try { mh.invokeWithArguments(Arrays.asList(NAME)); fail(); } catch (Throwable e) {}
+ try { Class cls = (Class) mh.invokeExact(NAME); fail(); } catch (Throwable e) {}
+ }
+
+ if (!isTestFailed) {
+ System.out.println("TEST PASSED");
+ } else {
+ System.out.println("TEST FAILED");
+ System.exit(1);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/7196190/MHProxyTest.java Thu Sep 20 14:02:55 2012 -0700
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2012, 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 7196190
+ * @summary Improve method of handling MethodHandles
+ *
+ * @run main/othervm MHProxyTest
+ */
+
+import java.lang.invoke.*;
+import java.security.*;
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+
+public class MHProxyTest {
+ private static final Class<?> C_Unsafe;
+ private static final MethodHandle MH_getUnsafe;
+ static {
+ // Do these before there is a SM installed.
+ C_Unsafe = sun.misc.Unsafe.class; // EXPECT A WARNING ON THIS LINE
+ Lookup lookup = lookup();
+ MethodHandle gumh = null;
+ try {
+ gumh = lookup.findStatic(C_Unsafe, "getUnsafe", methodType(C_Unsafe));
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex.toString());
+ }
+ MH_getUnsafe = gumh;
+ // Try some different lookups:
+ try {
+ lookup.in(Object.class).findStatic(C_Unsafe, "getUnsafe", methodType(C_Unsafe));
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex.toString());
+ }
+ lookup = lookup().in(C_Unsafe);
+ try {
+ lookup.in(C_Unsafe).findStatic(C_Unsafe, "getUnsafe", methodType(C_Unsafe));
+ } catch (ReflectiveOperationException ex) {
+ throw new InternalError(ex.toString());
+ }
+ }
+
+ public static void main(String[] args) throws Throwable {
+ System.setSecurityManager(new SecurityManager());
+ Lookup lookup = lookup();
+ testBasic(lookup);
+ testDoPriv(lookup);
+ testSetVar();
+ Lookup l2 = lookup.in(Object.class);
+ System.out.println("=== "+l2);
+ testBasic(l2);
+ testDoPriv(l2);
+ Lookup l3 = lookup.in(C_Unsafe);
+ System.out.println("=== "+l3);
+ testBasic(l3);
+ testDoPriv(l3);
+ if (failure != null)
+ throw failure;
+ }
+
+ private static Throwable failure;
+ private static void fail(Throwable ex) {
+ if (failure == null)
+ failure = ex;
+ StackTraceElement frame = new Exception().getStackTrace()[1];
+ System.out.printf("Failed at %s:%d: %s\n", frame.getFileName(), frame.getLineNumber(), ex);
+ }
+ private static void ok(Throwable ex) {
+ StackTraceElement frame = new Exception().getStackTrace()[1];
+ System.out.printf("OK at %s:%d: %s\n", frame.getFileName(), frame.getLineNumber(), ex);
+ }
+
+ private static void testBasic(Lookup lookup) throws Throwable {
+ // Verify that we can't get to this guy under the SM:
+ try {
+ MethodHandle badmh = lookup.findStatic(C_Unsafe, "getUnsafe", methodType(C_Unsafe));
+ assert(badmh.type() == methodType(C_Unsafe));
+ badmh = badmh.asType(badmh.type().generic());
+ Object u = C_Unsafe.cast(badmh.invokeExact());
+ assert(C_Unsafe.isInstance(u));
+ fail(new AssertionError("got mh to getUnsafe!"));
+ } catch (SecurityException ex) {
+ ok(ex);
+ }
+ try {
+ Object u = MH_getUnsafe.invokeWithArguments();
+ assert(C_Unsafe.isInstance(u));
+ fail(new AssertionError("got the Unsafe object! (MH invoke)"));
+ } catch (SecurityException ex) {
+ ok(ex);
+ }
+ try {
+ MethodHandle mh = MH_getUnsafe;
+ mh = mh.asType(mh.type().generic());
+ mh = foldArguments(identity(Object.class), mh);
+ mh = filterReturnValue(mh, identity(Object.class));
+ Object u = mh.invokeExact();
+ assert(C_Unsafe.isInstance(u));
+ fail(new AssertionError("got the Unsafe object! (MH invokeWithArguments)"));
+ } catch (SecurityException ex) {
+ ok(ex);
+ }
+ }
+
+ private static void testDoPriv(Lookup lookup) throws Throwable {
+ PrivilegedAction privAct = MethodHandleProxies.asInterfaceInstance(PrivilegedAction.class, MH_getUnsafe);
+ try {
+ Object u = AccessController.doPrivileged(privAct);
+ assert(C_Unsafe.isInstance(u));
+ fail(new AssertionError("got the Unsafe object! (static doPriv)"));
+ } catch (SecurityException ex) {
+ ok(ex);
+ }
+ MethodHandle MH_doPriv = lookup.findStatic(AccessController.class, "doPrivileged",
+ methodType(Object.class, PrivilegedAction.class));
+ MH_doPriv = MH_doPriv.bindTo(privAct);
+ try {
+ Object u = MH_doPriv.invoke();
+ assert(C_Unsafe.isInstance(u));
+ fail(new AssertionError("got the Unsafe object! (MH + doPriv)"));
+ } catch (SecurityException ex) {
+ ok(ex);
+ }
+ // try one more layer of indirection:
+ Runnable rbl = MethodHandleProxies.asInterfaceInstance(Runnable.class, MH_doPriv);
+ try {
+ rbl.run();
+ fail(new AssertionError("got the Unsafe object! (Runnable + MH + doPriv)"));
+ } catch (SecurityException ex) {
+ ok(ex);
+ }
+ }
+
+ private static void testSetVar() throws Throwable {
+ {
+ // Test the box pattern:
+ Object[] box = new Object[1];
+ MethodHandle MH_getFoo = identity(Object.class).bindTo("foo");
+ MethodHandle MH_storeToBox = insertArguments(arrayElementSetter(Object[].class), 0, box, 0);
+ MethodHandle mh = filterReturnValue(MH_getFoo, MH_storeToBox);
+ mh.invokeExact();
+ assert(box[0] == "foo");
+ }
+ {
+ Object[] box = new Object[1];
+ MethodHandle MH_storeToBox = insertArguments(arrayElementSetter(Object[].class), 0, box, 0);
+ MethodHandle mh = filterReturnValue(MH_getUnsafe.asType(MH_getUnsafe.type().generic()), MH_storeToBox);
+ try {
+ mh.invokeExact();
+ Object u = box[0];
+ assert(C_Unsafe.isInstance(u));
+ fail(new AssertionError("got the Unsafe object! (MH + setElement)"));
+ } catch (SecurityException ex) {
+ ok(ex);
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/7196190/jtreg.security.policy Thu Sep 20 14:02:55 2012 -0700
@@ -0,0 +1,9 @@
+/*
+ * security policy used by the test process
+ * must allow file reads so that jtreg itself can run
+ */
+
+grant {
+ // standard test activation permissions
+ permission java.io.FilePermission "*", "read";
+};