7196190: Improve method of handling MethodHandles
Bind callers to caller-sensitive methods. Reviewed-by: twisti, jjh, vlivanov, ahgross
This commit is contained in:
parent
1a3e1b43e0
commit
0a735e76f6
@ -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 @@ 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;
|
||||
}
|
||||
}
|
||||
|
@ -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 @@ 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() {
|
||||
|
@ -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 @@ 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);
|
||||
}
|
||||
|
||||
|
@ -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 @@ 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;
|
||||
|
@ -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 @@ 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;
|
||||
|
98
jdk/test/java/lang/invoke/7196190/ClassForNameTest.java
Normal file
98
jdk/test/java/lang/invoke/7196190/ClassForNameTest.java
Normal file
@ -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");
|
||||
}
|
||||
}
|
112
jdk/test/java/lang/invoke/7196190/GetUnsafeTest.java
Normal file
112
jdk/test/java/lang/invoke/7196190/GetUnsafeTest.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
181
jdk/test/java/lang/invoke/7196190/MHProxyTest.java
Normal file
181
jdk/test/java/lang/invoke/7196190/MHProxyTest.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
jdk/test/java/lang/invoke/7196190/jtreg.security.policy
Normal file
9
jdk/test/java/lang/invoke/7196190/jtreg.security.policy
Normal file
@ -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";
|
||||
};
|
Loading…
Reference in New Issue
Block a user