8149574: Fix java/lang/invoke/MethodHandleImpl's use of Unsafe.defineAnonymousClass()
Reviewed-by: vlivanov, psandoz, sundar, mhaupt
This commit is contained in:
parent
ca98819c49
commit
8aff72ba65
@ -41,9 +41,15 @@ import sun.invoke.empty.Empty;
|
||||
import sun.invoke.util.ValueConversions;
|
||||
import sun.invoke.util.VerifyType;
|
||||
import sun.invoke.util.Wrapper;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.AnnotationVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import static java.lang.invoke.LambdaForm.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
/**
|
||||
* Trusted implementation code for MethodHandle.
|
||||
@ -1155,6 +1161,8 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
// 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 {
|
||||
private static MethodType INVOKER_MT = MethodType.methodType(Object.class, MethodHandle.class, Object[].class);
|
||||
|
||||
static
|
||||
MethodHandle bindCaller(MethodHandle mh, Class<?> hostClass) {
|
||||
// Do not use this function to inject calls into system classes.
|
||||
@ -1173,38 +1181,15 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
}
|
||||
|
||||
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);
|
||||
Class<?> invokerClass = UNSAFE.defineAnonymousClass(hostClass, INJECTED_INVOKER_TEMPLATE, null);
|
||||
assert checkInjectedInvoker(hostClass, invokerClass);
|
||||
return IMPL_LOOKUP.findStatic(invokerClass, "invoke_V", INVOKER_MT);
|
||||
} 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);
|
||||
@ -1235,62 +1220,82 @@ import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
return mh;
|
||||
}
|
||||
|
||||
private static boolean checkInjectedInvoker(Class<?> hostClass, Class<?> invokerClass) {
|
||||
assert (hostClass.getClassLoader() == invokerClass.getClassLoader()) : hostClass.getName()+" (CL)";
|
||||
try {
|
||||
assert (hostClass.getProtectionDomain() == invokerClass.getProtectionDomain()) : hostClass.getName()+" (PD)";
|
||||
} catch (SecurityException ex) {
|
||||
// Self-check was blocked by security manager. This is OK.
|
||||
}
|
||||
try {
|
||||
// Test the invoker to ensure that it really injects into the right place.
|
||||
MethodHandle invoker = IMPL_LOOKUP.findStatic(invokerClass, "invoke_V", INVOKER_MT);
|
||||
MethodHandle vamh = prepareForInvoker(MH_checkCallerClass);
|
||||
return (boolean)invoker.invoke(vamh, new Object[]{ invokerClass });
|
||||
} catch (Throwable ex) {
|
||||
throw new InternalError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static final MethodHandle MH_checkCallerClass;
|
||||
static {
|
||||
final Class<?> THIS_CLASS = BindCaller.class;
|
||||
assert(checkCallerClass(THIS_CLASS, THIS_CLASS));
|
||||
assert(checkCallerClass(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));
|
||||
MethodType.methodType(boolean.class, Class.class));
|
||||
assert((boolean) MH_checkCallerClass.invokeExact(THIS_CLASS));
|
||||
} catch (Throwable ex) {
|
||||
throw new InternalError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@CallerSensitive
|
||||
private static boolean checkCallerClass(Class<?> expected, Class<?> expected2) {
|
||||
// This method is called via MH_checkCallerClass and so it's
|
||||
// correct to ask for the immediate caller here.
|
||||
private static boolean checkCallerClass(Class<?> expected) {
|
||||
// This method is called via MH_checkCallerClass and so it's correct to ask for the immediate caller here.
|
||||
Class<?> actual = Reflection.getCallerClass();
|
||||
if (actual != expected && actual != expected2)
|
||||
throw new InternalError("found "+actual.getName()+", expected "+expected.getName()
|
||||
+(expected == expected2 ? "" : ", or else "+expected2.getName()));
|
||||
if (actual != expected)
|
||||
throw new InternalError("found " + actual.getName() + ", expected " + expected.getName());
|
||||
return true;
|
||||
}
|
||||
|
||||
private static final byte[] T_BYTES;
|
||||
static {
|
||||
final Object[] values = {null};
|
||||
AccessController.doPrivileged(new PrivilegedAction<>() {
|
||||
public Void run() {
|
||||
try {
|
||||
Class<T> tClass = T.class;
|
||||
String tName = tClass.getName();
|
||||
String tResource = tName.substring(tName.lastIndexOf('.')+1)+".class";
|
||||
try (java.io.InputStream in = tClass.getResourceAsStream(tResource)) {
|
||||
values[0] = in.readAllBytes();
|
||||
}
|
||||
} catch (java.io.IOException ex) {
|
||||
throw new InternalError(ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
T_BYTES = (byte[]) values[0];
|
||||
}
|
||||
private static final byte[] INJECTED_INVOKER_TEMPLATE = generateInvokerTemplate();
|
||||
|
||||
// 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);
|
||||
}
|
||||
/** Produces byte code for a class that is used as an injected invoker. */
|
||||
private static byte[] generateInvokerTemplate() {
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
|
||||
// private static class InjectedInvoker {
|
||||
// @Hidden
|
||||
// static Object invoke_V(MethodHandle vamh, Object[] args) throws Throwable {
|
||||
// return vamh.invokeExact(args);
|
||||
// }
|
||||
// }
|
||||
cw.visit(52, ACC_PRIVATE | ACC_SUPER, "InjectedInvoker", null, "java/lang/Object", null);
|
||||
|
||||
MethodVisitor mv = cw.visitMethod(ACC_STATIC, "invoke_V",
|
||||
"(Ljava/lang/invoke/MethodHandle;[Ljava/lang/Object;)Ljava/lang/Object;",
|
||||
null, null);
|
||||
|
||||
// Suppress invoker method in stack traces.
|
||||
AnnotationVisitor av0 = mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
|
||||
av0.visitEnd();
|
||||
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invokeExact",
|
||||
"([Ljava/lang/Object;)Ljava/lang/Object;", false);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(2, 2);
|
||||
mv.visitEnd();
|
||||
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** This subclass allows a wrapped method handle to be re-associated with an arbitrary member name. */
|
||||
private static final class WrappedMember extends DelegatingMethodHandle {
|
||||
private final MethodHandle target;
|
||||
|
Loading…
x
Reference in New Issue
Block a user