Skip to content
Permalink
Browse files
8230501: Class data support for hidden classes
Reviewed-by: jvernee, psandoz, chegar
  • Loading branch information
Mandy Chung committed Dec 1, 2020
1 parent 11dad14 commit 4356469a31f52450732d8f941f6a3b0d00657fcf
@@ -170,16 +170,7 @@ else if (info.getClass() == int[].class) {
}
}
}
if (resultType.isPrimitive()) {
// Non-reference conversions are more than just plain casts.
// By pushing the value through a funnel of the form (T x)->x,
// the boxed result can be widened as needed. See MH::asType.
MethodHandle funnel = MethodHandles.identity(resultType);
result = funnel.invoke(result);
// Now it is the wrapper type for resultType.
resultType = Wrapper.asWrapperType(resultType);
}
return resultType.cast(result);
return widenAndCast(result, resultType);
}
catch (Error e) {
// Pass through an Error, including BootstrapMethodError, any other
@@ -195,6 +186,38 @@ else if (info.getClass() == int[].class) {
}
}


/**
* If resultType is a reference type, do Class::cast on the result through
* an identity function of that type, as-type converted to return
* the corresponding reference wrapper type for resultType.
* Works like {@code MethodHandles.identity(resultType).invoke((Object)result)}.
*
* This utility function enforces type correctness of bootstrap method results.
* It is also used to enforce type correctness in other dependently-typed
* methods, such as classData.
*/
static <T> T widenAndCast(Object result, Class<T> resultType) throws Throwable {
if (!resultType.isPrimitive()) {
return resultType.cast(result);
}

Class<T> wrapperType = Wrapper.asWrapperType(resultType);
if (wrapperType.isInstance(result)) {
@SuppressWarnings("unchecked")
T wrapper = (T) result;
return wrapper;
}
// Non-reference conversions are more than just plain casts.
// By pushing the value through a funnel of the form (T x)->x,
// the boxed result can be widened as needed. See MH::asType.
// Note that this might widen byte into int, float into double, etc
MethodHandle funnel = MethodHandles.identity(resultType);
result = funnel.invoke(result);
// Now it is the wrapper type for resultType.
return wrapperType.cast(result);
}

// If we don't provide static type information for type, we'll generate runtime
// checks. Let's try not to...

@@ -413,8 +413,8 @@ public static Object explicitCast(MethodHandles.Lookup lookup, String name, Clas
MethodHandle conv = MethodHandles.explicitCastArguments(id, mt);
try {
return conv.invoke(value);
} catch (ClassCastException e) {
throw e; // specified, let CCE through
} catch (RuntimeException|Error e) {
throw e; // let specified CCE and other runtime exceptions/errors through
} catch (Throwable throwable) {
throw new InternalError(throwable); // Not specified, throw InternalError
}
@@ -34,6 +34,7 @@

import java.io.FilePermission;
import java.io.Serializable;
import java.lang.constant.ConstantDescs;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
@@ -55,7 +56,7 @@
* @see LambdaMetafactory
*/
/* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
private static final int CLASSFILE_VERSION = 52;
private static final int CLASSFILE_VERSION = 59;
private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
private static final String JAVA_LANG_OBJECT = "java/lang/Object";
private static final String NAME_CTOR = "<init>";
@@ -71,12 +72,10 @@
private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
private static final String NAME_METHOD_READ_OBJECT = "readObject";
private static final String NAME_METHOD_WRITE_OBJECT = "writeObject";
private static final String NAME_FIELD_IMPL_METHOD = "protectedImplMethod";

private static final String DESCR_CLASS = "Ljava/lang/Class;";
private static final String DESCR_STRING = "Ljava/lang/String;";
private static final String DESCR_OBJECT = "Ljava/lang/Object;";
private static final String DESCR_METHOD_HANDLE = "Ljava/lang/invoke/MethodHandle;";
private static final String DESCR_CTOR_SERIALIZED_LAMBDA
= "(" + DESCR_CLASS + DESCR_STRING + DESCR_STRING + DESCR_STRING + "I"
+ DESCR_STRING + DESCR_STRING + DESCR_STRING + DESCR_STRING + "[" + DESCR_OBJECT + ")V";
@@ -94,13 +93,22 @@

private static final boolean disableEagerInitialization;

// condy to load implMethod from class data
private static final ConstantDynamic implMethodCondy;

static {
final String dumpProxyClassesKey = "jdk.internal.lambda.dumpProxyClasses";
String dumpPath = GetPropertyAction.privilegedGetProperty(dumpProxyClassesKey);
dumper = (null == dumpPath) ? null : ProxyClassesDumper.getInstance(dumpPath);

final String disableEagerInitializationKey = "jdk.internal.lambda.disableEagerInitialization";
disableEagerInitialization = GetBooleanAction.privilegedGetProperty(disableEagerInitializationKey);

// condy to load implMethod from class data
MethodType classDataMType = MethodType.methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class);
Handle classDataBsm = new Handle(H_INVOKESTATIC, Type.getInternalName(MethodHandles.class), "classData",
classDataMType.descriptorString(), false);
implMethodCondy = new ConstantDynamic(ConstantDescs.DEFAULT_NAME, MethodHandle.class.descriptorString(), classDataBsm);
}

// See context values in AbstractValidatingLambdaMetafactory
@@ -361,14 +369,6 @@ CallSite buildCallSite() throws LambdaConversionException {
}
}

if (useImplMethodHandle) {
FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_STATIC,
NAME_FIELD_IMPL_METHOD,
DESCR_METHOD_HANDLE,
null, null);
fv.visitEnd();
}

if (isSerializable)
generateSerializationFriendlyMethods();
else if (accidentallySerializable)
@@ -394,7 +394,7 @@ public Void run() {
}
try {
// this class is linked at the indy callsite; so define a hidden nestmate
Lookup lookup = caller.defineHiddenClass(classBytes, !disableEagerInitialization, NESTMATE, STRONG);
Lookup lookup;
if (useImplMethodHandle) {
// If the target class invokes a method reference this::m which is
// resolved to a protected method inherited from a superclass in a different
@@ -403,8 +403,10 @@ public Void run() {
// This lambda proxy class has no access to the resolved method.
// So this workaround by passing the live implMethod method handle
// to the proxy class to invoke directly.
MethodHandle mh = lookup.findStaticSetter(lookup.lookupClass(), NAME_FIELD_IMPL_METHOD, MethodHandle.class);
mh.invokeExact(implMethod);
lookup = caller.defineHiddenClassWithClassData(classBytes, implMethod, !disableEagerInitialization,
NESTMATE, STRONG);
} else {
lookup = caller.defineHiddenClass(classBytes, !disableEagerInitialization, NESTMATE, STRONG);
}
return lookup.lookupClass();
} catch (IllegalAccessException e) {
@@ -554,8 +556,7 @@ void generate(MethodType methodType) {
visitInsn(DUP);
}
if (useImplMethodHandle) {
visitVarInsn(ALOAD, 0);
visitFieldInsn(GETSTATIC, lambdaClassName, NAME_FIELD_IMPL_METHOD, DESCR_METHOD_HANDLE);
visitLdcInsn(implMethodCondy);
}
for (int i = 0; i < argNames.length; i++) {
visitVarInsn(ALOAD, 0);
@@ -35,6 +35,7 @@

import static java.lang.invoke.MethodHandleNatives.Constants.*;
import static java.lang.invoke.MethodHandleStatics.TRACE_METHOD_LINKAGE;
import static java.lang.invoke.MethodHandleStatics.UNSAFE;
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;

/**
@@ -683,10 +684,13 @@ static boolean canBeCalledVirtual(MemberName symbolicRef, Class<?> definingClass

private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
/*
* A convenient method for LambdaForms to get the class data of a given class.
* LambdaForms cannot use condy via MethodHandles.classData
* Returns the class data set by the VM in the Class::classData field.
*
* This is also invoked by LambdaForms as it cannot use condy via
* MethodHandles.classData due to bootstrapping issue.
*/
static Object classData(Class<?> c) {
UNSAFE.ensureClassInitialized(c);
return JLA.classData(c);
}
}

1 comment on commit 4356469

@openjdk-notifier

This comment has been minimized.

Copy link

@openjdk-notifier openjdk-notifier bot commented on 4356469 Dec 1, 2020

Please sign in to comment.