8230501: Class data support for hidden classes

Reviewed-by: jvernee, psandoz, chegar
This commit is contained in:
Mandy Chung 2020-12-01 00:24:40 +00:00
parent 11dad148ff
commit 4356469a31
17 changed files with 946 additions and 139 deletions

View File

@ -170,16 +170,7 @@ final class BootstrapMethodInvoker {
}
}
}
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 @@ final class BootstrapMethodInvoker {
}
}
/**
* 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...

View File

@ -413,8 +413,8 @@ public final class ConstantBootstraps {
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
}

View File

@ -34,6 +34,7 @@ import sun.security.action.GetBooleanAction;
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 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
* @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 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
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,6 +93,9 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
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);
@ -101,6 +103,12 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
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 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
}
}
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 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
}
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 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
// 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 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
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);

View File

@ -35,6 +35,7 @@ import java.lang.reflect.Field;
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 @@ class MethodHandleNatives {
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);
}
}

View File

@ -25,7 +25,6 @@
package java.lang.invoke;
import jdk.internal.access.JavaLangAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.Unsafe;
import jdk.internal.misc.VM;
@ -42,6 +41,7 @@ import sun.invoke.util.Wrapper;
import sun.reflect.misc.ReflectUtil;
import sun.security.util.SecurityConstants;
import java.lang.constant.ConstantDescs;
import java.lang.invoke.LambdaForm.BasicType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
@ -101,13 +101,16 @@ public class MethodHandles {
* <a href="MethodHandleInfo.html#directmh">direct method handles</a>
* for any member that the caller has access to via bytecodes,
* including protected and private fields and methods.
* This lookup object is created by the original lookup class
* and has the {@link Lookup#ORIGINAL ORIGINAL} bit set.
* This lookup object is a <em>capability</em> which may be delegated to trusted agents.
* Do not store it in place where untrusted code can access it.
* <p>
* This method is caller sensitive, which means that it may return different
* values to different callers.
* @return a lookup object for the caller of this method, with
* {@linkplain Lookup#hasFullPrivilegeAccess() full privilege access}
* {@linkplain Lookup#ORIGINAL original} and
* {@linkplain Lookup#hasFullPrivilegeAccess() full privilege access}.
*/
@CallerSensitive
@ForceInline // to ensure Reflection.getCallerClass optimization
@ -201,13 +204,15 @@ public class MethodHandles {
* <p>
* Otherwise, if {@code M1} and {@code M2} are the same module, this method
* returns a {@code Lookup} on {@code targetClass} with
* {@linkplain Lookup#hasFullPrivilegeAccess() full privilege access} and
* {@code null} previous lookup class.
* {@linkplain Lookup#hasFullPrivilegeAccess() full privilege access}
* with {@code null} previous lookup class.
* <p>
* Otherwise, {@code M1} and {@code M2} are two different modules. This method
* returns a {@code Lookup} on {@code targetClass} that records
* the lookup class of the caller as the new previous lookup class and
* drops {@code MODULE} access from the full privilege access.
* the lookup class of the caller as the new previous lookup class with
* {@code PRIVATE} access but no {@code MODULE} access.
* <p>
* The resulting {@code Lookup} object has no {@code ORIGINAL} access.
*
* @param targetClass the target class
* @param caller the caller lookup object
@ -232,7 +237,8 @@ public class MethodHandles {
if (targetClass.isArray())
throw new IllegalArgumentException(targetClass + " is an array class");
// Ensure that we can reason accurately about private and module access.
if (!caller.hasFullPrivilegeAccess())
int requireAccess = Lookup.PRIVATE|Lookup.MODULE;
if ((caller.lookupModes() & requireAccess) != requireAccess)
throw new IllegalAccessException("caller does not have PRIVATE and MODULE lookup mode");
// previous lookup class is never set if it has MODULE access
@ -242,7 +248,7 @@ public class MethodHandles {
Module callerModule = callerClass.getModule(); // M1
Module targetModule = targetClass.getModule(); // M2
Class<?> newPreviousClass = null;
int newModes = Lookup.FULL_POWER_MODES;
int newModes = Lookup.FULL_POWER_MODES & ~Lookup.ORIGINAL;
if (targetModule != callerModule) {
if (!callerModule.canRead(targetModule))
@ -270,53 +276,154 @@ public class MethodHandles {
/**
* Returns the <em>class data</em> associated with the lookup class
* of the specified {@code Lookup} object, or {@code null}.
* of the given {@code caller} lookup object, or {@code null}.
*
* <p> Classes can be created with class data by calling
* {@link Lookup#defineHiddenClassWithClassData(byte[], Object, Lookup.ClassOption...)
* <p> A hidden class with class data can be created by calling
* {@link Lookup#defineHiddenClassWithClassData(byte[], Object, boolean, Lookup.ClassOption...)
* Lookup::defineHiddenClassWithClassData}.
* A hidden class with a class data behaves as if the hidden class
* has a private static final unnamed field pre-initialized with
* the class data and this method is equivalent as if calling
* {@link ConstantBootstraps#getStaticFinal(Lookup, String, Class)} to
* obtain the value of such field corresponding to the class data.
* This method will cause the static class initializer of the lookup
* class of the given {@code caller} lookup object be executed if
* it has not been initialized.
*
* <p> A hidden class created by {@link Lookup#defineHiddenClass(byte[], boolean, Lookup.ClassOption...)
* Lookup::defineHiddenClass} and non-hidden classes have no class data.
* {@code null} is returned if this method is called on the lookup object
* on these classes.
*
* <p> The {@linkplain Lookup#lookupModes() lookup modes} for this lookup
* must have {@link Lookup#ORIGINAL ORIGINAL} access in order to retrieve
* the class data.
* must have {@linkplain Lookup#ORIGINAL original access}
* in order to retrieve the class data.
*
* @apiNote
* This method can be called as a bootstrap method for a dynamically computed
* constant. A framework can create a hidden class with class data, for
* example that can be {@code Class} or {@code MethodHandle} object.
* The class data is accessible only to the lookup object
* created by the original caller but inaccessible to other members
* in the same nest. If a framework passes security sensitive objects
* to a hidden class via class data, it is recommended to load the value
* of class data as a dynamically computed constant instead of storing
* the class data in private static field(s) which are accessible to
* other nestmates.
*
* @param <T> the type to cast the class data object to
* @param caller the lookup context describing the class performing the
* operation (normally stacked by the JVM)
* @param name must be {@link ConstantDescs#DEFAULT_NAME}
* ({@code "_"})
* @param type the type of the class data
* @return the value of the class data if present in the lookup class;
* otherwise {@code null}
* @throws IllegalArgumentException if name is not {@code "_"}
* @throws IllegalAccessException if the lookup context does not have
* {@linkplain Lookup#ORIGINAL original} access
* @throws ClassCastException if the class data cannot be converted to
* the given {@code type}
* @throws NullPointerException if {@code caller} or {@code type} argument
* is {@code null}
* @see Lookup#defineHiddenClassWithClassData(byte[], Object, boolean, Lookup.ClassOption...)
* @see MethodHandles#classDataAt(Lookup, String, Class, int)
* @since 16
* @jvms 5.5 Initialization
*/
public static <T> T classData(Lookup caller, String name, Class<T> type) throws IllegalAccessException {
Objects.requireNonNull(caller);
Objects.requireNonNull(type);
if (!ConstantDescs.DEFAULT_NAME.equals(name)) {
throw new IllegalArgumentException("name must be \"_\": " + name);
}
if ((caller.lookupModes() & Lookup.ORIGINAL) != Lookup.ORIGINAL) {
throw new IllegalAccessException(caller + " does not have ORIGINAL access");
}
Object classdata = MethodHandleNatives.classData(caller.lookupClass());
if (classdata == null) return null;
try {
return BootstrapMethodInvoker.widenAndCast(classdata, type);
} catch (RuntimeException|Error e) {
throw e; // let CCE and other runtime exceptions through
} catch (Throwable e) {
throw new InternalError(e);
}
}
/**
* Returns the element at the specified index in the
* {@linkplain #classData(Lookup, String, Class) class data},
* if the class data associated with the lookup class
* of the given {@code caller} lookup object is a {@code List}.
* If the class data is not present in this lookup class, this method
* returns {@code null}.
*
* <p> A hidden class with class data can be created by calling
* {@link Lookup#defineHiddenClassWithClassData(byte[], Object, boolean, Lookup.ClassOption...)
* Lookup::defineHiddenClassWithClassData}.
* This method will cause the static class initializer of the lookup
* class of the given {@code caller} lookup object be executed if
* it has not been initialized.
*
* <p> A hidden class created by {@link Lookup#defineHiddenClass(byte[], boolean, Lookup.ClassOption...)
* Lookup::defineHiddenClass} and non-hidden classes have no class data.
* {@code null} is returned if this method is called on the lookup object
* on these classes.
*
* <p> The {@linkplain Lookup#lookupModes() lookup modes} for this lookup
* must have {@linkplain Lookup#ORIGINAL original access}
* in order to retrieve the class data.
*
* @apiNote
* This method can be called as a bootstrap method for a dynamically computed
* constant. A framework can create a hidden class with class data, for
* example that can be {@code List.of(o1, o2, o3....)} containing more than
* one live object. The class data is accessible only to the lookup object
* one object and use this method to load one element at a specific index.
* The class data is accessible only to the lookup object
* created by the original caller but inaccessible to other members
* in the same nest. If a framework passes security sensitive live objects
* in the same nest. If a framework passes security sensitive objects
* to a hidden class via class data, it is recommended to load the value
* of class data as a dynamically computed constant instead of storing
* the live objects in private fields which are accessible to other
* the class data in private static field(s) which are accessible to other
* nestmates.
*
* @param <T> the type to cast the class data object to
* @param <T> the type to cast the result object to
* @param caller the lookup context describing the class performing the
* operation (normally stacked by the JVM)
* @param name ignored
* @param type the type of the class data
* @return the value of the class data if present in the lookup class;
* otherwise {@code null}
* @param name must be {@link java.lang.constant.ConstantDescs#DEFAULT_NAME}
* ({@code "_"})
* @param type the type of the element at the given index in the class data
* @param index index of the element in the class data
* @return the element at the given index in the class data
* if the class data is present; otherwise {@code null}
* @throws IllegalArgumentException if name is not {@code "_"}
* @throws IllegalAccessException if the lookup context does not have
* original caller access
* @throws ClassCastException if the class data cannot be converted to
* the specified {@code type}
* @see Lookup#defineHiddenClassWithClassData(byte[], Object, Lookup.ClassOption...)
* @since 15
* {@linkplain Lookup#ORIGINAL original} access
* @throws ClassCastException if the class data cannot be converted to {@code List}
* or the element at the specified index cannot be converted to the given type
* @throws IndexOutOfBoundsException if the index is out of range
* @throws NullPointerException if {@code caller} or {@code type} argument is
* {@code null}; or if unboxing operation fails because
* the element at the given index is {@code null}
*
* @since 16
* @see #classData(Lookup, String, Class)
* @see Lookup#defineHiddenClassWithClassData(byte[], Object, boolean, Lookup.ClassOption...)
*/
static <T> T classData(Lookup caller, String name, Class<T> type) throws IllegalAccessException {
if (!caller.hasFullPrivilegeAccess()) {
throw new IllegalAccessException(caller + " does not have full privilege access");
public static <T> T classDataAt(Lookup caller, String name, Class<T> type, int index)
throws IllegalAccessException
{
@SuppressWarnings("unchecked")
List<Object> classdata = (List<Object>)classData(caller, name, List.class);
if (classdata == null) return null;
try {
Object element = classdata.get(index);
return BootstrapMethodInvoker.widenAndCast(element, type);
} catch (RuntimeException|Error e) {
throw e; // let specified exceptions and other runtime exceptions/errors through
} catch (Throwable e) {
throw new InternalError(e);
}
Object classData = MethodHandleNatives.classData(caller.lookupClass);
return type.cast(classData);
}
/**
@ -636,11 +743,17 @@ public class MethodHandles {
* <p style="font-size:smaller;">
* Private and module access are independently determined modes; a lookup may have
* either or both or neither. A lookup which possesses both access modes is said to
* possess {@linkplain #hasFullPrivilegeAccess() full privilege access}. Such a lookup has
* the following additional capability:
* possess {@linkplain #hasFullPrivilegeAccess() full privilege access}.
* <p style="font-size:smaller;">
* A lookup with <em>original access</em> ensures that this lookup is created by
* the original lookup class and the bootstrap method invoked by the VM.
* Such a lookup with original access also has private and module access
* which has the following additional capability:
* <ul style="font-size:smaller;">
* <li>create method handles which invoke <a href="MethodHandles.Lookup.html#callsens">caller sensitive</a> methods,
* such as {@code Class.forName}
* <li>obtain the {@linkplain MethodHandles#classData(Lookup, String, Class)
* class data} associated with the lookup class</li>
* </ul>
* <p style="font-size:smaller;">
* Each of these permissions is a consequence of the fact that a lookup object
@ -811,6 +924,7 @@ public class MethodHandles {
* <thead>
* <tr>
* <th scope="col">Lookup object</th>
* <th style="text-align:center">original</th>
* <th style="text-align:center">protected</th>
* <th style="text-align:center">private</th>
* <th style="text-align:center">package</th>
@ -821,6 +935,7 @@ public class MethodHandles {
* <tbody>
* <tr>
* <th scope="row" style="text-align:left">{@code CL = MethodHandles.lookup()} in {@code C}</th>
* <td style="text-align:center">ORI</td>
* <td style="text-align:center">PRO</td>
* <td style="text-align:center">PRI</td>
* <td style="text-align:center">PAC</td>
@ -831,6 +946,7 @@ public class MethodHandles {
* <th scope="row" style="text-align:left">{@code CL.in(C1)} same package</th>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">PAC</td>
* <td style="text-align:center">MOD</td>
* <td style="text-align:center">1R</td>
@ -840,6 +956,7 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">MOD</td>
* <td style="text-align:center">1R</td>
* </tr>
@ -849,6 +966,7 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">2R</td>
* </tr>
* <tr>
@ -857,10 +975,12 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">2R</td>
* </tr>
* <tr>
* <td>{@code PRI1 = privateLookupIn(C1,CL)}</td>
* <td></td>
* <td style="text-align:center">PRO</td>
* <td style="text-align:center">PRI</td>
* <td style="text-align:center">PAC</td>
@ -869,6 +989,7 @@ public class MethodHandles {
* </tr>
* <tr>
* <td>{@code PRI1a = privateLookupIn(C,PRI1)}</td>
* <td></td>
* <td style="text-align:center">PRO</td>
* <td style="text-align:center">PRI</td>
* <td style="text-align:center">PAC</td>
@ -879,6 +1000,7 @@ public class MethodHandles {
* <td>{@code PRI1.in(C1)} same package</td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">PAC</td>
* <td style="text-align:center">MOD</td>
* <td style="text-align:center">1R</td>
@ -888,6 +1010,7 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">MOD</td>
* <td style="text-align:center">1R</td>
* </tr>
@ -897,11 +1020,13 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">2R</td>
* </tr>
* <tr>
* <td>{@code PRI1.dropLookupMode(PROTECTED)}</td>
* <td></td>
* <td></td>
* <td style="text-align:center">PRI</td>
* <td style="text-align:center">PAC</td>
* <td style="text-align:center">MOD</td>
@ -911,6 +1036,7 @@ public class MethodHandles {
* <td>{@code PRI1.dropLookupMode(PRIVATE)}</td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">PAC</td>
* <td style="text-align:center">MOD</td>
* <td style="text-align:center">1R</td>
@ -920,6 +1046,7 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">MOD</td>
* <td style="text-align:center">1R</td>
* </tr>
@ -929,6 +1056,7 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">1R</td>
* </tr>
* <tr>
@ -937,9 +1065,11 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">none</td>
* <tr>
* <td>{@code PRI2 = privateLookupIn(D,CL)}</td>
* <td></td>
* <td style="text-align:center">PRO</td>
* <td style="text-align:center">PRI</td>
* <td style="text-align:center">PAC</td>
@ -948,6 +1078,7 @@ public class MethodHandles {
* </tr>
* <tr>
* <td>{@code privateLookupIn(D,PRI1)}</td>
* <td></td>
* <td style="text-align:center">PRO</td>
* <td style="text-align:center">PRI</td>
* <td style="text-align:center">PAC</td>
@ -960,12 +1091,14 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">IAE</td>
* </tr>
* <tr>
* <td>{@code PRI2.in(D2)} same package</td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">PAC</td>
* <td></td>
* <td style="text-align:center">2R</td>
@ -976,6 +1109,7 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">2R</td>
* </tr>
* <tr>
@ -984,6 +1118,7 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">2R</td>
* </tr>
* <tr>
@ -992,11 +1127,13 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">none</td>
* </tr>
* <tr>
* <td>{@code PRI2.dropLookupMode(PROTECTED)}</td>
* <td></td>
* <td></td>
* <td style="text-align:center">PRI</td>
* <td style="text-align:center">PAC</td>
* <td></td>
@ -1006,6 +1143,7 @@ public class MethodHandles {
* <td>{@code PRI2.dropLookupMode(PRIVATE)}</td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">PAC</td>
* <td></td>
* <td style="text-align:center">2R</td>
@ -1016,6 +1154,7 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">2R</td>
* </tr>
* <tr>
@ -1024,6 +1163,7 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">2R</td>
* </tr>
* <tr>
@ -1032,11 +1172,13 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">none</td>
* </tr>
* <tr>
* <td>{@code CL.dropLookupMode(PROTECTED)}</td>
* <td></td>
* <td></td>
* <td style="text-align:center">PRI</td>
* <td style="text-align:center">PAC</td>
* <td style="text-align:center">MOD</td>
@ -1046,6 +1188,7 @@ public class MethodHandles {
* <td>{@code CL.dropLookupMode(PRIVATE)}</td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">PAC</td>
* <td style="text-align:center">MOD</td>
* <td style="text-align:center">1R</td>
@ -1055,6 +1198,7 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">MOD</td>
* <td style="text-align:center">1R</td>
* </tr>
@ -1064,6 +1208,7 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">1R</td>
* </tr>
* <tr>
@ -1072,6 +1217,7 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">none</td>
* </tr>
* <tr>
@ -1080,6 +1226,7 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">U</td>
* </tr>
* <tr>
@ -1088,6 +1235,7 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">U</td>
* </tr>
* <tr>
@ -1096,6 +1244,7 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">U</td>
* </tr>
* <tr>
@ -1104,6 +1253,7 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">none</td>
* </tr>
* <tr>
@ -1112,6 +1262,7 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">IAE</td>
* </tr>
* <tr>
@ -1120,6 +1271,7 @@ public class MethodHandles {
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* <td style="text-align:center">none</td>
* </tr>
* </tbody>
@ -1132,7 +1284,8 @@ public class MethodHandles {
* but {@code D} and {@code D2} are in module {@code M2}, and {@code E}
* is in module {@code M3}. {@code X} stands for class which is inaccessible
* to the lookup. {@code ANY} stands for any of the example lookups.</li>
* <li>{@code PRO} indicates {@link #PROTECTED} bit set,
* <li>{@code ORI} indicates {@link #ORIGINAL} bit set,
* {@code PRO} indicates {@link #PROTECTED} bit set,
* {@code PRI} indicates {@link #PRIVATE} bit set,
* {@code PAC} indicates {@link #PACKAGE} bit set,
* {@code MOD} indicates {@link #MODULE} bit set,
@ -1217,7 +1370,10 @@ public class MethodHandles {
* <p>
* If a security manager is present and the current lookup object does not have
* {@linkplain #hasFullPrivilegeAccess() full privilege access}, then
* {@link #defineClass(byte[]) defineClass}
* {@link #defineClass(byte[]) defineClass},
* {@link #defineHiddenClass(byte[], boolean, ClassOption...) defineHiddenClass},
* {@link #defineHiddenClassWithClassData(byte[], Object, boolean, ClassOption...)
* defineHiddenClassWithClassData}
* calls {@link SecurityManager#checkPermission smgr.checkPermission}
* with {@code RuntimePermission("defineClass")}.
*
@ -1240,7 +1396,7 @@ public class MethodHandles {
* In cases where the lookup object is
* {@link MethodHandles#publicLookup() publicLookup()},
* or some other lookup object without the
* {@linkplain #hasFullPrivilegeAccess() full privilege access},
* {@linkplain #ORIGINAL original access},
* the lookup class is disregarded.
* In such cases, no caller-sensitive method handle can be created,
* access is forbidden, and the lookup fails with an
@ -1351,16 +1507,32 @@ public class MethodHandles {
*/
public static final int UNCONDITIONAL = PACKAGE << 2;
private static final int ALL_MODES = (PUBLIC | PRIVATE | PROTECTED | PACKAGE | MODULE | UNCONDITIONAL);
private static final int FULL_POWER_MODES = (ALL_MODES & ~UNCONDITIONAL);
/** A single-bit mask representing {@code original} access
* which may contribute to the result of {@link #lookupModes lookupModes}.
* The value is {@code 0x40}, which does not correspond meaningfully to
* any particular {@linkplain java.lang.reflect.Modifier modifier bit}.
*
* <p>
* If this lookup mode is set, the {@code Lookup} object must be
* created by the original lookup class by calling
* {@link MethodHandles#lookup()} method or by a bootstrap method
* invoked by the VM. The {@code Lookup} object with this lookup
* mode has {@linkplain #hasFullPrivilegeAccess() full privilege access}.
*
* @since 16
*/
public static final int ORIGINAL = PACKAGE << 3;
private static final int ALL_MODES = (PUBLIC | PRIVATE | PROTECTED | PACKAGE | MODULE | UNCONDITIONAL | ORIGINAL);
private static final int FULL_POWER_MODES = (ALL_MODES & ~UNCONDITIONAL); // with original access
private static final int TRUSTED = -1;
/*
* Adjust PUBLIC => PUBLIC|MODULE|UNCONDITIONAL
* Adjust PUBLIC => PUBLIC|MODULE|ORIGINAL|UNCONDITIONAL
* Adjust 0 => PACKAGE
*/
private static int fixmods(int mods) {
mods &= (ALL_MODES - PACKAGE - MODULE - UNCONDITIONAL);
mods &= (ALL_MODES - PACKAGE - MODULE - ORIGINAL - UNCONDITIONAL);
if (Modifier.isPublic(mods))
mods |= UNCONDITIONAL;
return (mods != 0) ? mods : PACKAGE;
@ -1416,7 +1588,8 @@ public class MethodHandles {
* {@linkplain #PROTECTED PROTECTED (0x04)},
* {@linkplain #PACKAGE PACKAGE (0x08)},
* {@linkplain #MODULE MODULE (0x10)},
* and {@linkplain #UNCONDITIONAL UNCONDITIONAL (0x20)}.
* {@linkplain #UNCONDITIONAL UNCONDITIONAL (0x20)},
* and {@linkplain #ORIGINAL ORIGINAL (0x40)}.
* <p>
* A freshly-created lookup object
* on the {@linkplain java.lang.invoke.MethodHandles#lookup() caller's class} has
@ -1473,6 +1646,8 @@ public class MethodHandles {
* However, the resulting {@code Lookup} object is guaranteed
* to have no more access capabilities than the original.
* In particular, access capabilities can be lost as follows:<ul>
* <li>If the new lookup class is different from the old lookup class,
* i.e. {@link #ORIGINAL ORIGINAL} access is lost.
* <li>If the new lookup class is in a different module from the old one,
* i.e. {@link #MODULE MODULE} access is lost.
* <li>If the new lookup class is in a different package
@ -1528,7 +1703,7 @@ public class MethodHandles {
return new Lookup(requestedLookupClass, null, FULL_POWER_MODES);
if (requestedLookupClass == this.lookupClass)
return this; // keep same capabilities
int newModes = (allowedModes & FULL_POWER_MODES);
int newModes = (allowedModes & FULL_POWER_MODES) & ~ORIGINAL;
Module fromModule = this.lookupClass.getModule();
Module targetModule = requestedLookupClass.getModule();
Class<?> plc = this.previousLookupClass();
@ -1569,7 +1744,8 @@ public class MethodHandles {
* finds members, but with a lookup mode that has lost the given lookup mode.
* The lookup mode to drop is one of {@link #PUBLIC PUBLIC}, {@link #MODULE
* MODULE}, {@link #PACKAGE PACKAGE}, {@link #PROTECTED PROTECTED},
* {@link #PRIVATE PRIVATE}, or {@link #UNCONDITIONAL UNCONDITIONAL}.
* {@link #PRIVATE PRIVATE}, {@link #ORIGINAL ORIGINAL}, or
* {@link #UNCONDITIONAL UNCONDITIONAL}.
*
* <p> If this lookup is a {@linkplain MethodHandles#publicLookup() public lookup},
* this lookup has {@code UNCONDITIONAL} mode set and it has no other mode set.
@ -1578,8 +1754,9 @@ public class MethodHandles {
*
* <p> If this lookup is not a public lookup, then the following applies
* regardless of its {@linkplain #lookupModes() lookup modes}.
* {@link #PROTECTED PROTECTED} is always dropped and so the resulting lookup
* mode will never have this access capability. When dropping {@code PACKAGE}
* {@link #PROTECTED PROTECTED} and {@link #ORIGINAL ORIGINAL} are always
* dropped and so the resulting lookup mode will never have these access
* capabilities. When dropping {@code PACKAGE}
* then the resulting lookup will not have {@code PACKAGE} or {@code PRIVATE}
* access. When dropping {@code MODULE} then the resulting lookup will not
* have {@code MODULE}, {@code PACKAGE}, or {@code PRIVATE} access.
@ -1600,19 +1777,21 @@ public class MethodHandles {
* @param modeToDrop the lookup mode to drop
* @return a lookup object which lacks the indicated mode, or the same object if there is no change
* @throws IllegalArgumentException if {@code modeToDrop} is not one of {@code PUBLIC},
* {@code MODULE}, {@code PACKAGE}, {@code PROTECTED}, {@code PRIVATE} or {@code UNCONDITIONAL}
* {@code MODULE}, {@code PACKAGE}, {@code PROTECTED}, {@code PRIVATE}, {@code ORIGINAL}
* or {@code UNCONDITIONAL}
* @see MethodHandles#privateLookupIn
* @since 9
*/
public Lookup dropLookupMode(int modeToDrop) {
int oldModes = lookupModes();
int newModes = oldModes & ~(modeToDrop | PROTECTED);
int newModes = oldModes & ~(modeToDrop | PROTECTED | ORIGINAL);
switch (modeToDrop) {
case PUBLIC: newModes &= ~(FULL_POWER_MODES); break;
case MODULE: newModes &= ~(PACKAGE | PRIVATE); break;
case PACKAGE: newModes &= ~(PRIVATE); break;
case PROTECTED:
case PRIVATE:
case ORIGINAL:
case UNCONDITIONAL: break;
default: throw new IllegalArgumentException(modeToDrop + " is not a valid mode to drop");
}
@ -1905,7 +2084,9 @@ public class MethodHandles {
* <cite>The Java Virtual Machine Specification</cite>.
* @param initialize if {@code true} the class will be initialized.
* @param options {@linkplain ClassOption class options}
* @return the {@code Lookup} object on the hidden class
* @return the {@code Lookup} object on the hidden class,
* with {@linkplain #ORIGINAL original} and
* {@linkplain Lookup#hasFullPrivilegeAccess() full privilege} access
*
* @throws IllegalAccessException if this {@code Lookup} does not have
* {@linkplain #hasFullPrivilegeAccess() full privilege} access
@ -1958,19 +2139,37 @@ public class MethodHandles {
* returning a {@code Lookup} on the newly created class or interface.
*
* <p> This method is equivalent to calling
* {@link #defineHiddenClass(byte[], boolean, ClassOption...) defineHiddenClass(bytes, true, options)}
* as if the hidden class has a private static final unnamed field whose value
* is initialized to {@code classData} right before the class initializer is
* executed. The newly created class is linked and initialized by the Java
* Virtual Machine.
* {@link #defineHiddenClass(byte[], boolean, ClassOption...) defineHiddenClass(bytes, initialize, options)}
* as if the hidden class is injected with a private static final <i>unnamed</i>
* field which is initialized with the given {@code classData} at
* the first instruction of the class initializer.
* The newly created class is linked by the Java Virtual Machine.
*
* <p> The {@link MethodHandles#classData(Lookup, String, Class) MethodHandles::classData}
* method can be used to retrieve the {@code classData}.
* and {@link MethodHandles#classDataAt(Lookup, String, Class, int) MethodHandles::classDataAt}
* methods can be used to retrieve the {@code classData}.
*
* @apiNote
* A framework can create a hidden class with class data with one or more
* objects and load the class data as dynamically-computed constant(s)
* via a bootstrap method. {@link MethodHandles#classData(Lookup, String, Class)
* Class data} is accessible only to the lookup object created by the newly
* defined hidden class but inaccessible to other members in the same nest
* (unlike private static fields that are accessible to nestmates).
* Care should be taken w.r.t. mutability for example when passing
* an array or other mutable structure through the class data.
* Changing any value stored in the class data at runtime may lead to
* unpredictable behavior.
* If the class data is a {@code List}, it is good practice to make it
* unmodifiable for example via {@link List#of List::of}.
*
* @param bytes the class bytes
* @param classData pre-initialized class data
* @param initialize if {@code true} the class will be initialized.
* @param options {@linkplain ClassOption class options}
* @return the {@code Lookup} object on the hidden class
* @return the {@code Lookup} object on the hidden class,
* with {@linkplain #ORIGINAL original} and
* {@linkplain Lookup#hasFullPrivilegeAccess() full privilege} access
*
* @throws IllegalAccessException if this {@code Lookup} does not have
* {@linkplain #hasFullPrivilegeAccess() full privilege} access
@ -1990,11 +2189,23 @@ public class MethodHandles {
* @throws LinkageError if the newly created class cannot be linked for any other reason
* @throws NullPointerException if any parameter is {@code null}
*
* @since 15
* @since 16
* @see Lookup#defineHiddenClass(byte[], boolean, ClassOption...)
* @see Class#isHidden()
* @see MethodHandles#classData(Lookup, String, Class)
* @see MethodHandles#classDataAt(Lookup, String, Class, int)
* @jvms 4.2.1 Binary Class and Interface Names
* @jvms 4.2.2 Unqualified Names
* @jvms 4.7.28 The {@code NestHost} Attribute
* @jvms 4.7.29 The {@code NestMembers} Attribute
* @jvms 5.4.3.1 Class and Interface Resolution
* @jvms 5.4.4 Access Control
* @jvms 5.3.5 Deriving a {@code Class} from a {@code class} File Representation
* @jvms 5.4 Linking
* @jvms 5.5 Initialization
* @jls 12.7 Unloading of Classes and Interface
*/
/* package-private */ Lookup defineHiddenClassWithClassData(byte[] bytes, Object classData, ClassOption... options)
public Lookup defineHiddenClassWithClassData(byte[] bytes, Object classData, boolean initialize, ClassOption... options)
throws IllegalAccessException
{
Objects.requireNonNull(bytes);
@ -2007,7 +2218,7 @@ public class MethodHandles {
}
return makeHiddenClassDefiner(bytes.clone(), Set.of(options), false)
.defineClassAsLookup(true, classData);
.defineClassAsLookup(initialize, classData);
}
static class ClassFile {
@ -2238,8 +2449,6 @@ public class MethodHandles {
}
Lookup defineClassAsLookup(boolean initialize, Object classData) {
// initialize must be true if classData is non-null
assert classData == null || initialize == true;
Class<?> c = defineClass(initialize, classData);
return new Lookup(c, null, FULL_POWER_MODES);
}
@ -2295,7 +2504,8 @@ public class MethodHandles {
* <li>If public and package access are allowed, the suffix is "/package".
* <li>If public, package, and private access are allowed, the suffix is "/private".
* </ul>
* If none of the above cases apply, it is the case that full access
* If none of the above cases apply, it is the case that
* {@linkplain #hasFullPrivilegeAccess() full privilege access}
* (public, module, package, private, and protected) is allowed.
* In this case, no suffix is added.
* This is true only of an object obtained originally from
@ -2329,12 +2539,13 @@ public class MethodHandles {
case PUBLIC|PACKAGE:
case PUBLIC|MODULE|PACKAGE:
return cname + "/package";
case FULL_POWER_MODES & (~PROTECTED):
case FULL_POWER_MODES & ~(PROTECTED|MODULE):
return cname + "/private";
case PUBLIC|PACKAGE|PRIVATE:
case PUBLIC|MODULE|PACKAGE|PRIVATE:
return cname + "/private";
case PUBLIC|PACKAGE|PRIVATE|PROTECTED:
case PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED:
case FULL_POWER_MODES:
case FULL_POWER_MODES & (~MODULE):
return cname;
return cname;
case TRUSTED:
return "/trusted"; // internal only; not exported
default: // Should not happen, but it's a bitfield...
@ -3415,7 +3626,7 @@ return mh1;
}
if (allowedModes != TRUSTED && member.isCallerSensitive()) {
Class<?> callerClass = target.internalCallerClass();
if (!hasFullPrivilegeAccess() || callerClass != lookupClass())
if ((lookupModes() & ORIGINAL) == 0 || callerClass != lookupClass())
throw new IllegalArgumentException("method handle is caller sensitive: "+callerClass);
}
// Produce the handle to the results.
@ -3480,11 +3691,11 @@ return mh1;
/**
* Find my trustable caller class if m is a caller sensitive method.
* If this lookup object has full privilege access, then the caller class is the lookupClass.
* If this lookup object has original full privilege access, then the caller class is the lookupClass.
* Otherwise, if m is caller-sensitive, throw IllegalAccessException.
*/
Lookup findBoundCallerLookup(MemberName m) throws IllegalAccessException {
if (MethodHandleNatives.isCallerSensitive(m) && !hasFullPrivilegeAccess()) {
if (MethodHandleNatives.isCallerSensitive(m) && (lookupModes() & ORIGINAL) == 0) {
// Only lookups with full privilege access are allowed to resolve caller-sensitive methods
throw new IllegalAccessException("Attempt to lookup caller-sensitive method using restricted lookup object");
}
@ -3510,7 +3721,8 @@ return mh1;
* Returns {@code true} if this lookup has <em>full privilege access</em>,
* i.e. {@code PRIVATE} and {@code MODULE} access.
* A {@code Lookup} object must have full privilege access in order to
* access all members that are allowed to the {@linkplain #lookupClass() lookup class}.
* access all members that are allowed to the
* {@linkplain #lookupClass() lookup class}.
*
* @return {@code true} if this lookup has full privilege access.
* @since 14
@ -3531,14 +3743,14 @@ return mh1;
if (smgr == null) return;
// Step 1:
boolean fullPowerLookup = hasFullPrivilegeAccess();
if (!fullPowerLookup ||
boolean fullPrivilegeLookup = hasFullPrivilegeAccess();
if (!fullPrivilegeLookup ||
!VerifyAccess.classLoaderIsAncestor(lookupClass, refc)) {
ReflectUtil.checkPackageAccess(refc);
}
// Step 2b:
if (!fullPowerLookup) {
if (!fullPrivilegeLookup) {
smgr.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
}
}
@ -3546,7 +3758,11 @@ return mh1;
/**
* Perform steps 1, 2a and 3 <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 full privilege access, then the caller class is the lookupClass.
* If this lookup object has full privilege access except original access,
* then the caller class is the lookupClass.
*
* Lookup object created by {@link MethodHandles#privateLookupIn(Class, Lookup)}
* from the same module skips the security permission check.
*/
void checkSecurityManager(Class<?> refc, MemberName m) {
Objects.requireNonNull(refc);
@ -3558,21 +3774,21 @@ return mh1;
if (smgr == null) return;
// Step 1:
boolean fullPowerLookup = hasFullPrivilegeAccess();
if (!fullPowerLookup ||
boolean fullPrivilegeLookup = hasFullPrivilegeAccess();
if (!fullPrivilegeLookup ||
!VerifyAccess.classLoaderIsAncestor(lookupClass, refc)) {
ReflectUtil.checkPackageAccess(refc);
}
// Step 2a:
if (m.isPublic()) return;
if (!fullPowerLookup) {
if (!fullPrivilegeLookup) {
smgr.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);
}
// Step 3:
Class<?> defc = m.getDeclaringClass();
if (!fullPowerLookup && defc != refc) {
if (!fullPrivilegeLookup && defc != refc) {
ReflectUtil.checkPackageAccess(defc);
}
}
@ -3787,9 +4003,11 @@ return mh1;
// boundCaller must have full privilege access.
// It should have been checked by findBoundCallerLookup. Safe to check this again.
if (!boundCaller.hasFullPrivilegeAccess())
if ((boundCaller.lookupModes() & ORIGINAL) == 0)
throw new IllegalAccessException("Attempt to lookup caller-sensitive method using restricted lookup object");
assert boundCaller.hasFullPrivilegeAccess();
MethodHandle cbmh = MethodHandleImpl.bindCaller(mh, boundCaller.lookupClass);
// Note: caller will apply varargs after this step happens.
return cbmh;

View File

@ -1345,6 +1345,7 @@ public final class Unsafe {
* @param data bytes of a class file
* @param cpPatches where non-null entries exist, they replace corresponding CP entries in data
*/
@Deprecated(since = "15", forRemoval = true)
public Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[] cpPatches) {
if (hostClass == null || data == null) {
throw new NullPointerException();

View File

@ -39,6 +39,7 @@ public class VerifyAccess {
private VerifyAccess() { } // cannot instantiate
private static final int UNCONDITIONAL_ALLOWED = java.lang.invoke.MethodHandles.Lookup.UNCONDITIONAL;
private static final int ORIGINAL_ALLOWED = java.lang.invoke.MethodHandles.Lookup.ORIGINAL;
private static final int MODULE_ALLOWED = java.lang.invoke.MethodHandles.Lookup.MODULE;
private static final int PACKAGE_ONLY = 0;
private static final int PACKAGE_ALLOWED = java.lang.invoke.MethodHandles.Lookup.PACKAGE;
@ -99,7 +100,7 @@ public class VerifyAccess {
Class<?> prevLookupClass,
int allowedModes) {
if (allowedModes == 0) return false;
assert((allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED)) == 0);
assert((allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED|ORIGINAL_ALLOWED)) == 0);
// The symbolic reference class (refc) must always be fully verified.
if (!isClassAccessible(refc, lookupClass, prevLookupClass, allowedModes)) {
return false;
@ -189,7 +190,7 @@ public class VerifyAccess {
Class<?> prevLookupClass,
int allowedModes) {
if (allowedModes == 0) return false;
assert((allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED)) == 0);
assert((allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED|ORIGINAL_ALLOWED)) == 0);
if ((allowedModes & PACKAGE_ALLOWED) != 0 &&
isSamePackage(lookupClass, refc))

View File

@ -867,15 +867,17 @@ public final class Unsafe {
* <li>InterfaceMethodRef: (NYI) a method handle to invoke on that call site's arguments
* </ul>
*
* @deprecated Use the {@link java.lang.invoke.MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOption...)}
* method.
* @deprecated Use {@link java.lang.invoke.MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOption...)}
* or {@link java.lang.invoke.MethodHandles.Lookup#defineHiddenClassWithClassData(byte[], Object, boolean, MethodHandles.Lookup.ClassOption...)}
* instead.
*
* @param hostClass context for linkage, access control, protection domain, and class loader
* @param data bytes of a class file
* @param cpPatches where non-null entries exist, they replace corresponding CP entries in data
*/
@ForceInline
@Deprecated(since = "15", forRemoval = false)
@Deprecated(since = "15", forRemoval = true)
@SuppressWarnings("removal")
public Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[] cpPatches) {
return theInternalUnsafe.defineAnonymousClass(hostClass, data, cpPatches);
}

View File

@ -148,7 +148,8 @@ public class AccessControlTest {
|| lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE))
suffix = "/private";
else if (lookupModes == (PUBLIC|PACKAGE|PRIVATE|PROTECTED)
|| lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED))
|| lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED)
|| lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED|ORIGINAL))
suffix = "";
else
suffix = "/#"+Integer.toHexString(lookupModes);
@ -160,9 +161,11 @@ public class AccessControlTest {
* Creates a lookup on the specified new lookup class.
* [A1] The resulting object will report the specified
* class as its own {@link #lookupClass lookupClass}.
* [A2] However, the resulting {@code Lookup} object is guaranteed
* [A1-a] However, the resulting {@code Lookup} object is guaranteed
* to have no more access capabilities than the original.
* In particular, access capabilities can be lost as follows:<ul>
* [A2] If the new lookup class is not the same as the old lookup class,
* then {@link #ORIGINAL ORIGINAL} access is lost.
* [A3] If the new lookup class is in a different module from the old one,
* i.e. {@link #MODULE MODULE} access is lost.
* [A4] If the new lookup class is in a different package
@ -227,11 +230,14 @@ public class AccessControlTest {
}
if (!accessible) {
// no access to c2; lose all access.
changed |= (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED|UNCONDITIONAL); // [A6]
changed |= (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED|UNCONDITIONAL); // [A7]
}
if (!sameClass) {
changed |= ORIGINAL; // [A2]
}
if (m2 != m1 && m0 != m1) {
// hop to a third module; lose all access
changed |= (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED); // [A7]
changed |= (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED); // [A8]
}
if (!sameModule) {
changed |= MODULE; // [A3]
@ -254,20 +260,21 @@ public class AccessControlTest {
if ((modes1 & UNCONDITIONAL) != 0) plc = null; // [A8]
LookupCase l2 = new LookupCase(c2, plc, modes2);
assert(l2.lookupClass() == c2); // [A1]
assert((modes1 | modes2) == modes1); // [A2] (no elevation of access)
assert((modes1 | modes2) == modes1); // [A1-a] (no elevation of access)
assert(l2.prevLookupClass() == null || (modes2 & MODULE) == 0);
return l2;
}
LookupCase dropLookupMode(int modeToDrop) {
int oldModes = lookupModes();
int newModes = oldModes & ~(modeToDrop | PROTECTED);
int newModes = oldModes & ~(modeToDrop | PROTECTED | ORIGINAL);
switch (modeToDrop) {
case PUBLIC: newModes &= ~(MODULE|PACKAGE|PROTECTED|PRIVATE); break;
case MODULE: newModes &= ~(PACKAGE|PRIVATE); break;
case PACKAGE: newModes &= ~(PRIVATE); break;
case PROTECTED:
case PRIVATE:
case ORIGINAL:
case UNCONDITIONAL: break;
default: throw new IllegalArgumentException(modeToDrop + " is not a valid mode to drop");
}

View File

@ -117,6 +117,37 @@ public class CallerSensitiveAccess {
MethodHandles.publicLookup().unreflect(method);
}
/**
* Using a Lookup with no original access that can't lookup caller-sensitive
* method
*/
@Test(dataProvider = "callerSensitiveMethods",
expectedExceptions = IllegalAccessException.class)
public void testLookupNoOriginalAccessFind(@NoInjection Method method, String desc) throws Exception {
Lookup lookup = MethodHandles.lookup().dropLookupMode(Lookup.ORIGINAL);
assertTrue(lookup.hasFullPrivilegeAccess());
Class<?> refc = method.getDeclaringClass();
String name = method.getName();
MethodType mt = MethodType.methodType(method.getReturnType(), method.getParameterTypes());
if (Modifier.isStatic(method.getModifiers())) {
lookup.findStatic(refc, name, mt);
} else {
lookup.findVirtual(refc, name, mt);
}
}
/**
* Using a Lookup with no original access that can't unreflect caller-sensitive
* method
*/
@Test(dataProvider = "callerSensitiveMethods",
expectedExceptions = IllegalAccessException.class)
public void testLookupNoOriginalAccessUnreflect(@NoInjection Method method, String desc) throws Exception {
Lookup lookup = MethodHandles.lookup().dropLookupMode(Lookup.ORIGINAL);
assertTrue(lookup.hasFullPrivilegeAccess());
lookup.unreflect(method);
}
// -- Test method handles to setAccessible --
private int aField;

View File

@ -44,7 +44,7 @@ public class DropLookupModeTest {
public void testBasic() {
final Lookup fullPowerLookup = MethodHandles.lookup();
final Class<?> lc = fullPowerLookup.lookupClass();
assertTrue(fullPowerLookup.lookupModes() == (PUBLIC|MODULE|PACKAGE|PROTECTED|PRIVATE));
assertTrue(fullPowerLookup.lookupModes() == (PUBLIC|MODULE|PACKAGE|PROTECTED|PRIVATE|ORIGINAL));
Lookup lookup = fullPowerLookup.dropLookupMode(PRIVATE);
assertTrue(lookup.lookupClass() == lc);
@ -78,7 +78,7 @@ public class DropLookupModeTest {
public void testReducingAccess() {
Lookup lookup = MethodHandles.lookup();
final Class<?> lc = lookup.lookupClass();
assertTrue(lookup.lookupModes() == (PUBLIC|MODULE|PACKAGE|PROTECTED|PRIVATE));
assertTrue(lookup.lookupModes() == (PUBLIC|MODULE|PACKAGE|PROTECTED|PRIVATE|ORIGINAL));
lookup = lookup.dropLookupMode(PROTECTED);
assertTrue(lookup.lookupClass() == lc);

View File

@ -0,0 +1,510 @@
/*
* Copyright (c) 2020, 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 8230501
* @library /test/lib
* @modules java.base/jdk.internal.org.objectweb.asm
* @run testng/othervm ClassDataTest
*/
import java.io.IOException;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import jdk.internal.org.objectweb.asm.*;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static java.lang.invoke.MethodHandles.Lookup.*;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
import static org.testng.Assert.*;
public class ClassDataTest {
private static final Lookup LOOKUP = MethodHandles.lookup();
@Test
public void testOriginalAccess() throws IllegalAccessException {
Lookup lookup = hiddenClass(20);
assertTrue(lookup.hasFullPrivilegeAccess());
int value = MethodHandles.classData(lookup, "_", int.class);
assertEquals(value, 20);
Integer i = MethodHandles.classData(lookup, "_", Integer.class);
assertEquals(i.intValue(), 20);
}
/*
* A lookup class with no class data.
*/
@Test
public void noClassData() throws IllegalAccessException {
assertNull(MethodHandles.classData(LOOKUP, "_", Object.class));
}
@DataProvider(name = "teleportedLookup")
private Object[][] teleportedLookup() throws ReflectiveOperationException {
Lookup lookup = hiddenClass(30);
Class<?> hc = lookup.lookupClass();
assertClassData(lookup, 30);
int fullAccess = PUBLIC|PROTECTED|PACKAGE|MODULE|PRIVATE;
return new Object[][] {
new Object[] { MethodHandles.privateLookupIn(hc, LOOKUP), fullAccess},
new Object[] { LOOKUP.in(hc), fullAccess & ~(PROTECTED|PRIVATE) },
new Object[] { lookup.dropLookupMode(PRIVATE), fullAccess & ~(PROTECTED|PRIVATE) },
};
}
@Test(dataProvider = "teleportedLookup", expectedExceptions = { IllegalAccessException.class })
public void illegalAccess(Lookup lookup, int access) throws IllegalAccessException {
int lookupModes = lookup.lookupModes();
assertTrue((lookupModes & ORIGINAL) == 0);
assertEquals(lookupModes, access);
MethodHandles.classData(lookup, "_", int.class);
}
@Test(expectedExceptions = { ClassCastException.class })
public void incorrectType() throws IllegalAccessException {
Lookup lookup = hiddenClass(20);
MethodHandles.classData(lookup, "_", Long.class);
}
@Test(expectedExceptions = { IndexOutOfBoundsException.class })
public void invalidIndex() throws IllegalAccessException {
Lookup lookup = hiddenClass(List.of());
MethodHandles.classDataAt(lookup, "_", Object.class, 0);
}
@Test(expectedExceptions = { NullPointerException.class })
public void unboxNull() throws IllegalAccessException {
List<Integer> list = new ArrayList<>();
list.add(null);
Lookup lookup = hiddenClass(list);
MethodHandles.classDataAt(lookup, "_", int.class, 0);
}
@Test
public void nullElement() throws IllegalAccessException {
List<Object> list = new ArrayList<>();
list.add(null);
Lookup lookup = hiddenClass(list);
assertTrue(MethodHandles.classDataAt(lookup, "_", Object.class, 0) == null);
}
@Test
public void intClassData() throws ReflectiveOperationException {
ClassByteBuilder builder = new ClassByteBuilder("T1-int");
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, int.class).build();
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, 100, true);
int value = MethodHandles.classData(lookup, "_", int.class);
assertEquals(value, 100);
// call through condy
assertClassData(lookup, 100);
}
@Test
public void floatClassData() throws ReflectiveOperationException {
ClassByteBuilder builder = new ClassByteBuilder("T1-float");
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, float.class).build();
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, 0.1234f, true);
float value = MethodHandles.classData(lookup, "_", float.class);
assertEquals(value, 0.1234f);
// call through condy
assertClassData(lookup, 0.1234f);
}
@Test
public void classClassData() throws ReflectiveOperationException {
Class<?> hc = hiddenClass(100).lookupClass();
ClassByteBuilder builder = new ClassByteBuilder("T2");
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, Class.class).build();
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, hc, true);
Class<?> value = MethodHandles.classData(lookup, "_", Class.class);
assertEquals(value, hc);
// call through condy
assertClassData(lookup, hc);
}
@Test
public void arrayClassData() throws ReflectiveOperationException {
ClassByteBuilder builder = new ClassByteBuilder("T3");
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, String[].class).build();
String[] colors = new String[] { "red", "yellow", "blue"};
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, colors, true);
assertClassData(lookup, colors.clone());
// class data is modifiable and not a constant
colors[0] = "black";
// it will get back the modified class data
String[] value = MethodHandles.classData(lookup, "_", String[].class);
assertEquals(value, colors);
// even call through condy as it's not a constant
assertClassData(lookup, colors);
}
@Test
public void listClassData() throws ReflectiveOperationException {
ClassByteBuilder builder = new ClassByteBuilder("T4");
byte[] bytes = builder.classDataAt(ACC_PUBLIC|ACC_STATIC, Integer.class, 2).build();
List<Integer> cd = List.of(100, 101, 102, 103);
int expected = 102; // element at index=2
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, cd, true);
int value = MethodHandles.classDataAt(lookup, "_", int.class, 2);
assertEquals(value, expected);
// call through condy
assertClassData(lookup, expected);
}
@Test
public void arrayListClassData() throws ReflectiveOperationException {
ClassByteBuilder builder = new ClassByteBuilder("T4");
byte[] bytes = builder.classDataAt(ACC_PUBLIC|ACC_STATIC, Integer.class, 1).build();
ArrayList<Integer> cd = new ArrayList<>();
Stream.of(100, 101, 102, 103).forEach(cd::add);
int expected = 101; // element at index=1
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, cd, true);
int value = MethodHandles.classDataAt(lookup, "_", int.class, 1);
assertEquals(value, expected);
// call through condy
assertClassData(lookup, expected);
}
private static Lookup hiddenClass(int value) {
ClassByteBuilder builder = new ClassByteBuilder("HC");
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, int.class).build();
try {
return LOOKUP.defineHiddenClassWithClassData(bytes, value, true);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
private static Lookup hiddenClass(List<?> list) {
ClassByteBuilder builder = new ClassByteBuilder("HC");
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, List.class).build();
try {
return LOOKUP.defineHiddenClassWithClassData(bytes, list, true);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
@Test
public void condyInvokedFromVirtualMethod() throws ReflectiveOperationException {
ClassByteBuilder builder = new ClassByteBuilder("T5");
// generate classData instance method
byte[] bytes = builder.classData(ACC_PUBLIC, Class.class).build();
Lookup hcLookup = hiddenClass(100);
assertClassData(hcLookup, 100);
Class<?> hc = hcLookup.lookupClass();
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, hc, true);
Class<?> value = MethodHandles.classData(lookup, "_", Class.class);
assertEquals(value, hc);
// call through condy
Class<?> c = lookup.lookupClass();
assertClassData(lookup, c.newInstance(), hc);
}
@Test
public void immutableListClassData() throws ReflectiveOperationException {
ClassByteBuilder builder = new ClassByteBuilder("T6");
// generate classDataAt instance method
byte[] bytes = builder.classDataAt(ACC_PUBLIC, Integer.class, 2).build();
List<Integer> cd = List.of(100, 101, 102, 103);
int expected = 102; // element at index=2
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, cd, true);
int value = MethodHandles.classDataAt(lookup, "_", int.class, 2);
assertEquals(value, expected);
// call through condy
Class<?> c = lookup.lookupClass();
assertClassData(lookup, c.newInstance() ,expected);
}
/*
* The return value of MethodHandles::classDataAt is the element
* contained in the list when the method is called.
* If MethodHandles::classDataAt is called via condy, the value
* will be captured as a constant. If the class data is modified
* after the element at the given index is computed via condy,
* subsequent LDC of such ConstantDynamic entry will return the same
* value. However, direct invocation of MethodHandles::classDataAt
* will return the modified value.
*/
@Test
public void mutableListClassData() throws ReflectiveOperationException {
ClassByteBuilder builder = new ClassByteBuilder("T7");
// generate classDataAt instance method
byte[] bytes = builder.classDataAt(ACC_PUBLIC, MethodType.class, 0).build();
MethodType mtype = MethodType.methodType(int.class, String.class);
List<MethodType> cd = new ArrayList<>(List.of(mtype));
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, cd, true);
// call through condy
Class<?> c = lookup.lookupClass();
assertClassData(lookup, c.newInstance(), mtype);
// modify the class data
assertTrue(cd.remove(0) == mtype);
cd.add(0, MethodType.methodType(void.class));
MethodType newMType = cd.get(0);
// loading the element using condy returns the original value
assertClassData(lookup, c.newInstance(), mtype);
// direct invocation of MethodHandles.classDataAt returns the modified value
assertEquals(MethodHandles.classDataAt(lookup, "_", MethodType.class, 0), newMType);
}
// helper method to extract from a class data map
public static <T> T getClassDataEntry(Lookup lookup, String key, Class<T> type) throws IllegalAccessException {
Map<String, T> cd = MethodHandles.classData(lookup, "_", Map.class);
return type.cast(cd.get(key));
}
@Test
public void classDataMap() throws ReflectiveOperationException {
ClassByteBuilder builder = new ClassByteBuilder("map");
// generate classData static method
Handle bsm = new Handle(H_INVOKESTATIC, "ClassDataTest", "getClassDataEntry",
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;",
false);
// generate two accessor methods to get the entries from class data
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, Map.class)
.classData(ACC_PUBLIC|ACC_STATIC, "getClass",
Class.class, new ConstantDynamic("class", Type.getDescriptor(Class.class), bsm))
.classData(ACC_PUBLIC|ACC_STATIC, "getMethod",
MethodHandle.class, new ConstantDynamic("method", Type.getDescriptor(MethodHandle.class), bsm))
.build();
// generate a hidden class
Lookup hcLookup = hiddenClass(100);
Class<?> hc = hcLookup.lookupClass();
assertClassData(hcLookup, 100);
MethodHandle mh = hcLookup.findStatic(hc, "classData", MethodType.methodType(int.class));
Map<String, Object> cd = Map.of("class", hc, "method", mh);
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, cd, true);
assertClassData(lookup, cd);
// validate the entries from the class data map
Class<?> c = lookup.lookupClass();
Method m = c.getMethod("getClass");
Class<?> v = (Class<?>)m.invoke(null);
assertEquals(hc, v);
Method m1 = c.getMethod("getMethod");
MethodHandle v1 = (MethodHandle) m1.invoke(null);
assertEquals(mh, v1);
}
@Test(expectedExceptions = { IllegalArgumentException.class })
public void nonDefaultName() throws ReflectiveOperationException {
ClassByteBuilder builder = new ClassByteBuilder("nonDefaultName");
byte[] bytes = builder.classData(ACC_PUBLIC|ACC_STATIC, Class.class)
.build();
Lookup lookup = LOOKUP.defineHiddenClassWithClassData(bytes, ClassDataTest.class, true);
assertClassData(lookup, ClassDataTest.class);
// throw IAE
MethodHandles.classData(lookup, "non_default_name", Class.class);
}
static class ClassByteBuilder {
private static final String OBJECT_CLS = "java/lang/Object";
private static final String MHS_CLS = "java/lang/invoke/MethodHandles";
private static final String CLASS_DATA_BSM_DESCR =
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;";
private final ClassWriter cw;
private final String classname;
/**
* A builder to generate a class file to access class data
* @param classname
*/
ClassByteBuilder(String classname) {
this.classname = classname;
this.cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
cw.visit(V14, ACC_FINAL, classname, null, OBJECT_CLS, null);
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, OBJECT_CLS, "<init>", "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
byte[] build() {
cw.visitEnd();
byte[] bytes = cw.toByteArray();
Path p = Paths.get(classname + ".class");
try (OutputStream os = Files.newOutputStream(p)) {
os.write(bytes);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return bytes;
}
/*
* Generate classData method to load class data via condy
*/
ClassByteBuilder classData(int accessFlags, Class<?> returnType) {
MethodType mtype = MethodType.methodType(returnType);
MethodVisitor mv = cw.visitMethod(accessFlags,
"classData",
mtype.descriptorString(), null, null);
mv.visitCode();
Handle bsm = new Handle(H_INVOKESTATIC, MHS_CLS, "classData",
CLASS_DATA_BSM_DESCR,
false);
ConstantDynamic dynamic = new ConstantDynamic("_", Type.getDescriptor(returnType), bsm);
mv.visitLdcInsn(dynamic);
mv.visitInsn(returnType == int.class ? IRETURN :
(returnType == float.class ? FRETURN : ARETURN));
mv.visitMaxs(0, 0);
mv.visitEnd();
return this;
}
/*
* Generate classDataAt method to load an element from class data via condy
*/
ClassByteBuilder classDataAt(int accessFlags, Class<?> returnType, int index) {
MethodType mtype = MethodType.methodType(returnType);
MethodVisitor mv = cw.visitMethod(accessFlags,
"classData",
mtype.descriptorString(), null, null);
mv.visitCode();
Handle bsm = new Handle(H_INVOKESTATIC, "java/lang/invoke/MethodHandles", "classDataAt",
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;I)Ljava/lang/Object;",
false);
ConstantDynamic dynamic = new ConstantDynamic("_", Type.getDescriptor(returnType), bsm, index);
mv.visitLdcInsn(dynamic);
mv.visitInsn(returnType == int.class? IRETURN : ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
return this;
}
ClassByteBuilder classData(int accessFlags, String name, Class<?> returnType, ConstantDynamic dynamic) {
MethodType mtype = MethodType.methodType(returnType);
MethodVisitor mv = cw.visitMethod(accessFlags,
name,
mtype.descriptorString(), null, null);
mv.visitCode();
mv.visitLdcInsn(dynamic);
mv.visitInsn(returnType == int.class? IRETURN : ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
return this;
}
}
/*
* Load an int constant from class data via condy and
* verify it matches the given value.
*/
private void assertClassData(Lookup lookup, int value) throws ReflectiveOperationException {
Class<?> c = lookup.lookupClass();
Method m = c.getMethod("classData");
int v = (int) m.invoke(null);
assertEquals(value, v);
}
/*
* Load an int constant from class data via condy and
* verify it matches the given value.
*/
private void assertClassData(Lookup lookup, Object o, int value) throws ReflectiveOperationException {
Class<?> c = lookup.lookupClass();
Method m = c.getMethod("classData");
int v = (int) m.invoke(o);
assertEquals(value, v);
}
/*
* Load a float constant from class data via condy and
* verify it matches the given value.
*/
private void assertClassData(Lookup lookup, float value) throws ReflectiveOperationException {
Class<?> c = lookup.lookupClass();
Method m = c.getMethod("classData");
float v = (float) m.invoke(null);
assertEquals(value, v);
}
/*
* Load a Class constant from class data via condy and
* verify it matches the given value.
*/
private void assertClassData(Lookup lookup, Class<?> value) throws ReflectiveOperationException {
Class<?> c = lookup.lookupClass();
Method m = c.getMethod("classData");
Class<?> v = (Class<?>)m.invoke(null);
assertEquals(value, v);
}
/*
* Load a Class from class data via condy and
* verify it matches the given value.
*/
private void assertClassData(Lookup lookup, Object o, Class<?> value) throws ReflectiveOperationException {
Class<?> c = lookup.lookupClass();
Method m = c.getMethod("classData");
Object v = m.invoke(o);
assertEquals(value, v);
}
/*
* Load an Object from class data via condy and
* verify it matches the given value.
*/
private void assertClassData(Lookup lookup, Object value) throws ReflectiveOperationException {
Class<?> c = lookup.lookupClass();
Method m = c.getMethod("classData");
Object v = m.invoke(null);
assertEquals(value, v);
}
/*
* Load an Object from class data via condy and
* verify it matches the given value.
*/
private void assertClassData(Lookup lookup, Object o, Object value) throws ReflectiveOperationException {
Class<?> c = lookup.lookupClass();
Method m = c.getMethod("classData");
Object v = m.invoke(o);
assertEquals(value, v);
}
}

View File

@ -39,7 +39,6 @@ import static org.testng.Assert.*;
@Test
public class PrivateLookupInTests {
/**
* A public and non-public types in the test module but in a different
* package to the test class.
@ -75,6 +74,7 @@ public class PrivateLookupInTests {
Lookup lookup = MethodHandles.privateLookupIn(nonPublicType, MethodHandles.lookup());
assertTrue(lookup.lookupClass() == nonPublicType);
assertTrue(lookup.hasFullPrivilegeAccess());
assertTrue((lookup.lookupModes() & ORIGINAL) == 0);
// get obj field
MethodHandle mh = lookup.findStaticGetter(nonPublicType, "obj", Object.class);
@ -88,6 +88,7 @@ public class PrivateLookupInTests {
assertTrue((caller.lookupModes() & PRIVATE) == 0);
assertTrue((caller.lookupModes() & PACKAGE) == 0);
assertTrue((caller.lookupModes() & MODULE) != 0);
assertTrue((caller.lookupModes() & ORIGINAL) == 0);
Lookup lookup = MethodHandles.privateLookupIn(nonPublicType, caller);
}
@ -113,8 +114,8 @@ public class PrivateLookupInTests {
Lookup lookup = MethodHandles.privateLookupIn(clazz, MethodHandles.lookup());
assertTrue(lookup.lookupClass() == clazz);
assertTrue((lookup.lookupModes() & PRIVATE) != 0);
assertFalse(lookup.hasFullPrivilegeAccess());
assertTrue((lookup.lookupModes() & PRIVATE) == PRIVATE);
assertTrue((lookup.lookupModes() & MODULE) == 0);
// get obj field
MethodHandle mh = lookup.findStaticGetter(clazz, "obj", Object.class);
@ -138,7 +139,8 @@ public class PrivateLookupInTests {
thisModule.addReads(clazz.getModule());
Lookup lookup = MethodHandles.privateLookupIn(clazz, MethodHandles.lookup());
assertTrue(lookup.lookupClass() == clazz);
assertTrue((lookup.lookupModes() & PRIVATE) != 0);
assertTrue((lookup.lookupModes() & PRIVATE) == PRIVATE);
assertTrue((lookup.lookupModes() & MODULE) == 0);
}
// test does not read m2, m2 opens p2 to test

View File

@ -158,6 +158,10 @@ public class RevealDirectTest {
// CS methods have to be revealed with a matching lookupClass
testOnMembersNoReveal("testCallerSensitiveNegative/2", mems, Simple.localLookup(), publicLookup());
testOnMembersNoReveal("testCallerSensitiveNegative/3", mems, Simple.localLookup(), Nestmate.localLookup());
// CS methods have to have original access
Lookup lookup = Simple.localLookup().dropLookupMode(Lookup.ORIGINAL);
testOnMembersNoLookup("testCallerSensitiveNegative/4", mems, lookup);
testOnMembersNoReveal("testCallerSensitiveNegative/5", mems, Simple.localLookup(), lookup);
}
@Test public void testMethodHandleNatives() throws Throwable {
if (VERBOSE) System.out.println("@Test testMethodHandleNatives");

View File

@ -72,6 +72,7 @@ public class HiddenNestmateTest {
Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false);
Class<?> c = lookup.lookupClass();
assertTrue(lookup.hasFullPrivilegeAccess());
assertTrue((lookup.lookupModes() & ORIGINAL) == ORIGINAL);
assertTrue(c.getNestHost() == c); // host of its own nest
assertTrue(c.isHidden());
@ -137,6 +138,8 @@ public class HiddenNestmateTest {
// Teleport to a hidden nestmate
Lookup lc = MethodHandles.lookup().in(lookup.lookupClass());
assertTrue((lc.lookupModes() & PRIVATE) != 0);
assertTrue((lc.lookupModes() & ORIGINAL) == 0);
Lookup lc2 = lc.defineHiddenClass(bytes, false, NESTMATE);
assertNestmate(lc2);
}

View File

@ -91,7 +91,7 @@ public class ModuleAccessTest {
*
* [A0] targetClass becomes the lookup class
* [A1] no change in previous lookup class
* [A2] PROTECTED and PRIVATE are dropped
* [A2] PROTECTED, PRIVATE and ORIGINAL are dropped
*/
@Test(dataProvider = "samePackage")
public void testLookupInSamePackage(Lookup lookup, Class<?> targetClass) throws Exception {
@ -102,7 +102,7 @@ public class ModuleAccessTest {
assertTrue(lookupClass.getModule() == targetClass.getModule());
assertTrue(lookup2.lookupClass() == targetClass); // [A0]
assertTrue(lookup2.previousLookupClass() == lookup.previousLookupClass()); // [A1]
assertTrue(lookup2.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE))); // [A2]
assertTrue(lookup2.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|ORIGINAL))); // [A2]
}
@DataProvider(name = "sameModule")
@ -119,7 +119,7 @@ public class ModuleAccessTest {
*
* [A0] targetClass becomes the lookup class
* [A1] no change in previous lookup class
* [A2] PROTECTED, PRIVATE and PACKAGE are dropped
* [A2] PROTECTED, PRIVATE, PACKAGE and ORIGINAL are dropped
*/
@Test(dataProvider = "sameModule")
public void testLookupInSameModule(Lookup lookup, Class<?> targetClass) throws Exception {
@ -130,7 +130,7 @@ public class ModuleAccessTest {
assertTrue(lookupClass.getModule() == targetClass.getModule());
assertTrue(lookup2.lookupClass() == targetClass); // [A0]
assertTrue(lookup2.previousLookupClass() == lookup.previousLookupClass()); // [A1]
assertTrue(lookup2.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE))); // [A2]
assertTrue(lookup2.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE|ORIGINAL))); // [A2]
}
@DataProvider(name = "anotherModule")
@ -148,7 +148,7 @@ public class ModuleAccessTest {
*
* [A0] targetClass becomes the lookup class
* [A1] lookup class becomes previous lookup class
* [A2] PROTECTED, PRIVATE, PACKAGE, and MODULE are dropped
* [A2] PROTECTED, PRIVATE, PACKAGE, MODULE and ORIGINAL are dropped
* [A3] no access to module internal types in m0 and m1
* [A4] if m1 reads m0, can access public types in m0; otherwise no access.
* [A5] can access public types in m1 exported to m0
@ -168,7 +168,7 @@ public class ModuleAccessTest {
Lookup lookup2 = lookup.in(targetClass);
assertTrue(lookup2.lookupClass() == targetClass); // [A0]
assertTrue(lookup2.previousLookupClass() == lookup.lookupClass()); // [A1]
assertTrue(lookup2.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE|MODULE))); // [A2]
assertTrue(lookup2.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE|MODULE|ORIGINAL))); // [A2]
// [A3] no access to module internal type in m0
// [A4] if m1 reads m0,
@ -289,7 +289,7 @@ public class ModuleAccessTest {
Lookup lookup1 = lookup.in(c1);
assertTrue(lookup1.lookupClass() == c1);
assertTrue(lookup1.previousLookupClass() == c0);
assertTrue(lookup1.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE|MODULE)));
assertTrue(lookup1.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE|MODULE|ORIGINAL)));
Lookup lookup2 = lookup1.in(c2);
assertTrue(lookup2.lookupClass() == c2); // [A0]

View File

@ -230,7 +230,7 @@ public class LookupDefineClass {
private static final MethodHandles.Lookup lookup =
defineHostClass(new Loader("anonymous-class-loader"),"foo.AnonymousHost", FOO_HOST_BYTES);
@SuppressWarnings("deprecation")
@SuppressWarnings("removal")
@Benchmark
public Class<?> load() throws ClassNotFoundException {
return unsafe.defineAnonymousClass(lookup.lookupClass(), X_BYTECODE, null);