提交 d4b0219b 编写于 作者: J jrose

7196190: Improve method of handling MethodHandles

Summary: Bind callers to caller-sensitive methods.
Reviewed-by: twisti, jjh, vlivanov, ahgross
上级 a2f1fa8e
/*
* 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 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
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);
}
}
}
}
......@@ -385,4 +385,101 @@ class MethodHandleNatives {
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;
}
}
/*
* 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 @@ import sun.misc.Unsafe;
/*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() {
......
/*
* 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 @@ public class MethodHandles {
* 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 @@ return mh1;
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 @@ return mh1;
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 @@ return mh1;
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);
}
......
/*
* 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 @@ import sun.misc.IOUtils;
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);
// Privileged constructor.
private AnonymousClassLoader(Class<?> hostClass) {
this.hostClass = hostClass;
}
private static Class<?> getTopLevelClass(Class<?> clazz) {
for(Class<?> outer = clazz.getDeclaringClass(); outer != null;
outer = outer.getDeclaringClass()) {
clazz = outer;
}
return clazz;
}
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 @@ public class AnonymousClassLoader {
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;
......
/*
* 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 @@ public class ValueConversions {
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;
......
/*
* 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");
}
}
/*
* 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);
}
}
}
/*
* 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);
}
}
}
}
/*
* 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";
};
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册