提交 e3e92002 编写于 作者: V vlivanov

8006439: Improve MethodHandles coverage

Reviewed-by: jrose, twisti
上级 e06c9d77
...@@ -801,12 +801,11 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; ...@@ -801,12 +801,11 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
static static
MethodHandle bindCaller(MethodHandle mh, Class<?> hostClass) { MethodHandle bindCaller(MethodHandle mh, Class<?> hostClass) {
// Do not use this function to inject calls into system classes. // Do not use this function to inject calls into system classes.
if (hostClass == null) { if (hostClass == null
hostClass = C_Trampoline; || (hostClass.isArray() ||
} else if (hostClass.isArray() ||
hostClass.isPrimitive() || hostClass.isPrimitive() ||
hostClass.getName().startsWith("java.") || hostClass.getName().startsWith("java.") ||
hostClass.getName().startsWith("sun.")) { hostClass.getName().startsWith("sun."))) {
throw new InternalError(); // does not happen, and should not anyway throw new InternalError(); // does not happen, and should not anyway
} }
// For simplicity, convert mh to a varargs-like method. // For simplicity, convert mh to a varargs-like method.
...@@ -816,23 +815,6 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; ...@@ -816,23 +815,6 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
return restoreToType(bccInvoker.bindTo(vamh), mh.type()); 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) { private static MethodHandle makeInjectedInvoker(Class<?> hostClass) {
Class<?> bcc = UNSAFE.defineAnonymousClass(hostClass, T_BYTES, null); Class<?> bcc = UNSAFE.defineAnonymousClass(hostClass, T_BYTES, null);
if (hostClass.getClassLoader() != bcc.getClassLoader()) if (hostClass.getClassLoader() != bcc.getClassLoader())
......
...@@ -393,11 +393,14 @@ class MethodHandleNatives { ...@@ -393,11 +393,14 @@ class MethodHandleNatives {
*/ */
// FIXME: Replace this pattern match by an annotation @sun.reflect.CallerSensitive. // FIXME: Replace this pattern match by an annotation @sun.reflect.CallerSensitive.
static boolean isCallerSensitive(MemberName mem) { static boolean isCallerSensitive(MemberName mem) {
assert(mem.isInvocable()); if (!mem.isInvocable()) return false; // fields are not caller sensitive
Class<?> defc = mem.getDeclaringClass(); Class<?> defc = mem.getDeclaringClass();
switch (mem.getName()) { switch (mem.getName()) {
case "doPrivileged": case "doPrivileged":
case "doPrivilegedWithCombiner":
return defc == java.security.AccessController.class; return defc == java.security.AccessController.class;
case "checkMemberAccess":
return canBeCalledVirtual(mem, java.lang.SecurityManager.class);
case "getUnsafe": case "getUnsafe":
return defc == sun.misc.Unsafe.class; return defc == sun.misc.Unsafe.class;
case "lookup": case "lookup":
...@@ -455,7 +458,7 @@ class MethodHandleNatives { ...@@ -455,7 +458,7 @@ class MethodHandleNatives {
if (defc == java.util.concurrent.atomic.AtomicReferenceFieldUpdater.class) return true; if (defc == java.util.concurrent.atomic.AtomicReferenceFieldUpdater.class) return true;
break; break;
case "getContextClassLoader": case "getContextClassLoader":
return defc == java.lang.Thread.class; return canBeCalledVirtual(mem, java.lang.Thread.class);
case "getPackage": case "getPackage":
case "getPackages": case "getPackages":
return defc == java.lang.Package.class; return defc == java.lang.Package.class;
...@@ -473,6 +476,8 @@ class MethodHandleNatives { ...@@ -473,6 +476,8 @@ class MethodHandleNatives {
break; break;
case "getCallerClassLoader": case "getCallerClassLoader":
return defc == java.lang.ClassLoader.class; return defc == java.lang.ClassLoader.class;
case "registerAsParallelCapable":
return canBeCalledVirtual(mem, java.lang.ClassLoader.class);
case "getProxyClass": case "getProxyClass":
case "newProxyInstance": case "newProxyInstance":
return defc == java.lang.reflect.Proxy.class; return defc == java.lang.reflect.Proxy.class;
...@@ -494,4 +499,11 @@ class MethodHandleNatives { ...@@ -494,4 +499,11 @@ class MethodHandleNatives {
throw new InternalError(e); throw new InternalError(e);
} }
} }
static boolean canBeCalledVirtual(MemberName symbolicRef, Class<?> definingClass) {
Class<?> symbolicRefClass = symbolicRef.getDeclaringClass();
if (symbolicRefClass == definingClass) return true;
if (symbolicRef.isStatic() || symbolicRef.isPrivate()) return false;
return (definingClass.isAssignableFrom(symbolicRefClass) || // Msym overrides Mdef
symbolicRefClass.isInterface()); // Mdef implements Msym
}
} }
...@@ -598,7 +598,8 @@ public class MethodHandles { ...@@ -598,7 +598,8 @@ public class MethodHandles {
MethodHandle findStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException { MethodHandle findStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
MemberName method = resolveOrFail(REF_invokeStatic, refc, name, type); MemberName method = resolveOrFail(REF_invokeStatic, refc, name, type);
checkSecurityManager(refc, method); // stack walk magic: do not refactor checkSecurityManager(refc, method); // stack walk magic: do not refactor
return getDirectMethod(REF_invokeStatic, refc, method); Class<?> callerClass = findBoundCallerClass(method); // stack walk magic: do not refactor
return getDirectMethod(REF_invokeStatic, refc, method, callerClass);
} }
/** /**
...@@ -652,7 +653,8 @@ public class MethodHandles { ...@@ -652,7 +653,8 @@ public class MethodHandles {
byte refKind = (refc.isInterface() ? REF_invokeInterface : REF_invokeVirtual); byte refKind = (refc.isInterface() ? REF_invokeInterface : REF_invokeVirtual);
MemberName method = resolveOrFail(refKind, refc, name, type); MemberName method = resolveOrFail(refKind, refc, name, type);
checkSecurityManager(refc, method); // stack walk magic: do not refactor checkSecurityManager(refc, method); // stack walk magic: do not refactor
return getDirectMethod(refKind, refc, method); Class<?> callerClass = findBoundCallerClass(method);
return getDirectMethod(refKind, refc, method, callerClass);
} }
private MethodHandle findVirtualForMH(String name, MethodType type) { private MethodHandle findVirtualForMH(String name, MethodType type) {
// these names require special lookups because of the implicit MethodType argument // these names require special lookups because of the implicit MethodType argument
...@@ -736,7 +738,8 @@ public class MethodHandles { ...@@ -736,7 +738,8 @@ public class MethodHandles {
Lookup specialLookup = this.in(specialCaller); Lookup specialLookup = this.in(specialCaller);
MemberName method = specialLookup.resolveOrFail(REF_invokeSpecial, refc, name, type); MemberName method = specialLookup.resolveOrFail(REF_invokeSpecial, refc, name, type);
checkSecurityManager(refc, method); // stack walk magic: do not refactor checkSecurityManager(refc, method); // stack walk magic: do not refactor
return specialLookup.getDirectMethod(REF_invokeSpecial, refc, method); Class<?> callerClass = findBoundCallerClass(method);
return specialLookup.getDirectMethod(REF_invokeSpecial, refc, method, callerClass);
} }
/** /**
...@@ -879,7 +882,8 @@ return mh1; ...@@ -879,7 +882,8 @@ return mh1;
Class<? extends Object> refc = receiver.getClass(); // may get NPE Class<? extends Object> refc = receiver.getClass(); // may get NPE
MemberName method = resolveOrFail(REF_invokeSpecial, refc, name, type); MemberName method = resolveOrFail(REF_invokeSpecial, refc, name, type);
checkSecurityManager(refc, method); // stack walk magic: do not refactor checkSecurityManager(refc, method); // stack walk magic: do not refactor
MethodHandle mh = getDirectMethodNoRestrict(REF_invokeSpecial, refc, method); Class<?> callerClass = findBoundCallerClass(method); // stack walk magic: do not refactor
MethodHandle mh = getDirectMethodNoRestrict(REF_invokeSpecial, refc, method, callerClass);
return mh.bindReceiver(receiver).setVarargs(method); return mh.bindReceiver(receiver).setVarargs(method);
} }
...@@ -910,8 +914,9 @@ return mh1; ...@@ -910,8 +914,9 @@ return mh1;
if (refKind == REF_invokeSpecial) if (refKind == REF_invokeSpecial)
refKind = REF_invokeVirtual; refKind = REF_invokeVirtual;
assert(method.isMethod()); assert(method.isMethod());
Class<?> callerClass = findBoundCallerClass(method); // stack walk magic: do not refactor
Lookup lookup = m.isAccessible() ? IMPL_LOOKUP : this; Lookup lookup = m.isAccessible() ? IMPL_LOOKUP : this;
return lookup.getDirectMethod(refKind, method.getDeclaringClass(), method); return lookup.getDirectMethod(refKind, method.getDeclaringClass(), method, callerClass);
} }
/** /**
...@@ -940,8 +945,9 @@ return mh1; ...@@ -940,8 +945,9 @@ return mh1;
Lookup specialLookup = this.in(specialCaller); Lookup specialLookup = this.in(specialCaller);
MemberName method = new MemberName(m, true); MemberName method = new MemberName(m, true);
assert(method.isMethod()); assert(method.isMethod());
Class<?> callerClass = findBoundCallerClass(method); // stack walk magic: do not refactor
// ignore m.isAccessible: this is a new kind of access // ignore m.isAccessible: this is a new kind of access
return specialLookup.getDirectMethod(REF_invokeSpecial, method.getDeclaringClass(), method); return specialLookup.getDirectMethod(REF_invokeSpecial, method.getDeclaringClass(), method, callerClass);
} }
/** /**
...@@ -1039,8 +1045,30 @@ return mh1; ...@@ -1039,8 +1045,30 @@ return mh1;
throw new MemberName(refc).makeAccessException("symbolic reference class is not public", this); throw new MemberName(refc).makeAccessException("symbolic reference class is not public", this);
} }
/**
* Find my trustable caller class if m is a caller sensitive method.
* If this lookup object has private access, then the caller class is the lookupClass.
* Otherwise, it is the caller of the currently executing public API method (e.g., findVirtual).
* This is the same caller class as is used by checkSecurityManager.
* This function performs stack walk magic: do not refactor it.
*/
Class<?> findBoundCallerClass(MemberName m) {
Class<?> callerClass = null;
if (MethodHandleNatives.isCallerSensitive(m)) {
// Do not refactor this to a more "logical" place, since it is stack walk magic.
// Note that this is the same expression as in Step 2 below in checkSecurityManager.
callerClass = ((allowedModes & PRIVATE) != 0
? lookupClass // for strong access modes, no extra check
// next line does stack walk magic; do not refactor:
: getCallerClassAtEntryPoint(true));
}
return callerClass;
}
/** /**
* Perform necessary <a href="MethodHandles.Lookup.html#secmgr">access checks</a>. * Perform necessary <a href="MethodHandles.Lookup.html#secmgr">access checks</a>.
* Determines a trustable caller class to compare with refc, the symbolic reference class.
* If this lookup object has private access, then the caller class is the lookupClass.
* Otherwise, it is the caller of the currently executing public API method (e.g., findVirtual).
* This function performs stack walk magic: do not refactor it. * This function performs stack walk magic: do not refactor it.
*/ */
void checkSecurityManager(Class<?> refc, MemberName m) { void checkSecurityManager(Class<?> refc, MemberName m) {
...@@ -1195,22 +1223,22 @@ return mh1; ...@@ -1195,22 +1223,22 @@ return mh1;
return mh.viewAsType(narrowType); return mh.viewAsType(narrowType);
} }
private MethodHandle getDirectMethod(byte refKind, Class<?> refc, MemberName method) throws IllegalAccessException { private MethodHandle getDirectMethod(byte refKind, Class<?> refc, MemberName method, Class<?> callerClass) throws IllegalAccessException {
return getDirectMethodCommon(refKind, refc, method, return getDirectMethodCommon(refKind, refc, method,
(refKind == REF_invokeSpecial || (refKind == REF_invokeSpecial ||
(MethodHandleNatives.refKindHasReceiver(refKind) && (MethodHandleNatives.refKindHasReceiver(refKind) &&
restrictProtectedReceiver(method)))); restrictProtectedReceiver(method))), callerClass);
} }
private MethodHandle getDirectMethodNoRestrict(byte refKind, Class<?> refc, MemberName method) throws IllegalAccessException { private MethodHandle getDirectMethodNoRestrict(byte refKind, Class<?> refc, MemberName method, Class<?> callerClass) throws IllegalAccessException {
return getDirectMethodCommon(refKind, refc, method, false); return getDirectMethodCommon(refKind, refc, method, false, callerClass);
} }
private MethodHandle getDirectMethodCommon(byte refKind, Class<?> refc, MemberName method, private MethodHandle getDirectMethodCommon(byte refKind, Class<?> refc, MemberName method,
boolean doRestrict) throws IllegalAccessException { boolean doRestrict, Class<?> callerClass) throws IllegalAccessException {
checkMethod(refKind, refc, method); checkMethod(refKind, refc, method);
if (method.isMethodHandleInvoke()) if (method.isMethodHandleInvoke())
return fakeMethodHandleInvoke(method); return fakeMethodHandleInvoke(method);
MethodHandle mh = DirectMethodHandle.make(refc, method); MethodHandle mh = DirectMethodHandle.make(refc, method);
mh = maybeBindCaller(method, mh); mh = maybeBindCaller(method, mh, callerClass);
mh = mh.setVarargs(method); mh = mh.setVarargs(method);
if (doRestrict) if (doRestrict)
mh = restrictReceiver(method, mh, lookupClass()); mh = restrictReceiver(method, mh, lookupClass());
...@@ -1219,12 +1247,14 @@ return mh1; ...@@ -1219,12 +1247,14 @@ return mh1;
private MethodHandle fakeMethodHandleInvoke(MemberName method) { private MethodHandle fakeMethodHandleInvoke(MemberName method) {
return throwException(method.getReturnType(), UnsupportedOperationException.class); return throwException(method.getReturnType(), UnsupportedOperationException.class);
} }
private MethodHandle maybeBindCaller(MemberName method, MethodHandle mh) throws IllegalAccessException { private MethodHandle maybeBindCaller(MemberName method, MethodHandle mh,
Class<?> callerClass)
throws IllegalAccessException {
if (allowedModes == TRUSTED || !MethodHandleNatives.isCallerSensitive(method)) if (allowedModes == TRUSTED || !MethodHandleNatives.isCallerSensitive(method))
return mh; return mh;
Class<?> hostClass = lookupClass; Class<?> hostClass = lookupClass;
if ((allowedModes & PRIVATE) == 0) // caller must use full-power lookup if ((allowedModes & PRIVATE) == 0) // caller must use full-power lookup
hostClass = null; hostClass = callerClass; // callerClass came from a security manager style stack walk
MethodHandle cbmh = MethodHandleImpl.bindCaller(mh, hostClass); MethodHandle cbmh = MethodHandleImpl.bindCaller(mh, hostClass);
// Note: caller will apply varargs after this step happens. // Note: caller will apply varargs after this step happens.
return cbmh; return cbmh;
...@@ -1262,7 +1292,7 @@ return mh1; ...@@ -1262,7 +1292,7 @@ return mh1;
} else if (MethodHandleNatives.refKindIsMethod(refKind)) { } else if (MethodHandleNatives.refKindIsMethod(refKind)) {
MemberName method = (resolved != null) ? resolved MemberName method = (resolved != null) ? resolved
: resolveOrFail(refKind, defc, name, (MethodType) type); : resolveOrFail(refKind, defc, name, (MethodType) type);
return getDirectMethod(refKind, defc, method); return getDirectMethod(refKind, defc, method, lookupClass);
} else if (refKind == REF_newInvokeSpecial) { } else if (refKind == REF_newInvokeSpecial) {
assert(name == null || name.equals("<init>")); assert(name == null || name.equals("<init>"));
MemberName ctor = (resolved != null) ? resolved MemberName ctor = (resolved != null) ? resolved
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册