Skip to content

Commit

Permalink
8230501: Class data support for hidden classes
Browse files Browse the repository at this point in the history
Reviewed-by: jvernee, psandoz, chegar
  • Loading branch information
Mandy Chung committed Dec 1, 2020
1 parent 11dad14 commit 4356469
Show file tree
Hide file tree
Showing 17 changed files with 943 additions and 136 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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...

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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>";
Expand All @@ -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";
Expand All @@ -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
Expand Down Expand Up @@ -361,14 +369,6 @@ private Class<?> generateInnerClass() 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)
Expand All @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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);
}
}
Loading

1 comment on commit 4356469

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.