< prev index next > src/java.base/share/classes/java/lang/invoke/MethodHandles.java
Print this page
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
- import java.lang.reflect.ReflectPermission;
import java.nio.ByteOrder;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
Lookup(Class<?> lookupClass) {
this(lookupClass, null, FULL_POWER_MODES);
}
private Lookup(Class<?> lookupClass, Class<?> prevLookupClass, int allowedModes) {
+ assert lookupClass.isPrimaryType();
assert prevLookupClass == null || ((allowedModes & MODULE) == 0
&& prevLookupClass.getModule() != lookupClass.getModule());
assert !lookupClass.isArray() && !lookupClass.isPrimitive();
this.lookupClass = lookupClass;
this.prevLookupClass = prevLookupClass;
* <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
* @throws NullPointerException if any argument is null
*/
public MethodHandle findStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
MemberName method = resolveOrFail(REF_invokeStatic, refc, name, type);
+ // resolveOrFail could return a non-static <init> method if present
+ // detect and throw NSME before producing a MethodHandle
+ if (!method.isStatic() && name.equals("<init>")) {
+ throw new NoSuchMethodException("illegal method name: " + name);
+ }
+
return getDirectMethod(REF_invokeStatic, refc, method, findBoundCallerLookup(method));
}
/**
* Produces a method handle for a virtual method.
ProcessBuilder.class, methodType(void.class, String[].class));
ProcessBuilder pb = (ProcessBuilder)
MH_newProcessBuilder.invoke("x", "y", "z");
assertEquals("[x, y, z]", pb.command().toString());
* }</pre></blockquote>
+ *
+ * @apiNote
+ * This method does not find a static {@code <init>} factory method as it is invoked
+ * via {@code invokestatic} bytecode as opposed to {@code invokespecial} for an
+ * object constructor. To look up static {@code <init>} factory method, use
+ * the {@link #findStatic(Class, String, MethodType) findStatic} method.
+ *
* @param refc the class or interface from which the method is accessed
* @param type the type of the method, with the receiver argument omitted, and a void return type
* @return the desired method handle
* @throws NoSuchMethodException if the constructor does not exist
* @throws IllegalAccessException if access checking fails
*/
public MethodHandle findConstructor(Class<?> refc, MethodType type) throws NoSuchMethodException, IllegalAccessException {
if (refc.isArray()) {
throw new NoSuchMethodException("no constructor for array class: " + refc.getName());
}
+ if (type.returnType() != void.class) {
+ throw new NoSuchMethodException("Constructors must have void return type: " + refc.getName());
+ }
String name = "<init>";
MemberName ctor = resolveOrFail(REF_newInvokeSpecial, refc, name, type);
return getDirectConstructor(refc, ctor);
}
* is set and {@code asVarargsCollector} fails
* @throws NullPointerException if the argument is null
*/
public MethodHandle unreflectConstructor(Constructor<?> c) throws IllegalAccessException {
MemberName ctor = new MemberName(c);
- assert(ctor.isConstructor());
+ assert(ctor.isObjectConstructorOrStaticInitMethod());
@SuppressWarnings("deprecation")
Lookup lookup = c.isAccessible() ? IMPL_LOOKUP : this;
- return lookup.getDirectConstructorNoSecurityManager(ctor.getDeclaringClass(), ctor);
+ Class<?> defc = c.getDeclaringClass();
+ if (ctor.isObjectConstructor()) {
+ assert(ctor.getReturnType() == void.class);
+ return lookup.getDirectConstructorNoSecurityManager(defc, ctor);
+ } else {
+ // static init factory is a static method
+ assert(ctor.isMethod() && ctor.getReturnType() == defc && ctor.getReferenceKind() == REF_invokeStatic) : ctor.toString();
+ assert(!MethodHandleNatives.isCallerSensitive(ctor)); // must not be caller-sensitive
+ return lookup.getDirectMethodNoSecurityManager(ctor.getReferenceKind(), defc, ctor, lookup);
+ }
}
/**
* Produces a method handle giving read access to a reflected field.
* The type of the method handle will have a return type of the field's
return caller == null || VerifyAccess.isClassAccessible(type, caller, prevLookupClass, allowedModes);
}
/** Check name for an illegal leading "<" character. */
void checkMethodName(byte refKind, String name) throws NoSuchMethodException {
- if (name.startsWith("<") && refKind != REF_newInvokeSpecial)
- throw new NoSuchMethodException("illegal method name: "+name);
+ // "<init>" can only be invoked via invokespecial or it's a static init factory
+ if (name.startsWith("<") && refKind != REF_newInvokeSpecial &&
+ !(refKind == REF_invokeStatic && name.equals("<init>"))) {
+ throw new NoSuchMethodException("illegal method name: " + name);
+ }
}
/**
* Find my trustable caller class if m is a caller sensitive method.
* If this lookup object has original full privilege access, then the caller class is the lookupClass.
smgr.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);
}
// Step 3:
Class<?> defc = m.getDeclaringClass();
- if (!fullPrivilegeLookup && defc != refc) {
+ if (!fullPrivilegeLookup && defc.asPrimaryType() != refc.asPrimaryType()) {
ReflectUtil.checkPackageAccess(defc);
}
}
void checkMethod(byte refKind, Class<?> refc, MemberName m) throws IllegalAccessException {
boolean wantStatic = (refKind == REF_invokeStatic);
String message;
- if (m.isConstructor())
+ if (m.isObjectConstructor())
message = "expected a method, not a constructor";
else if (!m.isMethod())
message = "expected a method";
else if (wantStatic != m.isStatic())
message = wantStatic ? "expected a static method" : "expected a non-static method";
String accessFailedMessage(Class<?> refc, MemberName m) {
Class<?> defc = m.getDeclaringClass();
int mods = m.getModifiers();
// check the class first:
boolean classOK = (Modifier.isPublic(defc.getModifiers()) &&
- (defc == refc ||
+ (defc.asPrimaryType() == refc.asPrimaryType() ||
Modifier.isPublic(refc.getModifiers())));
if (!classOK && (allowedModes & PACKAGE) != 0) {
// ignore previous lookup class to check if default package access
classOK = (VerifyAccess.isClassAccessible(defc, lookupClass(), null, FULL_POWER_MODES) &&
- (defc == refc ||
+ (defc.asPrimaryType() == refc.asPrimaryType() ||
VerifyAccess.isClassAccessible(refc, lookupClass(), null, FULL_POWER_MODES)));
}
if (!classOK)
return "class is not public";
if (Modifier.isPublic(mods))
checkMethod(refKind, refc, method);
// Optionally check with the security manager; this isn't needed for unreflect* calls.
if (checkSecurity)
checkSecurityManager(refc, method);
assert(!method.isMethodHandleInvoke());
-
if (refKind == REF_invokeSpecial &&
refc != lookupClass() &&
!refc.isInterface() &&
refc != lookupClass().getSuperclass() &&
refc.isAssignableFrom(lookupClass())) {
return getDirectConstructorCommon(refc, ctor, checkSecurity);
}
/** Common code for all constructors; do not call directly except from immediately above. */
private MethodHandle getDirectConstructorCommon(Class<?> refc, MemberName ctor,
boolean checkSecurity) throws IllegalAccessException {
- assert(ctor.isConstructor());
+ assert(ctor.isObjectConstructor());
checkAccess(REF_newInvokeSpecial, refc, ctor);
// Optionally check with the security manager; this isn't needed for unreflect* calls.
if (checkSecurity)
checkSecurityManager(refc, ctor);
assert(!MethodHandleNatives.isCallerSensitive(ctor)); // maybeBindCaller not relevant here
* The first and second arguments will be the array type and int.
*
* <p> When the returned method handle is invoked,
* the array reference and array index are checked.
* A {@code NullPointerException} will be thrown if the array reference
- * is {@code null} and an {@code ArrayIndexOutOfBoundsException} will be
+ * is {@code null} or if the array's element type is a {@link Class#isPrimitiveValueType()
+ * a primitive value type} and attempts to set {@code null} in the
+ * array element. An {@code ArrayIndexOutOfBoundsException} will be
* thrown if the index is negative or if it is greater than or equal to
* the length of the array.
*
* @param arrayClass the class of an array
* @return a method handle which can store values into the array type
* else reference conversions are attempted.
* <p>The returned method handle is equivalent to {@code identity(type).bindTo(value)}.
* @param type the return type of the desired method handle
* @param value the value to return
* @return a method handle of the given return type and no arguments, which always returns the given value
- * @throws NullPointerException if the {@code type} argument is null
+ * @throws NullPointerException if the given {@code type} is null, or
+ * if the given {@code type} is primitive or a primitive value type
+ * and the given value is null
* @throws ClassCastException if the value cannot be converted to the required return type
* @throws IllegalArgumentException if the given type is {@code void.class}
*/
public static MethodHandle constant(Class<?> type, Object value) {
if (type.isPrimitive()) {
value = w.convert(value, type);
if (w.zero().equals(value))
return zero(w, type);
return insertArguments(identity(type), 0, value);
} else {
- if (value == null)
+ if (!type.isPrimitiveValueType() && value == null)
return zero(Wrapper.OBJECT, type);
return identity(type).bindTo(value);
}
}
* @see MethodHandles#explicitCastArguments
* @since 9
*/
public static MethodHandle zero(Class<?> type) {
Objects.requireNonNull(type);
- return type.isPrimitive() ? zero(Wrapper.forPrimitiveType(type), type) : zero(Wrapper.OBJECT, type);
+ if (type.isPrimitive()) {
+ return zero(Wrapper.forPrimitiveType(type), type);
+ } else if (type.isPrimitiveValueType()) {
+ // singleton default value
+ Object value = UNSAFE.uninitializedDefaultValue(type);
+ return identity(type).bindTo(value);
+ } else {
+ return zero(Wrapper.OBJECT, type);
+ }
}
private static MethodHandle identityOrVoid(Class<?> type) {
return type == void.class ? zero(type) : identity(type);
}
/**
* Produces a method handle of the requested type which ignores any arguments, does nothing,
* and returns a suitable default depending on the return type.
- * That is, it returns a zero primitive value, a {@code null}, or {@code void}.
+ * If the requested type is a primitive type or {@code void}, it returns
+ * a zero primitive value or {@code void}.
+ * If the requested type is a {@linkplain Class#isPrimitiveValueType() primitive value type},
+ * it returns a primitive object with the default value.
+ * If the requested type is a reference type, it returns {@code null}.
* <p>The returned method handle is equivalent to
* {@code dropArguments(zero(type.returnType()), 0, type.parameterList())}.
*
* @apiNote Given a predicate and target, a useful "if-then" construct can be produced as
* {@code guardWithTest(pred, target, empty(target.type())}.
return dropArguments(zero(type.returnType()), 0, type.parameterList());
}
private static final MethodHandle[] IDENTITY_MHS = new MethodHandle[Wrapper.COUNT];
private static MethodHandle makeIdentity(Class<?> ptype) {
- MethodType mtype = methodType(ptype, ptype);
+ MethodType mtype = MethodType.methodType(ptype, ptype);
LambdaForm lform = LambdaForm.identityForm(BasicType.basicType(ptype));
return MethodHandleImpl.makeIntrinsic(mtype, lform, Intrinsic.IDENTITY);
}
private static MethodHandle zero(Wrapper btw, Class<?> rtype) {
< prev index next >