/*
* Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package java.lang.invoke;
import java.security.
AccessController;
import java.security.
PrivilegedAction;
import java.util.
ArrayList;
import java.util.
Arrays;
import java.util.
Collections;
import java.util.function.
Function;
import sun.invoke.empty.
Empty;
import sun.invoke.util.
ValueConversions;
import sun.invoke.util.
VerifyType;
import sun.invoke.util.
Wrapper;
import sun.reflect.
CallerSensitive;
import sun.reflect.
Reflection;
import static java.lang.invoke.
LambdaForm.*;
import static java.lang.invoke.
MethodHandleStatics.*;
import static java.lang.invoke.
MethodHandles.
Lookup.
IMPL_LOOKUP;
/**
* Trusted implementation code for MethodHandle.
* @author jrose
*/
/*non-public*/ abstract class
MethodHandleImpl {
// Do not adjust this except for special platforms:
private static final int
MAX_ARITY;
static {
final
Object[]
values = { 255 };
AccessController.
doPrivileged(new
PrivilegedAction<
Void>() {
@
Override
public
Void run() {
values[0] =
Integer.
getInteger(
MethodHandleImpl.class.
getName()+".MAX_ARITY", 255);
return null;
}
});
MAX_ARITY = (
Integer)
values[0];
}
/// Factory methods to create method handles:
static void
initStatics() {
// Trigger selected static initializations.
MemberName.
Factory.
INSTANCE.
getClass();
}
static
MethodHandle makeArrayElementAccessor(
Class<?>
arrayClass, boolean
isSetter) {
if (
arrayClass ==
Object[].class)
return (
isSetter ?
ArrayAccessor.
OBJECT_ARRAY_SETTER :
ArrayAccessor.
OBJECT_ARRAY_GETTER);
if (!
arrayClass.
isArray())
throw
newIllegalArgumentException("not an array: "+
arrayClass);
MethodHandle[]
cache =
ArrayAccessor.
TYPED_ACCESSORS.
get(
arrayClass);
int
cacheIndex = (
isSetter ?
ArrayAccessor.
SETTER_INDEX :
ArrayAccessor.
GETTER_INDEX);
MethodHandle mh =
cache[
cacheIndex];
if (
mh != null) return
mh;
mh =
ArrayAccessor.
getAccessor(
arrayClass,
isSetter);
MethodType correctType =
ArrayAccessor.
correctType(
arrayClass,
isSetter);
if (
mh.
type() !=
correctType) {
assert(
mh.
type().
parameterType(0) ==
Object[].class);
assert((
isSetter ?
mh.
type().
parameterType(2) :
mh.
type().
returnType()) ==
Object.class);
assert(
isSetter ||
correctType.
parameterType(0).
getComponentType() ==
correctType.
returnType());
// safe to view non-strictly, because element type follows from array type
mh =
mh.
viewAsType(
correctType, false);
}
mh =
makeIntrinsic(
mh, (
isSetter ?
Intrinsic.
ARRAY_STORE :
Intrinsic.
ARRAY_LOAD));
// Atomically update accessor cache.
synchronized(
cache) {
if (
cache[
cacheIndex] == null) {
cache[
cacheIndex] =
mh;
} else {
// Throw away newly constructed accessor and use cached version.
mh =
cache[
cacheIndex];
}
}
return
mh;
}
static final class
ArrayAccessor {
/// Support for array element access
static final int
GETTER_INDEX = 0,
SETTER_INDEX = 1,
INDEX_LIMIT = 2;
static final
ClassValue<
MethodHandle[]>
TYPED_ACCESSORS
= new
ClassValue<
MethodHandle[]>() {
@
Override
protected
MethodHandle[]
computeValue(
Class<?>
type) {
return new
MethodHandle[
INDEX_LIMIT];
}
};
static final
MethodHandle OBJECT_ARRAY_GETTER,
OBJECT_ARRAY_SETTER;
static {
MethodHandle[]
cache =
TYPED_ACCESSORS.
get(
Object[].class);
cache[
GETTER_INDEX] =
OBJECT_ARRAY_GETTER =
makeIntrinsic(
getAccessor(
Object[].class, false),
Intrinsic.
ARRAY_LOAD);
cache[
SETTER_INDEX] =
OBJECT_ARRAY_SETTER =
makeIntrinsic(
getAccessor(
Object[].class, true),
Intrinsic.
ARRAY_STORE);
assert(
InvokerBytecodeGenerator.
isStaticallyInvocable(
ArrayAccessor.
OBJECT_ARRAY_GETTER.
internalMemberName()));
assert(
InvokerBytecodeGenerator.
isStaticallyInvocable(
ArrayAccessor.
OBJECT_ARRAY_SETTER.
internalMemberName()));
}
static int
getElementI(int[]
a, int
i) { return
a[
i]; }
static long
getElementJ(long[]
a, int
i) { return
a[
i]; }
static float
getElementF(float[]
a, int
i) { return
a[
i]; }
static double
getElementD(double[]
a, int
i) { return
a[
i]; }
static boolean
getElementZ(boolean[]
a, int
i) { return
a[
i]; }
static byte
getElementB(byte[]
a, int
i) { return
a[
i]; }
static short
getElementS(short[]
a, int
i) { return
a[
i]; }
static char
getElementC(char[]
a, int
i) { return
a[
i]; }
static
Object getElementL(
Object[]
a, int
i) { return
a[
i]; }
static void
setElementI(int[]
a, int
i, int
x) {
a[
i] =
x; }
static void
setElementJ(long[]
a, int
i, long
x) {
a[
i] =
x; }
static void
setElementF(float[]
a, int
i, float
x) {
a[
i] =
x; }
static void
setElementD(double[]
a, int
i, double
x) {
a[
i] =
x; }
static void
setElementZ(boolean[]
a, int
i, boolean
x) {
a[
i] =
x; }
static void
setElementB(byte[]
a, int
i, byte
x) {
a[
i] =
x; }
static void
setElementS(short[]
a, int
i, short
x) {
a[
i] =
x; }
static void
setElementC(char[]
a, int
i, char
x) {
a[
i] =
x; }
static void
setElementL(
Object[]
a, int
i,
Object x) {
a[
i] =
x; }
static
String name(
Class<?>
arrayClass, boolean
isSetter) {
Class<?>
elemClass =
arrayClass.
getComponentType();
if (
elemClass == null) throw
newIllegalArgumentException("not an array",
arrayClass);
return (!
isSetter ? "getElement" : "setElement") +
Wrapper.
basicTypeChar(
elemClass);
}
static
MethodType type(
Class<?>
arrayClass, boolean
isSetter) {
Class<?>
elemClass =
arrayClass.
getComponentType();
Class<?>
arrayArgClass =
arrayClass;
if (!
elemClass.
isPrimitive()) {
arrayArgClass =
Object[].class;
elemClass =
Object.class;
}
return !
isSetter ?
MethodType.
methodType(
elemClass,
arrayArgClass, int.class) :
MethodType.
methodType(void.class,
arrayArgClass, int.class,
elemClass);
}
static
MethodType correctType(
Class<?>
arrayClass, boolean
isSetter) {
Class<?>
elemClass =
arrayClass.
getComponentType();
return !
isSetter ?
MethodType.
methodType(
elemClass,
arrayClass, int.class) :
MethodType.
methodType(void.class,
arrayClass, int.class,
elemClass);
}
static
MethodHandle getAccessor(
Class<?>
arrayClass, boolean
isSetter) {
String name =
name(
arrayClass,
isSetter);
MethodType type =
type(
arrayClass,
isSetter);
try {
return
IMPL_LOOKUP.
findStatic(
ArrayAccessor.class,
name,
type);
} catch (
ReflectiveOperationException ex) {
throw
uncaughtException(
ex);
}
}
}
/**
* Create a JVM-level adapter method handle to conform the given method
* handle to the similar newType, using only pairwise argument conversions.
* For each argument, convert incoming argument to the exact type needed.
* The argument conversions allowed are casting, boxing and unboxing,
* integral widening or narrowing, and floating point widening or narrowing.
* @param srcType required call type
* @param target original method handle
* @param strict if true, only asType conversions are allowed; if false, explicitCastArguments conversions allowed
* @param monobox if true, unboxing conversions are assumed to be exactly typed (Integer to int only, not long or double)
* @return an adapter to the original handle with the desired new type,
* or the original target if the types are already identical
* or null if the adaptation cannot be made
*/
static
MethodHandle makePairwiseConvert(
MethodHandle target,
MethodType srcType,
boolean
strict, boolean
monobox) {
MethodType dstType =
target.
type();
if (
srcType ==
dstType)
return
target;
return
makePairwiseConvertByEditor(
target,
srcType,
strict,
monobox);
}
private static int
countNonNull(
Object[]
array) {
int
count = 0;
for (
Object x :
array) {
if (
x != null) ++
count;
}
return
count;
}
static
MethodHandle makePairwiseConvertByEditor(
MethodHandle target,
MethodType srcType,
boolean
strict, boolean
monobox) {
Object[]
convSpecs =
computeValueConversions(
srcType,
target.
type(),
strict,
monobox);
int
convCount =
countNonNull(
convSpecs);
if (
convCount == 0)
return
target.
viewAsType(
srcType,
strict);
MethodType basicSrcType =
srcType.
basicType();
MethodType midType =
target.
type().
basicType();
BoundMethodHandle mh =
target.
rebind();
// FIXME: Reduce number of bindings when there is more than one Class conversion.
// FIXME: Reduce number of bindings when there are repeated conversions.
for (int
i = 0;
i <
convSpecs.length-1;
i++) {
Object convSpec =
convSpecs[
i];
if (
convSpec == null) continue;
MethodHandle fn;
if (
convSpec instanceof
Class) {
fn =
Lazy.
MH_castReference.
bindTo(
convSpec);
} else {
fn = (
MethodHandle)
convSpec;
}
Class<?>
newType =
basicSrcType.
parameterType(
i);
if (--
convCount == 0)
midType =
srcType;
else
midType =
midType.
changeParameterType(
i,
newType);
LambdaForm form2 =
mh.
editor().
filterArgumentForm(1+
i,
BasicType.
basicType(
newType));
mh =
mh.
copyWithExtendL(
midType,
form2,
fn);
mh =
mh.
rebind();
}
Object convSpec =
convSpecs[
convSpecs.length-1];
if (
convSpec != null) {
MethodHandle fn;
if (
convSpec instanceof
Class) {
if (
convSpec == void.class)
fn = null;
else
fn =
Lazy.
MH_castReference.
bindTo(
convSpec);
} else {
fn = (
MethodHandle)
convSpec;
}
Class<?>
newType =
basicSrcType.
returnType();
assert(--
convCount == 0);
midType =
srcType;
if (
fn != null) {
mh =
mh.
rebind(); // rebind if too complex
LambdaForm form2 =
mh.
editor().
filterReturnForm(
BasicType.
basicType(
newType), false);
mh =
mh.
copyWithExtendL(
midType,
form2,
fn);
} else {
LambdaForm form2 =
mh.
editor().
filterReturnForm(
BasicType.
basicType(
newType), true);
mh =
mh.
copyWith(
midType,
form2);
}
}
assert(
convCount == 0);
assert(
mh.
type().
equals(
srcType));
return
mh;
}
static
MethodHandle makePairwiseConvertIndirect(
MethodHandle target,
MethodType srcType,
boolean
strict, boolean
monobox) {
assert(
target.
type().
parameterCount() ==
srcType.
parameterCount());
// Calculate extra arguments (temporaries) required in the names array.
Object[]
convSpecs =
computeValueConversions(
srcType,
target.
type(),
strict,
monobox);
final int
INARG_COUNT =
srcType.
parameterCount();
int
convCount =
countNonNull(
convSpecs);
boolean
retConv = (
convSpecs[
INARG_COUNT] != null);
boolean
retVoid =
srcType.
returnType() == void.class;
if (
retConv &&
retVoid) {
convCount -= 1;
retConv = false;
}
final int
IN_MH = 0;
final int
INARG_BASE = 1;
final int
INARG_LIMIT =
INARG_BASE +
INARG_COUNT;
final int
NAME_LIMIT =
INARG_LIMIT +
convCount + 1;
final int
RETURN_CONV = (!
retConv ? -1 :
NAME_LIMIT - 1);
final int
OUT_CALL = (!
retConv ?
NAME_LIMIT :
RETURN_CONV) - 1;
final int
RESULT = (
retVoid ? -1 :
NAME_LIMIT - 1);
// Now build a LambdaForm.
MethodType lambdaType =
srcType.
basicType().
invokerType();
Name[]
names =
arguments(
NAME_LIMIT -
INARG_LIMIT,
lambdaType);
// Collect the arguments to the outgoing call, maybe with conversions:
final int
OUTARG_BASE = 0; // target MH is Name.function, name Name.arguments[0]
Object[]
outArgs = new
Object[
OUTARG_BASE +
INARG_COUNT];
int
nameCursor =
INARG_LIMIT;
for (int
i = 0;
i <
INARG_COUNT;
i++) {
Object convSpec =
convSpecs[
i];
if (
convSpec == null) {
// do nothing: difference is trivial
outArgs[
OUTARG_BASE +
i] =
names[
INARG_BASE +
i];
continue;
}
Name conv;
if (
convSpec instanceof
Class) {
Class<?>
convClass = (
Class<?>)
convSpec;
conv = new
Name(
Lazy.
MH_castReference,
convClass,
names[
INARG_BASE +
i]);
} else {
MethodHandle fn = (
MethodHandle)
convSpec;
conv = new
Name(
fn,
names[
INARG_BASE +
i]);
}
assert(
names[
nameCursor] == null);
names[
nameCursor++] =
conv;
assert(
outArgs[
OUTARG_BASE +
i] == null);
outArgs[
OUTARG_BASE +
i] =
conv;
}
// Build argument array for the call.
assert(
nameCursor ==
OUT_CALL);
names[
OUT_CALL] = new
Name(
target,
outArgs);
Object convSpec =
convSpecs[
INARG_COUNT];
if (!
retConv) {
assert(
OUT_CALL ==
names.length-1);
} else {
Name conv;
if (
convSpec == void.class) {
conv = new
Name(
LambdaForm.
constantZero(
BasicType.
basicType(
srcType.
returnType())));
} else if (
convSpec instanceof
Class) {
Class<?>
convClass = (
Class<?>)
convSpec;
conv = new
Name(
Lazy.
MH_castReference,
convClass,
names[
OUT_CALL]);
} else {
MethodHandle fn = (
MethodHandle)
convSpec;
if (
fn.
type().
parameterCount() == 0)
conv = new
Name(
fn); // don't pass retval to void conversion
else
conv = new
Name(
fn,
names[
OUT_CALL]);
}
assert(
names[
RETURN_CONV] == null);
names[
RETURN_CONV] =
conv;
assert(
RETURN_CONV ==
names.length-1);
}
LambdaForm form = new
LambdaForm("convert",
lambdaType.
parameterCount(),
names,
RESULT);
return
SimpleMethodHandle.
make(
srcType,
form);
}
/**
* Identity function, with reference cast.
* @param t an arbitrary reference type
* @param x an arbitrary reference value
* @return the same value x
*/
@
ForceInline
@
SuppressWarnings("unchecked")
static <T,U> T
castReference(
Class<? extends T>
t, U
x) {
// inlined Class.cast because we can't ForceInline it
if (
x != null && !
t.
isInstance(
x))
throw
newClassCastException(
t,
x);
return (T)
x;
}
private static
ClassCastException newClassCastException(
Class<?>
t,
Object obj) {
return new
ClassCastException("Cannot cast " +
obj.
getClass().
getName() + " to " +
t.
getName());
}
static
Object[]
computeValueConversions(
MethodType srcType,
MethodType dstType,
boolean
strict, boolean
monobox) {
final int
INARG_COUNT =
srcType.
parameterCount();
Object[]
convSpecs = new
Object[
INARG_COUNT+1];
for (int
i = 0;
i <=
INARG_COUNT;
i++) {
boolean
isRet = (
i ==
INARG_COUNT);
Class<?>
src =
isRet ?
dstType.
returnType() :
srcType.
parameterType(
i);
Class<?>
dst =
isRet ?
srcType.
returnType() :
dstType.
parameterType(
i);
if (!
VerifyType.
isNullConversion(
src,
dst, /*keepInterfaces=*/
strict)) {
convSpecs[
i] =
valueConversion(
src,
dst,
strict,
monobox);
}
}
return
convSpecs;
}
static
MethodHandle makePairwiseConvert(
MethodHandle target,
MethodType srcType,
boolean
strict) {
return
makePairwiseConvert(
target,
srcType,
strict, /*monobox=*/ false);
}
/**
* Find a conversion function from the given source to the given destination.
* This conversion function will be used as a LF NamedFunction.
* Return a Class object if a simple cast is needed.
* Return void.class if void is involved.
*/
static
Object valueConversion(
Class<?>
src,
Class<?>
dst, boolean
strict, boolean
monobox) {
assert(!
VerifyType.
isNullConversion(
src,
dst, /*keepInterfaces=*/
strict)); // caller responsibility
if (
dst == void.class)
return
dst;
MethodHandle fn;
if (
src.
isPrimitive()) {
if (
src == void.class) {
return void.class; // caller must recognize this specially
} else if (
dst.
isPrimitive()) {
// Examples: int->byte, byte->int, boolean->int (!strict)
fn =
ValueConversions.
convertPrimitive(
src,
dst);
} else {
// Examples: int->Integer, boolean->Object, float->Number
Wrapper wsrc =
Wrapper.
forPrimitiveType(
src);
fn =
ValueConversions.
boxExact(
wsrc);
assert(
fn.
type().
parameterType(0) ==
wsrc.
primitiveType());
assert(
fn.
type().
returnType() ==
wsrc.
wrapperType());
if (!
VerifyType.
isNullConversion(
wsrc.
wrapperType(),
dst,
strict)) {
// Corner case, such as int->Long, which will probably fail.
MethodType mt =
MethodType.
methodType(
dst,
src);
if (
strict)
fn =
fn.
asType(
mt);
else
fn =
MethodHandleImpl.
makePairwiseConvert(
fn,
mt, /*strict=*/ false);
}
}
} else if (
dst.
isPrimitive()) {
Wrapper wdst =
Wrapper.
forPrimitiveType(
dst);
if (
monobox ||
src ==
wdst.
wrapperType()) {
// Use a strongly-typed unboxer, if possible.
fn =
ValueConversions.
unboxExact(
wdst,
strict);
} else {
// Examples: Object->int, Number->int, Comparable->int, Byte->int
// must include additional conversions
// src must be examined at runtime, to detect Byte, Character, etc.
fn = (
strict
?
ValueConversions.
unboxWiden(
wdst)
:
ValueConversions.
unboxCast(
wdst));
}
} else {
// Simple reference conversion.
// Note: Do not check for a class hierarchy relation
// between src and dst. In all cases a 'null' argument
// will pass the cast conversion.
return
dst;
}
assert(
fn.
type().
parameterCount() <= 1) : "pc"+
Arrays.
asList(
src.
getSimpleName(),
dst.
getSimpleName(),
fn);
return
fn;
}
static
MethodHandle makeVarargsCollector(
MethodHandle target,
Class<?>
arrayType) {
MethodType type =
target.
type();
int
last =
type.
parameterCount() - 1;
if (
type.
parameterType(
last) !=
arrayType)
target =
target.
asType(
type.
changeParameterType(
last,
arrayType));
target =
target.
asFixedArity(); // make sure this attribute is turned off
return new
AsVarargsCollector(
target,
arrayType);
}
private static final class
AsVarargsCollector extends
DelegatingMethodHandle {
private final
MethodHandle target;
private final
Class<?>
arrayType;
private @
Stable MethodHandle asCollectorCache;
AsVarargsCollector(
MethodHandle target,
Class<?>
arrayType) {
this(
target.
type(),
target,
arrayType);
}
AsVarargsCollector(
MethodType type,
MethodHandle target,
Class<?>
arrayType) {
super(
type,
target);
this.
target =
target;
this.
arrayType =
arrayType;
this.
asCollectorCache =
target.
asCollector(
arrayType, 0);
}
@
Override
public boolean
isVarargsCollector() {
return true;
}
@
Override
protected
MethodHandle getTarget() {
return
target;
}
@
Override
public
MethodHandle asFixedArity() {
return
target;
}
@
Override
MethodHandle setVarargs(
MemberName member) {
if (
member.
isVarargs()) return this;
return
asFixedArity();
}
@
Override
public
MethodHandle asTypeUncached(
MethodType newType) {
MethodType type = this.
type();
int
collectArg =
type.
parameterCount() - 1;
int
newArity =
newType.
parameterCount();
if (
newArity ==
collectArg+1 &&
type.
parameterType(
collectArg).
isAssignableFrom(
newType.
parameterType(
collectArg))) {
// if arity and trailing parameter are compatible, do normal thing
return
asTypeCache =
asFixedArity().
asType(
newType);
}
// check cache
MethodHandle acc =
asCollectorCache;
if (
acc != null &&
acc.
type().
parameterCount() ==
newArity)
return
asTypeCache =
acc.
asType(
newType);
// build and cache a collector
int
arrayLength =
newArity -
collectArg;
MethodHandle collector;
try {
collector =
asFixedArity().
asCollector(
arrayType,
arrayLength);
assert(
collector.
type().
parameterCount() ==
newArity) : "newArity="+
newArity+" but collector="+
collector;
} catch (
IllegalArgumentException ex) {
throw new
WrongMethodTypeException("cannot build collector",
ex);
}
asCollectorCache =
collector;
return
asTypeCache =
collector.
asType(
newType);
}
@
Override
boolean
viewAsTypeChecks(
MethodType newType, boolean
strict) {
super.viewAsTypeChecks(
newType, true);
if (
strict) return true;
// extra assertion for non-strict checks:
assert (
type().
lastParameterType().
getComponentType()
.
isAssignableFrom(
newType.
lastParameterType().
getComponentType()))
:
Arrays.
asList(this,
newType);
return true;
}
}
/** Factory method: Spread selected argument. */
static
MethodHandle makeSpreadArguments(
MethodHandle target,
Class<?>
spreadArgType, int
spreadArgPos, int
spreadArgCount) {
MethodType targetType =
target.
type();
for (int
i = 0;
i <
spreadArgCount;
i++) {
Class<?>
arg =
VerifyType.
spreadArgElementType(
spreadArgType,
i);
if (
arg == null)
arg =
Object.class;
targetType =
targetType.
changeParameterType(
spreadArgPos +
i,
arg);
}
target =
target.
asType(
targetType);
MethodType srcType =
targetType
.
replaceParameterTypes(
spreadArgPos,
spreadArgPos +
spreadArgCount,
spreadArgType);
// Now build a LambdaForm.
MethodType lambdaType =
srcType.
invokerType();
Name[]
names =
arguments(
spreadArgCount + 2,
lambdaType);
int
nameCursor =
lambdaType.
parameterCount();
int[]
indexes = new int[
targetType.
parameterCount()];
for (int
i = 0,
argIndex = 1;
i <
targetType.
parameterCount() + 1;
i++,
argIndex++) {
Class<?>
src =
lambdaType.
parameterType(
i);
if (
i ==
spreadArgPos) {
// Spread the array.
MethodHandle aload =
MethodHandles.
arrayElementGetter(
spreadArgType);
Name array =
names[
argIndex];
names[
nameCursor++] = new
Name(
Lazy.
NF_checkSpreadArgument,
array,
spreadArgCount);
for (int
j = 0;
j <
spreadArgCount;
i++,
j++) {
indexes[
i] =
nameCursor;
names[
nameCursor++] = new
Name(
aload,
array,
j);
}
} else if (
i <
indexes.length) {
indexes[
i] =
argIndex;
}
}
assert(
nameCursor ==
names.length-1); // leave room for the final call
// Build argument array for the call.
Name[]
targetArgs = new
Name[
targetType.
parameterCount()];
for (int
i = 0;
i <
targetType.
parameterCount();
i++) {
int
idx =
indexes[
i];
targetArgs[
i] =
names[
idx];
}
names[
names.length - 1] = new
Name(
target, (
Object[])
targetArgs);
LambdaForm form = new
LambdaForm("spread",
lambdaType.
parameterCount(),
names);
return
SimpleMethodHandle.
make(
srcType,
form);
}
static void
checkSpreadArgument(
Object av, int
n) {
if (
av == null) {
if (
n == 0) return;
} else if (
av instanceof
Object[]) {
int
len = ((
Object[])
av).length;
if (
len ==
n) return;
} else {
int
len = java.lang.reflect.
Array.
getLength(
av);
if (
len ==
n) return;
}
// fall through to error:
throw
newIllegalArgumentException("array is not of length "+
n);
}
/**
* Pre-initialized NamedFunctions for bootstrapping purposes.
* Factored in an inner class to delay initialization until first usage.
*/
static class
Lazy {
private static final
Class<?>
MHI =
MethodHandleImpl.class;
private static final
MethodHandle[]
ARRAYS;
private static final
MethodHandle[]
FILL_ARRAYS;
static final
NamedFunction NF_checkSpreadArgument;
static final
NamedFunction NF_guardWithCatch;
static final
NamedFunction NF_throwException;
static final
NamedFunction NF_profileBoolean;
static final
MethodHandle MH_castReference;
static final
MethodHandle MH_selectAlternative;
static final
MethodHandle MH_copyAsPrimitiveArray;
static final
MethodHandle MH_fillNewTypedArray;
static final
MethodHandle MH_fillNewArray;
static final
MethodHandle MH_arrayIdentity;
static {
ARRAYS =
makeArrays();
FILL_ARRAYS =
makeFillArrays();
try {
NF_checkSpreadArgument = new
NamedFunction(
MHI.
getDeclaredMethod("checkSpreadArgument",
Object.class, int.class));
NF_guardWithCatch = new
NamedFunction(
MHI.
getDeclaredMethod("guardWithCatch",
MethodHandle.class,
Class.class,
MethodHandle.class,
Object[].class));
NF_throwException = new
NamedFunction(
MHI.
getDeclaredMethod("throwException",
Throwable.class));
NF_profileBoolean = new
NamedFunction(
MHI.
getDeclaredMethod("profileBoolean", boolean.class, int[].class));
NF_checkSpreadArgument.
resolve();
NF_guardWithCatch.
resolve();
NF_throwException.
resolve();
NF_profileBoolean.
resolve();
MH_castReference =
IMPL_LOOKUP.
findStatic(
MHI, "castReference",
MethodType.
methodType(
Object.class,
Class.class,
Object.class));
MH_copyAsPrimitiveArray =
IMPL_LOOKUP.
findStatic(
MHI, "copyAsPrimitiveArray",
MethodType.
methodType(
Object.class,
Wrapper.class,
Object[].class));
MH_arrayIdentity =
IMPL_LOOKUP.
findStatic(
MHI, "identity",
MethodType.
methodType(
Object[].class,
Object[].class));
MH_fillNewArray =
IMPL_LOOKUP.
findStatic(
MHI, "fillNewArray",
MethodType.
methodType(
Object[].class,
Integer.class,
Object[].class));
MH_fillNewTypedArray =
IMPL_LOOKUP.
findStatic(
MHI, "fillNewTypedArray",
MethodType.
methodType(
Object[].class,
Object[].class,
Integer.class,
Object[].class));
MH_selectAlternative =
makeIntrinsic(
IMPL_LOOKUP.
findStatic(
MHI, "selectAlternative",
MethodType.
methodType(
MethodHandle.class, boolean.class,
MethodHandle.class,
MethodHandle.class)),
Intrinsic.
SELECT_ALTERNATIVE);
} catch (
ReflectiveOperationException ex) {
throw
newInternalError(
ex);
}
}
}
/** Factory method: Collect or filter selected argument(s). */
static
MethodHandle makeCollectArguments(
MethodHandle target,
MethodHandle collector, int
collectArgPos, boolean
retainOriginalArgs) {
MethodType targetType =
target.
type(); // (a..., c, [b...])=>r
MethodType collectorType =
collector.
type(); // (b...)=>c
int
collectArgCount =
collectorType.
parameterCount();
Class<?>
collectValType =
collectorType.
returnType();
int
collectValCount = (
collectValType == void.class ? 0 : 1);
MethodType srcType =
targetType // (a..., [b...])=>r
.
dropParameterTypes(
collectArgPos,
collectArgPos+
collectValCount);
if (!
retainOriginalArgs) { // (a..., b...)=>r
srcType =
srcType.
insertParameterTypes(
collectArgPos,
collectorType.
parameterList());
}
// in arglist: [0: ...keep1 | cpos: collect... | cpos+cacount: keep2... ]
// out arglist: [0: ...keep1 | cpos: collectVal? | cpos+cvcount: keep2... ]
// out(retain): [0: ...keep1 | cpos: cV? coll... | cpos+cvc+cac: keep2... ]
// Now build a LambdaForm.
MethodType lambdaType =
srcType.
invokerType();
Name[]
names =
arguments(2,
lambdaType);
final int
collectNamePos =
names.length - 2;
final int
targetNamePos =
names.length - 1;
Name[]
collectorArgs =
Arrays.
copyOfRange(
names, 1 +
collectArgPos, 1 +
collectArgPos +
collectArgCount);
names[
collectNamePos] = new
Name(
collector, (
Object[])
collectorArgs);
// Build argument array for the target.
// Incoming LF args to copy are: [ (mh) headArgs collectArgs tailArgs ].
// Output argument array is [ headArgs (collectVal)? (collectArgs)? tailArgs ].
Name[]
targetArgs = new
Name[
targetType.
parameterCount()];
int
inputArgPos = 1; // incoming LF args to copy to target
int
targetArgPos = 0; // fill pointer for targetArgs
int
chunk =
collectArgPos; // |headArgs|
System.
arraycopy(
names,
inputArgPos,
targetArgs,
targetArgPos,
chunk);
inputArgPos +=
chunk;
targetArgPos +=
chunk;
if (
collectValType != void.class) {
targetArgs[
targetArgPos++] =
names[
collectNamePos];
}
chunk =
collectArgCount;
if (
retainOriginalArgs) {
System.
arraycopy(
names,
inputArgPos,
targetArgs,
targetArgPos,
chunk);
targetArgPos +=
chunk; // optionally pass on the collected chunk
}
inputArgPos +=
chunk;
chunk =
targetArgs.length -
targetArgPos; // all the rest
System.
arraycopy(
names,
inputArgPos,
targetArgs,
targetArgPos,
chunk);
assert(
inputArgPos +
chunk ==
collectNamePos); // use of rest of input args also
names[
targetNamePos] = new
Name(
target, (
Object[])
targetArgs);
LambdaForm form = new
LambdaForm("collect",
lambdaType.
parameterCount(),
names);
return
SimpleMethodHandle.
make(
srcType,
form);
}
@
LambdaForm.
Hidden
static
MethodHandle selectAlternative(boolean
testResult,
MethodHandle target,
MethodHandle fallback) {
if (
testResult) {
return
target;
} else {
return
fallback;
}
}
// Intrinsified by C2. Counters are used during parsing to calculate branch frequencies.
@
LambdaForm.
Hidden
static
boolean
profileBoolean(boolean
result, int[]
counters) {
// Profile is int[2] where [0] and [1] correspond to false and true occurrences respectively.
int
idx =
result ? 1 : 0;
try {
counters[
idx] =
Math.
addExact(
counters[
idx], 1);
} catch (
ArithmeticException e) {
// Avoid continuous overflow by halving the problematic count.
counters[
idx] =
counters[
idx] / 2;
}
return
result;
}
static
MethodHandle makeGuardWithTest(
MethodHandle test,
MethodHandle target,
MethodHandle fallback) {
MethodType type =
target.
type();
assert(
test.
type().
equals(
type.
changeReturnType(boolean.class)) &&
fallback.
type().
equals(
type));
MethodType basicType =
type.
basicType();
LambdaForm form =
makeGuardWithTestForm(
basicType);
BoundMethodHandle mh;
try {
if (
PROFILE_GWT) {
int[]
counts = new int[2];
mh = (
BoundMethodHandle)
BoundMethodHandle.
speciesData_LLLL().
constructor().
invokeBasic(
type,
form,
(
Object)
test, (
Object)
profile(
target), (
Object)
profile(
fallback),
counts);
} else {
mh = (
BoundMethodHandle)
BoundMethodHandle.
speciesData_LLL().
constructor().
invokeBasic(
type,
form,
(
Object)
test, (
Object)
profile(
target), (
Object)
profile(
fallback));
}
} catch (
Throwable ex) {
throw
uncaughtException(
ex);
}
assert(
mh.
type() ==
type);
return
mh;
}
static
MethodHandle profile(
MethodHandle target) {
if (
DONT_INLINE_THRESHOLD >= 0) {
return
makeBlockInlningWrapper(
target);
} else {
return
target;
}
}
/**
* Block inlining during JIT-compilation of a target method handle if it hasn't been invoked enough times.
* Corresponding LambdaForm has @DontInline when compiled into bytecode.
*/
static
MethodHandle makeBlockInlningWrapper(
MethodHandle target) {
LambdaForm lform =
PRODUCE_BLOCK_INLINING_FORM.
apply(
target);
return new
CountingWrapper(
target,
lform,
PRODUCE_BLOCK_INLINING_FORM,
PRODUCE_REINVOKER_FORM,
DONT_INLINE_THRESHOLD);
}
/** Constructs reinvoker lambda form which block inlining during JIT-compilation for a particular method handle */
private static final
Function<
MethodHandle,
LambdaForm>
PRODUCE_BLOCK_INLINING_FORM = new
Function<
MethodHandle,
LambdaForm>() {
@
Override
public
LambdaForm apply(
MethodHandle target) {
return
DelegatingMethodHandle.
makeReinvokerForm(
target,
MethodTypeForm.
LF_DELEGATE_BLOCK_INLINING,
CountingWrapper.class, "reinvoker.dontInline", false,
DelegatingMethodHandle.
NF_getTarget,
CountingWrapper.
NF_maybeStopCounting);
}
};
/** Constructs simple reinvoker lambda form for a particular method handle */
private static final
Function<
MethodHandle,
LambdaForm>
PRODUCE_REINVOKER_FORM = new
Function<
MethodHandle,
LambdaForm>() {
@
Override
public
LambdaForm apply(
MethodHandle target) {
return
DelegatingMethodHandle.
makeReinvokerForm(
target,
MethodTypeForm.
LF_DELEGATE,
DelegatingMethodHandle.class,
DelegatingMethodHandle.
NF_getTarget);
}
};
/**
* Counting method handle. It has 2 states: counting and non-counting.
* It is in counting state for the first n invocations and then transitions to non-counting state.
* Behavior in counting and non-counting states is determined by lambda forms produced by
* countingFormProducer & nonCountingFormProducer respectively.
*/
static class
CountingWrapper extends
DelegatingMethodHandle {
private final
MethodHandle target;
private int
count;
private
Function<
MethodHandle,
LambdaForm>
countingFormProducer;
private
Function<
MethodHandle,
LambdaForm>
nonCountingFormProducer;
private volatile boolean
isCounting;
private
CountingWrapper(
MethodHandle target,
LambdaForm lform,
Function<
MethodHandle,
LambdaForm>
countingFromProducer,
Function<
MethodHandle,
LambdaForm>
nonCountingFormProducer,
int
count) {
super(
target.
type(),
lform);
this.
target =
target;
this.
count =
count;
this.
countingFormProducer =
countingFromProducer;
this.
nonCountingFormProducer =
nonCountingFormProducer;
this.
isCounting = (
count > 0);
}
@
Hidden
@
Override
protected
MethodHandle getTarget() {
return
target;
}
@
Override
public
MethodHandle asTypeUncached(
MethodType newType) {
MethodHandle newTarget =
target.
asType(
newType);
MethodHandle wrapper;
if (
isCounting) {
LambdaForm lform;
lform =
countingFormProducer.
apply(
newTarget);
wrapper = new
CountingWrapper(
newTarget,
lform,
countingFormProducer,
nonCountingFormProducer,
DONT_INLINE_THRESHOLD);
} else {
wrapper =
newTarget; // no need for a counting wrapper anymore
}
return (
asTypeCache =
wrapper);
}
boolean
countDown() {
if (
count <= 0) {
// Try to limit number of updates. MethodHandle.updateForm() doesn't guarantee LF update visibility.
if (
isCounting) {
isCounting = false;
return true;
} else {
return false;
}
} else {
--
count;
return false;
}
}
@
Hidden
static void
maybeStopCounting(
Object o1) {
CountingWrapper wrapper = (
CountingWrapper)
o1;
if (
wrapper.
countDown()) {
// Reached invocation threshold. Replace counting behavior with a non-counting one.
LambdaForm lform =
wrapper.
nonCountingFormProducer.
apply(
wrapper.
target);
lform.
compileToBytecode(); // speed up warmup by avoiding LF interpretation again after transition
wrapper.
updateForm(
lform);
}
}
static final
NamedFunction NF_maybeStopCounting;
static {
Class<?>
THIS_CLASS =
CountingWrapper.class;
try {
NF_maybeStopCounting = new
NamedFunction(
THIS_CLASS.
getDeclaredMethod("maybeStopCounting",
Object.class));
} catch (
ReflectiveOperationException ex) {
throw
newInternalError(
ex);
}
}
}
static
LambdaForm makeGuardWithTestForm(
MethodType basicType) {
LambdaForm lform =
basicType.
form().
cachedLambdaForm(
MethodTypeForm.
LF_GWT);
if (
lform != null) return
lform;
final int
THIS_MH = 0; // the BMH_LLL
final int
ARG_BASE = 1; // start of incoming arguments
final int
ARG_LIMIT =
ARG_BASE +
basicType.
parameterCount();
int
nameCursor =
ARG_LIMIT;
final int
GET_TEST =
nameCursor++;
final int
GET_TARGET =
nameCursor++;
final int
GET_FALLBACK =
nameCursor++;
final int
GET_COUNTERS =
PROFILE_GWT ?
nameCursor++ : -1;
final int
CALL_TEST =
nameCursor++;
final int
PROFILE = (
GET_COUNTERS != -1) ?
nameCursor++ : -1;
final int
TEST =
nameCursor-1; // previous statement: either PROFILE or CALL_TEST
final int
SELECT_ALT =
nameCursor++;
final int
CALL_TARGET =
nameCursor++;
assert(
CALL_TARGET ==
SELECT_ALT+1); // must be true to trigger IBG.emitSelectAlternative
MethodType lambdaType =
basicType.
invokerType();
Name[]
names =
arguments(
nameCursor -
ARG_LIMIT,
lambdaType);
BoundMethodHandle.
SpeciesData data =
(
GET_COUNTERS != -1) ?
BoundMethodHandle.
speciesData_LLLL()
:
BoundMethodHandle.
speciesData_LLL();
names[
THIS_MH] =
names[
THIS_MH].
withConstraint(
data);
names[
GET_TEST] = new
Name(
data.
getterFunction(0),
names[
THIS_MH]);
names[
GET_TARGET] = new
Name(
data.
getterFunction(1),
names[
THIS_MH]);
names[
GET_FALLBACK] = new
Name(
data.
getterFunction(2),
names[
THIS_MH]);
if (
GET_COUNTERS != -1) {
names[
GET_COUNTERS] = new
Name(
data.
getterFunction(3),
names[
THIS_MH]);
}
Object[]
invokeArgs =
Arrays.
copyOfRange(
names, 0,
ARG_LIMIT,
Object[].class);
// call test
MethodType testType =
basicType.
changeReturnType(boolean.class).
basicType();
invokeArgs[0] =
names[
GET_TEST];
names[
CALL_TEST] = new
Name(
testType,
invokeArgs);
// profile branch
if (
PROFILE != -1) {
names[
PROFILE] = new
Name(
Lazy.
NF_profileBoolean,
names[
CALL_TEST],
names[
GET_COUNTERS]);
}
// call selectAlternative
names[
SELECT_ALT] = new
Name(
Lazy.
MH_selectAlternative,
names[
TEST],
names[
GET_TARGET],
names[
GET_FALLBACK]);
// call target or fallback
invokeArgs[0] =
names[
SELECT_ALT];
names[
CALL_TARGET] = new
Name(
basicType,
invokeArgs);
lform = new
LambdaForm("guard",
lambdaType.
parameterCount(),
names, /*forceInline=*/true);
return
basicType.
form().
setCachedLambdaForm(
MethodTypeForm.
LF_GWT,
lform);
}
/**
* The LambaForm shape for catchException combinator is the following:
* <blockquote><pre>{@code
* guardWithCatch=Lambda(a0:L,a1:L,a2:L)=>{
* t3:L=BoundMethodHandle$Species_LLLLL.argL0(a0:L);
* t4:L=BoundMethodHandle$Species_LLLLL.argL1(a0:L);
* t5:L=BoundMethodHandle$Species_LLLLL.argL2(a0:L);
* t6:L=BoundMethodHandle$Species_LLLLL.argL3(a0:L);
* t7:L=BoundMethodHandle$Species_LLLLL.argL4(a0:L);
* t8:L=MethodHandle.invokeBasic(t6:L,a1:L,a2:L);
* t9:L=MethodHandleImpl.guardWithCatch(t3:L,t4:L,t5:L,t8:L);
* t10:I=MethodHandle.invokeBasic(t7:L,t9:L);t10:I}
* }</pre></blockquote>
*
* argL0 and argL2 are target and catcher method handles. argL1 is exception class.
* argL3 and argL4 are auxiliary method handles: argL3 boxes arguments and wraps them into Object[]
* (ValueConversions.array()) and argL4 unboxes result if necessary (ValueConversions.unbox()).
*
* Having t8 and t10 passed outside and not hardcoded into a lambda form allows to share lambda forms
* among catchException combinators with the same basic type.
*/
private static
LambdaForm makeGuardWithCatchForm(
MethodType basicType) {
MethodType lambdaType =
basicType.
invokerType();
LambdaForm lform =
basicType.
form().
cachedLambdaForm(
MethodTypeForm.
LF_GWC);
if (
lform != null) {
return
lform;
}
final int
THIS_MH = 0; // the BMH_LLLLL
final int
ARG_BASE = 1; // start of incoming arguments
final int
ARG_LIMIT =
ARG_BASE +
basicType.
parameterCount();
int
nameCursor =
ARG_LIMIT;
final int
GET_TARGET =
nameCursor++;
final int
GET_CLASS =
nameCursor++;
final int
GET_CATCHER =
nameCursor++;
final int
GET_COLLECT_ARGS =
nameCursor++;
final int
GET_UNBOX_RESULT =
nameCursor++;
final int
BOXED_ARGS =
nameCursor++;
final int
TRY_CATCH =
nameCursor++;
final int
UNBOX_RESULT =
nameCursor++;
Name[]
names =
arguments(
nameCursor -
ARG_LIMIT,
lambdaType);
BoundMethodHandle.
SpeciesData data =
BoundMethodHandle.
speciesData_LLLLL();
names[
THIS_MH] =
names[
THIS_MH].
withConstraint(
data);
names[
GET_TARGET] = new
Name(
data.
getterFunction(0),
names[
THIS_MH]);
names[
GET_CLASS] = new
Name(
data.
getterFunction(1),
names[
THIS_MH]);
names[
GET_CATCHER] = new
Name(
data.
getterFunction(2),
names[
THIS_MH]);
names[
GET_COLLECT_ARGS] = new
Name(
data.
getterFunction(3),
names[
THIS_MH]);
names[
GET_UNBOX_RESULT] = new
Name(
data.
getterFunction(4),
names[
THIS_MH]);
// FIXME: rework argument boxing/result unboxing logic for LF interpretation
// t_{i}:L=MethodHandle.invokeBasic(collectArgs:L,a1:L,...);
MethodType collectArgsType =
basicType.
changeReturnType(
Object.class);
MethodHandle invokeBasic =
MethodHandles.
basicInvoker(
collectArgsType);
Object[]
args = new
Object[
invokeBasic.
type().
parameterCount()];
args[0] =
names[
GET_COLLECT_ARGS];
System.
arraycopy(
names,
ARG_BASE,
args, 1,
ARG_LIMIT-
ARG_BASE);
names[
BOXED_ARGS] = new
Name(
makeIntrinsic(
invokeBasic,
Intrinsic.
GUARD_WITH_CATCH),
args);
// t_{i+1}:L=MethodHandleImpl.guardWithCatch(target:L,exType:L,catcher:L,t_{i}:L);
Object[]
gwcArgs = new
Object[] {
names[
GET_TARGET],
names[
GET_CLASS],
names[
GET_CATCHER],
names[
BOXED_ARGS]};
names[
TRY_CATCH] = new
Name(
Lazy.
NF_guardWithCatch,
gwcArgs);
// t_{i+2}:I=MethodHandle.invokeBasic(unbox:L,t_{i+1}:L);
MethodHandle invokeBasicUnbox =
MethodHandles.
basicInvoker(
MethodType.
methodType(
basicType.
rtype(),
Object.class));
Object[]
unboxArgs = new
Object[] {
names[
GET_UNBOX_RESULT],
names[
TRY_CATCH]};
names[
UNBOX_RESULT] = new
Name(
invokeBasicUnbox,
unboxArgs);
lform = new
LambdaForm("guardWithCatch",
lambdaType.
parameterCount(),
names);
return
basicType.
form().
setCachedLambdaForm(
MethodTypeForm.
LF_GWC,
lform);
}
static
MethodHandle makeGuardWithCatch(
MethodHandle target,
Class<? extends
Throwable>
exType,
MethodHandle catcher) {
MethodType type =
target.
type();
LambdaForm form =
makeGuardWithCatchForm(
type.
basicType());
// Prepare auxiliary method handles used during LambdaForm interpreation.
// Box arguments and wrap them into Object[]: ValueConversions.array().
MethodType varargsType =
type.
changeReturnType(
Object[].class);
MethodHandle collectArgs =
varargsArray(
type.
parameterCount()).
asType(
varargsType);
// Result unboxing: ValueConversions.unbox() OR ValueConversions.identity() OR ValueConversions.ignore().
MethodHandle unboxResult;
Class<?>
rtype =
type.
returnType();
if (
rtype.
isPrimitive()) {
if (
rtype == void.class) {
unboxResult =
ValueConversions.
ignore();
} else {
Wrapper w =
Wrapper.
forPrimitiveType(
type.
returnType());
unboxResult =
ValueConversions.
unboxExact(
w);
}
} else {
unboxResult =
MethodHandles.
identity(
Object.class);
}
BoundMethodHandle.
SpeciesData data =
BoundMethodHandle.
speciesData_LLLLL();
BoundMethodHandle mh;
try {
mh = (
BoundMethodHandle)
data.
constructor().
invokeBasic(
type,
form, (
Object)
target, (
Object)
exType, (
Object)
catcher,
(
Object)
collectArgs, (
Object)
unboxResult);
} catch (
Throwable ex) {
throw
uncaughtException(
ex);
}
assert(
mh.
type() ==
type);
return
mh;
}
/**
* Intrinsified during LambdaForm compilation
* (see {@link InvokerBytecodeGenerator#emitGuardWithCatch emitGuardWithCatch}).
*/
@
LambdaForm.
Hidden
static
Object guardWithCatch(
MethodHandle target,
Class<? extends
Throwable>
exType,
MethodHandle catcher,
Object...
av) throws
Throwable {
// Use asFixedArity() to avoid unnecessary boxing of last argument for VarargsCollector case.
try {
return
target.
asFixedArity().
invokeWithArguments(
av);
} catch (
Throwable t) {
if (!
exType.
isInstance(
t)) throw
t;
return
catcher.
asFixedArity().
invokeWithArguments(
prepend(
t,
av));
}
}
/** Prepend an element {@code elem} to an {@code array}. */
@
LambdaForm.
Hidden
private static
Object[]
prepend(
Object elem,
Object[]
array) {
Object[]
newArray = new
Object[
array.length+1];
newArray[0] =
elem;
System.
arraycopy(
array, 0,
newArray, 1,
array.length);
return
newArray;
}
static
MethodHandle throwException(
MethodType type) {
assert(
Throwable.class.
isAssignableFrom(
type.
parameterType(0)));
int
arity =
type.
parameterCount();
if (
arity > 1) {
MethodHandle mh =
throwException(
type.
dropParameterTypes(1,
arity));
mh =
MethodHandles.
dropArguments(
mh, 1,
type.
parameterList().
subList(1,
arity));
return
mh;
}
return
makePairwiseConvert(
Lazy.
NF_throwException.
resolvedHandle(),
type, false, true);
}
static <T extends
Throwable>
Empty throwException(T
t) throws T { throw
t; }
static
MethodHandle[]
FAKE_METHOD_HANDLE_INVOKE = new
MethodHandle[2];
static
MethodHandle fakeMethodHandleInvoke(
MemberName method) {
int
idx;
assert(
method.
isMethodHandleInvoke());
switch (
method.
getName()) {
case "invoke":
idx = 0; break;
case "invokeExact":
idx = 1; break;
default: throw new
InternalError(
method.
getName());
}
MethodHandle mh =
FAKE_METHOD_HANDLE_INVOKE[
idx];
if (
mh != null) return
mh;
MethodType type =
MethodType.
methodType(
Object.class,
UnsupportedOperationException.class,
MethodHandle.class,
Object[].class);
mh =
throwException(
type);
mh =
mh.
bindTo(new
UnsupportedOperationException("cannot reflectively invoke MethodHandle"));
if (!
method.
getInvocationType().
equals(
mh.
type()))
throw new
InternalError(
method.
toString());
mh =
mh.
withInternalMemberName(
method, false);
mh =
mh.
asVarargsCollector(
Object[].class);
assert(
method.
isVarargs());
FAKE_METHOD_HANDLE_INVOKE[
idx] =
mh;
return
mh;
}
/**
* Create an alias for the method handle which, when called,
* appears to be called from the same class loader and protection domain
* as hostClass.
* This is an expensive no-op unless the method which is called
* is sensitive to its caller. A small number of system methods
* are in this category, including Class.forName and Method.invoke.
*/
static
MethodHandle bindCaller(
MethodHandle mh,
Class<?>
hostClass) {
return
BindCaller.
bindCaller(
mh,
hostClass);
}
// Put the whole mess into its own nested class.
// That way we can lazily load the code and set up the constants.
private static class
BindCaller {
static
MethodHandle bindCaller(
MethodHandle mh,
Class<?>
hostClass) {
// Do not use this function to inject calls into system classes.
if (
hostClass == null
|| (
hostClass.
isArray() ||
hostClass.
isPrimitive() ||
hostClass.
getName().
startsWith("java.") ||
hostClass.
getName().
startsWith("sun."))) {
throw new
InternalError(); // does not happen, and should not anyway
}
// For simplicity, convert mh to a varargs-like method.
MethodHandle vamh =
prepareForInvoker(
mh);
// Cache the result of makeInjectedInvoker once per argument class.
MethodHandle bccInvoker =
CV_makeInjectedInvoker.
get(
hostClass);
return
restoreToType(
bccInvoker.
bindTo(
vamh),
mh,
hostClass);
}
private static
MethodHandle makeInjectedInvoker(
Class<?>
hostClass) {
Class<?>
bcc =
UNSAFE.
defineAnonymousClass(
hostClass,
T_BYTES, null);
if (
hostClass.
getClassLoader() !=
bcc.
getClassLoader())
throw new
InternalError(
hostClass.
getName()+" (CL)");
try {
if (
hostClass.
getProtectionDomain() !=
bcc.
getProtectionDomain())
throw new
InternalError(
hostClass.
getName()+" (PD)");
} catch (
SecurityException ex) {
// Self-check was blocked by security manager. This is OK.
// In fact the whole try body could be turned into an assertion.
}
try {
MethodHandle init =
IMPL_LOOKUP.
findStatic(
bcc, "init",
MethodType.
methodType(void.class));
init.
invokeExact(); // force initialization of the class
} catch (
Throwable ex) {
throw
uncaughtException(
ex);
}
MethodHandle bccInvoker;
try {
MethodType invokerMT =
MethodType.
methodType(
Object.class,
MethodHandle.class,
Object[].class);
bccInvoker =
IMPL_LOOKUP.
findStatic(
bcc, "invoke_V",
invokerMT);
} catch (
ReflectiveOperationException ex) {
throw
uncaughtException(
ex);
}
// Test the invoker, to ensure that it really injects into the right place.
try {
MethodHandle vamh =
prepareForInvoker(
MH_checkCallerClass);
Object ok =
bccInvoker.
invokeExact(
vamh, new
Object[]{
hostClass,
bcc});
} catch (
Throwable ex) {
throw new
InternalError(
ex);
}
return
bccInvoker;
}
private static
ClassValue<
MethodHandle>
CV_makeInjectedInvoker = new
ClassValue<
MethodHandle>() {
@
Override protected
MethodHandle computeValue(
Class<?>
hostClass) {
return
makeInjectedInvoker(
hostClass);
}
};
// Adapt mh so that it can be called directly from an injected invoker:
private static
MethodHandle prepareForInvoker(
MethodHandle mh) {
mh =
mh.
asFixedArity();
MethodType mt =
mh.
type();
int
arity =
mt.
parameterCount();
MethodHandle vamh =
mh.
asType(
mt.
generic());
vamh.
internalForm().
compileToBytecode(); // eliminate LFI stack frames
vamh =
vamh.
asSpreader(
Object[].class,
arity);
vamh.
internalForm().
compileToBytecode(); // eliminate LFI stack frames
return
vamh;
}
// Undo the adapter effect of prepareForInvoker:
private static
MethodHandle restoreToType(
MethodHandle vamh,
MethodHandle original,
Class<?>
hostClass) {
MethodType type =
original.
type();
MethodHandle mh =
vamh.
asCollector(
Object[].class,
type.
parameterCount());
MemberName member =
original.
internalMemberName();
mh =
mh.
asType(
type);
mh = new
WrappedMember(
mh,
type,
member,
original.
isInvokeSpecial(),
hostClass);
return
mh;
}
private static final
MethodHandle MH_checkCallerClass;
static {
final
Class<?>
THIS_CLASS =
BindCaller.class;
assert(
checkCallerClass(
THIS_CLASS,
THIS_CLASS));
try {
MH_checkCallerClass =
IMPL_LOOKUP
.
findStatic(
THIS_CLASS, "checkCallerClass",
MethodType.
methodType(boolean.class,
Class.class,
Class.class));
assert((boolean)
MH_checkCallerClass.
invokeExact(
THIS_CLASS,
THIS_CLASS));
} catch (
Throwable ex) {
throw new
InternalError(
ex);
}
}
@
CallerSensitive
private static boolean
checkCallerClass(
Class<?>
expected,
Class<?>
expected2) {
// This method is called via MH_checkCallerClass and so it's
// correct to ask for the immediate caller here.
Class<?>
actual =
Reflection.
getCallerClass();
if (
actual !=
expected &&
actual !=
expected2)
throw new
InternalError("found "+
actual.
getName()+", expected "+
expected.
getName()
+(
expected ==
expected2 ? "" : ", or else "+
expected2.
getName()));
return true;
}
private static final byte[]
T_BYTES;
static {
final
Object[]
values = {null};
AccessController.
doPrivileged(new
PrivilegedAction<
Void>() {
public
Void run() {
try {
Class<
T>
tClass =
T.class;
String tName =
tClass.
getName();
String tResource =
tName.
substring(
tName.
lastIndexOf('.')+1)+".class";
java.net.
URLConnection uconn =
tClass.
getResource(
tResource).
openConnection();
int
len =
uconn.
getContentLength();
byte[]
bytes = new byte[
len];
try (java.io.
InputStream str =
uconn.
getInputStream()) {
int
nr =
str.
read(
bytes);
if (
nr !=
len) throw new java.io.
IOException(
tResource);
}
values[0] =
bytes;
} catch (java.io.
IOException ex) {
throw new
InternalError(
ex);
}
return null;
}
});
T_BYTES = (byte[])
values[0];
}
// The following class is used as a template for Unsafe.defineAnonymousClass:
private static class
T {
static void
init() { } // side effect: initializes this class
static
Object invoke_V(
MethodHandle vamh,
Object[]
args) throws
Throwable {
return
vamh.
invokeExact(
args);
}
}
}
/** This subclass allows a wrapped method handle to be re-associated with an arbitrary member name. */
private static final class
WrappedMember extends
DelegatingMethodHandle {
private final
MethodHandle target;
private final
MemberName member;
private final
Class<?>
callerClass;
private final boolean
isInvokeSpecial;
private
WrappedMember(
MethodHandle target,
MethodType type,
MemberName member, boolean
isInvokeSpecial,
Class<?>
callerClass) {
super(
type,
target);
this.
target =
target;
this.
member =
member;
this.
callerClass =
callerClass;
this.
isInvokeSpecial =
isInvokeSpecial;
}
@
Override
MemberName internalMemberName() {
return
member;
}
@
Override
Class<?>
internalCallerClass() {
return
callerClass;
}
@
Override
boolean
isInvokeSpecial() {
return
isInvokeSpecial;
}
@
Override
protected
MethodHandle getTarget() {
return
target;
}
@
Override
public
MethodHandle asTypeUncached(
MethodType newType) {
// This MH is an alias for target, except for the MemberName
// Drop the MemberName if there is any conversion.
return
asTypeCache =
target.
asType(
newType);
}
}
static
MethodHandle makeWrappedMember(
MethodHandle target,
MemberName member, boolean
isInvokeSpecial) {
if (
member.
equals(
target.
internalMemberName()) &&
isInvokeSpecial ==
target.
isInvokeSpecial())
return
target;
return new
WrappedMember(
target,
target.
type(),
member,
isInvokeSpecial, null);
}
/** Intrinsic IDs */
/*non-public*/
enum
Intrinsic {
SELECT_ALTERNATIVE,
GUARD_WITH_CATCH,
NEW_ARRAY,
ARRAY_LOAD,
ARRAY_STORE,
IDENTITY,
ZERO,
NONE // no intrinsic associated
}
/** Mark arbitrary method handle as intrinsic.
* InvokerBytecodeGenerator uses this info to produce more efficient bytecode shape. */
private static final class
IntrinsicMethodHandle extends
DelegatingMethodHandle {
private final
MethodHandle target;
private final
Intrinsic intrinsicName;
IntrinsicMethodHandle(
MethodHandle target,
Intrinsic intrinsicName) {
super(
target.
type(),
target);
this.
target =
target;
this.
intrinsicName =
intrinsicName;
}
@
Override
protected
MethodHandle getTarget() {
return
target;
}
@
Override
Intrinsic intrinsicName() {
return
intrinsicName;
}
@
Override
public
MethodHandle asTypeUncached(
MethodType newType) {
// This MH is an alias for target, except for the intrinsic name
// Drop the name if there is any conversion.
return
asTypeCache =
target.
asType(
newType);
}
@
Override
String internalProperties() {
return super.internalProperties() +
"\n& Intrinsic="+
intrinsicName;
}
@
Override
public
MethodHandle asCollector(
Class<?>
arrayType, int
arrayLength) {
if (
intrinsicName ==
Intrinsic.
IDENTITY) {
MethodType resultType =
type().
asCollectorType(
arrayType,
arrayLength);
MethodHandle newArray =
MethodHandleImpl.
varargsArray(
arrayType,
arrayLength);
return
newArray.
asType(
resultType);
}
return super.asCollector(
arrayType,
arrayLength);
}
}
static
MethodHandle makeIntrinsic(
MethodHandle target,
Intrinsic intrinsicName) {
if (
intrinsicName ==
target.
intrinsicName())
return
target;
return new
IntrinsicMethodHandle(
target,
intrinsicName);
}
static
MethodHandle makeIntrinsic(
MethodType type,
LambdaForm form,
Intrinsic intrinsicName) {
return new
IntrinsicMethodHandle(
SimpleMethodHandle.
make(
type,
form),
intrinsicName);
}
/// Collection of multiple arguments.
private static
MethodHandle findCollector(
String name, int
nargs,
Class<?>
rtype,
Class<?>...
ptypes) {
MethodType type =
MethodType.
genericMethodType(
nargs)
.
changeReturnType(
rtype)
.
insertParameterTypes(0,
ptypes);
try {
return
IMPL_LOOKUP.
findStatic(
MethodHandleImpl.class,
name,
type);
} catch (
ReflectiveOperationException ex) {
return null;
}
}
private static final
Object[]
NO_ARGS_ARRAY = {};
private static
Object[]
makeArray(
Object...
args) { return
args; }
private static
Object[]
array() { return
NO_ARGS_ARRAY; }
private static
Object[]
array(
Object a0)
{ return
makeArray(
a0); }
private static
Object[]
array(
Object a0,
Object a1)
{ return
makeArray(
a0,
a1); }
private static
Object[]
array(
Object a0,
Object a1,
Object a2)
{ return
makeArray(
a0,
a1,
a2); }
private static
Object[]
array(
Object a0,
Object a1,
Object a2,
Object a3)
{ return
makeArray(
a0,
a1,
a2,
a3); }
private static
Object[]
array(
Object a0,
Object a1,
Object a2,
Object a3,
Object a4)
{ return
makeArray(
a0,
a1,
a2,
a3,
a4); }
private static
Object[]
array(
Object a0,
Object a1,
Object a2,
Object a3,
Object a4,
Object a5)
{ return
makeArray(
a0,
a1,
a2,
a3,
a4,
a5); }
private static
Object[]
array(
Object a0,
Object a1,
Object a2,
Object a3,
Object a4,
Object a5,
Object a6)
{ return
makeArray(
a0,
a1,
a2,
a3,
a4,
a5,
a6); }
private static
Object[]
array(
Object a0,
Object a1,
Object a2,
Object a3,
Object a4,
Object a5,
Object a6,
Object a7)
{ return
makeArray(
a0,
a1,
a2,
a3,
a4,
a5,
a6,
a7); }
private static
Object[]
array(
Object a0,
Object a1,
Object a2,
Object a3,
Object a4,
Object a5,
Object a6,
Object a7,
Object a8)
{ return
makeArray(
a0,
a1,
a2,
a3,
a4,
a5,
a6,
a7,
a8); }
private static
Object[]
array(
Object a0,
Object a1,
Object a2,
Object a3,
Object a4,
Object a5,
Object a6,
Object a7,
Object a8,
Object a9)
{ return
makeArray(
a0,
a1,
a2,
a3,
a4,
a5,
a6,
a7,
a8,
a9); }
private static
MethodHandle[]
makeArrays() {
ArrayList<
MethodHandle>
mhs = new
ArrayList<>();
for (;;) {
MethodHandle mh =
findCollector("array",
mhs.
size(),
Object[].class);
if (
mh == null) break;
mh =
makeIntrinsic(
mh,
Intrinsic.
NEW_ARRAY);
mhs.
add(
mh);
}
assert(
mhs.
size() == 11); // current number of methods
return
mhs.
toArray(new
MethodHandle[
MAX_ARITY+1]);
}
// filling versions of the above:
// using Integer len instead of int len and no varargs to avoid bootstrapping problems
private static
Object[]
fillNewArray(
Integer len,
Object[] /*not ...*/
args) {
Object[]
a = new
Object[
len];
fillWithArguments(
a, 0,
args);
return
a;
}
private static
Object[]
fillNewTypedArray(
Object[]
example,
Integer len,
Object[] /*not ...*/
args) {
Object[]
a =
Arrays.
copyOf(
example,
len);
assert(
a.
getClass() !=
Object[].class);
fillWithArguments(
a, 0,
args);
return
a;
}
private static void
fillWithArguments(
Object[]
a, int
pos,
Object...
args) {
System.
arraycopy(
args, 0,
a,
pos,
args.length);
}
// using Integer pos instead of int pos to avoid bootstrapping problems
private static
Object[]
fillArray(
Integer pos,
Object[]
a,
Object a0)
{
fillWithArguments(
a,
pos,
a0); return
a; }
private static
Object[]
fillArray(
Integer pos,
Object[]
a,
Object a0,
Object a1)
{
fillWithArguments(
a,
pos,
a0,
a1); return
a; }
private static
Object[]
fillArray(
Integer pos,
Object[]
a,
Object a0,
Object a1,
Object a2)
{
fillWithArguments(
a,
pos,
a0,
a1,
a2); return
a; }
private static
Object[]
fillArray(
Integer pos,
Object[]
a,
Object a0,
Object a1,
Object a2,
Object a3)
{
fillWithArguments(
a,
pos,
a0,
a1,
a2,
a3); return
a; }
private static
Object[]
fillArray(
Integer pos,
Object[]
a,
Object a0,
Object a1,
Object a2,
Object a3,
Object a4)
{
fillWithArguments(
a,
pos,
a0,
a1,
a2,
a3,
a4); return
a; }
private static
Object[]
fillArray(
Integer pos,
Object[]
a,
Object a0,
Object a1,
Object a2,
Object a3,
Object a4,
Object a5)
{
fillWithArguments(
a,
pos,
a0,
a1,
a2,
a3,
a4,
a5); return
a; }
private static
Object[]
fillArray(
Integer pos,
Object[]
a,
Object a0,
Object a1,
Object a2,
Object a3,
Object a4,
Object a5,
Object a6)
{
fillWithArguments(
a,
pos,
a0,
a1,
a2,
a3,
a4,
a5,
a6); return
a; }
private static
Object[]
fillArray(
Integer pos,
Object[]
a,
Object a0,
Object a1,
Object a2,
Object a3,
Object a4,
Object a5,
Object a6,
Object a7)
{
fillWithArguments(
a,
pos,
a0,
a1,
a2,
a3,
a4,
a5,
a6,
a7); return
a; }
private static
Object[]
fillArray(
Integer pos,
Object[]
a,
Object a0,
Object a1,
Object a2,
Object a3,
Object a4,
Object a5,
Object a6,
Object a7,
Object a8)
{
fillWithArguments(
a,
pos,
a0,
a1,
a2,
a3,
a4,
a5,
a6,
a7,
a8); return
a; }
private static
Object[]
fillArray(
Integer pos,
Object[]
a,
Object a0,
Object a1,
Object a2,
Object a3,
Object a4,
Object a5,
Object a6,
Object a7,
Object a8,
Object a9)
{
fillWithArguments(
a,
pos,
a0,
a1,
a2,
a3,
a4,
a5,
a6,
a7,
a8,
a9); return
a; }
private static final int
FILL_ARRAYS_COUNT = 11; // current number of fillArray methods
private static
MethodHandle[]
makeFillArrays() {
ArrayList<
MethodHandle>
mhs = new
ArrayList<>();
mhs.
add(null); // there is no empty fill; at least a0 is required
for (;;) {
MethodHandle mh =
findCollector("fillArray",
mhs.
size(),
Object[].class,
Integer.class,
Object[].class);
if (
mh == null) break;
mhs.
add(
mh);
}
assert(
mhs.
size() ==
FILL_ARRAYS_COUNT);
return
mhs.
toArray(new
MethodHandle[0]);
}
private static
Object copyAsPrimitiveArray(
Wrapper w,
Object...
boxes) {
Object a =
w.
makeArray(
boxes.length);
w.
copyArrayUnboxing(
boxes, 0,
a, 0,
boxes.length);
return
a;
}
/** Return a method handle that takes the indicated number of Object
* arguments and returns an Object array of them, as if for varargs.
*/
static
MethodHandle varargsArray(int
nargs) {
MethodHandle mh =
Lazy.
ARRAYS[
nargs];
if (
mh != null) return
mh;
mh =
findCollector("array",
nargs,
Object[].class);
if (
mh != null)
mh =
makeIntrinsic(
mh,
Intrinsic.
NEW_ARRAY);
if (
mh != null) return
Lazy.
ARRAYS[
nargs] =
mh;
mh =
buildVarargsArray(
Lazy.
MH_fillNewArray,
Lazy.
MH_arrayIdentity,
nargs);
assert(
assertCorrectArity(
mh,
nargs));
mh =
makeIntrinsic(
mh,
Intrinsic.
NEW_ARRAY);
return
Lazy.
ARRAYS[
nargs] =
mh;
}
private static boolean
assertCorrectArity(
MethodHandle mh, int
arity) {
assert(
mh.
type().
parameterCount() ==
arity) : "arity != "+
arity+": "+
mh;
return true;
}
// Array identity function (used as Lazy.MH_arrayIdentity).
static <T> T[]
identity(T[]
x) {
return
x;
}
private static
MethodHandle buildVarargsArray(
MethodHandle newArray,
MethodHandle finisher, int
nargs) {
// Build up the result mh as a sequence of fills like this:
// finisher(fill(fill(newArrayWA(23,x1..x10),10,x11..x20),20,x21..x23))
// The various fill(_,10*I,___*[J]) are reusable.
int
leftLen =
Math.
min(
nargs,
LEFT_ARGS); // absorb some arguments immediately
int
rightLen =
nargs -
leftLen;
MethodHandle leftCollector =
newArray.
bindTo(
nargs);
leftCollector =
leftCollector.
asCollector(
Object[].class,
leftLen);
MethodHandle mh =
finisher;
if (
rightLen > 0) {
MethodHandle rightFiller =
fillToRight(
LEFT_ARGS +
rightLen);
if (
mh ==
Lazy.
MH_arrayIdentity)
mh =
rightFiller;
else
mh =
MethodHandles.
collectArguments(
mh, 0,
rightFiller);
}
if (
mh ==
Lazy.
MH_arrayIdentity)
mh =
leftCollector;
else
mh =
MethodHandles.
collectArguments(
mh, 0,
leftCollector);
return
mh;
}
private static final int
LEFT_ARGS =
FILL_ARRAYS_COUNT - 1;
private static final
MethodHandle[]
FILL_ARRAY_TO_RIGHT = new
MethodHandle[
MAX_ARITY+1];
/** fill_array_to_right(N).invoke(a, argL..arg[N-1])
* fills a[L]..a[N-1] with corresponding arguments,
* and then returns a. The value L is a global constant (LEFT_ARGS).
*/
private static
MethodHandle fillToRight(int
nargs) {
MethodHandle filler =
FILL_ARRAY_TO_RIGHT[
nargs];
if (
filler != null) return
filler;
filler =
buildFiller(
nargs);
assert(
assertCorrectArity(
filler,
nargs -
LEFT_ARGS + 1));
return
FILL_ARRAY_TO_RIGHT[
nargs] =
filler;
}
private static
MethodHandle buildFiller(int
nargs) {
if (
nargs <=
LEFT_ARGS)
return
Lazy.
MH_arrayIdentity; // no args to fill; return the array unchanged
// we need room for both mh and a in mh.invoke(a, arg*[nargs])
final int
CHUNK =
LEFT_ARGS;
int
rightLen =
nargs %
CHUNK;
int
midLen =
nargs -
rightLen;
if (
rightLen == 0) {
midLen =
nargs - (
rightLen =
CHUNK);
if (
FILL_ARRAY_TO_RIGHT[
midLen] == null) {
// build some precursors from left to right
for (int
j =
LEFT_ARGS %
CHUNK;
j <
midLen;
j +=
CHUNK)
if (
j >
LEFT_ARGS)
fillToRight(
j);
}
}
if (
midLen <
LEFT_ARGS)
rightLen =
nargs - (
midLen =
LEFT_ARGS);
assert(
rightLen > 0);
MethodHandle midFill =
fillToRight(
midLen); // recursive fill
MethodHandle rightFill =
Lazy.
FILL_ARRAYS[
rightLen].
bindTo(
midLen); // [midLen..nargs-1]
assert(
midFill.
type().
parameterCount() == 1 +
midLen -
LEFT_ARGS);
assert(
rightFill.
type().
parameterCount() == 1 +
rightLen);
// Combine the two fills:
// right(mid(a, x10..x19), x20..x23)
// The final product will look like this:
// right(mid(newArrayLeft(24, x0..x9), x10..x19), x20..x23)
if (
midLen ==
LEFT_ARGS)
return
rightFill;
else
return
MethodHandles.
collectArguments(
rightFill, 0,
midFill);
}
// Type-polymorphic version of varargs maker.
private static final
ClassValue<
MethodHandle[]>
TYPED_COLLECTORS
= new
ClassValue<
MethodHandle[]>() {
@
Override
protected
MethodHandle[]
computeValue(
Class<?>
type) {
return new
MethodHandle[256];
}
};
static final int
MAX_JVM_ARITY = 255; // limit imposed by the JVM
/** Return a method handle that takes the indicated number of
* typed arguments and returns an array of them.
* The type argument is the array type.
*/
static
MethodHandle varargsArray(
Class<?>
arrayType, int
nargs) {
Class<?>
elemType =
arrayType.
getComponentType();
if (
elemType == null) throw new
IllegalArgumentException("not an array: "+
arrayType);
// FIXME: Need more special casing and caching here.
if (
nargs >=
MAX_JVM_ARITY/2 - 1) {
int
slots =
nargs;
final int
MAX_ARRAY_SLOTS =
MAX_JVM_ARITY - 1; // 1 for receiver MH
if (
slots <=
MAX_ARRAY_SLOTS &&
elemType.
isPrimitive())
slots *=
Wrapper.
forPrimitiveType(
elemType).
stackSlots();
if (
slots >
MAX_ARRAY_SLOTS)
throw new
IllegalArgumentException("too many arguments: "+
arrayType.
getSimpleName()+", length "+
nargs);
}
if (
elemType ==
Object.class)
return
varargsArray(
nargs);
// other cases: primitive arrays, subtypes of Object[]
MethodHandle cache[] =
TYPED_COLLECTORS.
get(
elemType);
MethodHandle mh =
nargs <
cache.length ?
cache[
nargs] : null;
if (
mh != null) return
mh;
if (
nargs == 0) {
Object example = java.lang.reflect.
Array.
newInstance(
arrayType.
getComponentType(), 0);
mh =
MethodHandles.
constant(
arrayType,
example);
} else if (
elemType.
isPrimitive()) {
MethodHandle builder =
Lazy.
MH_fillNewArray;
MethodHandle producer =
buildArrayProducer(
arrayType);
mh =
buildVarargsArray(
builder,
producer,
nargs);
} else {
Class<? extends
Object[]>
objArrayType =
arrayType.
asSubclass(
Object[].class);
Object[]
example =
Arrays.
copyOf(
NO_ARGS_ARRAY, 0,
objArrayType);
MethodHandle builder =
Lazy.
MH_fillNewTypedArray.
bindTo(
example);
MethodHandle producer =
Lazy.
MH_arrayIdentity; // must be weakly typed
mh =
buildVarargsArray(
builder,
producer,
nargs);
}
mh =
mh.
asType(
MethodType.
methodType(
arrayType,
Collections.<
Class<?>>
nCopies(
nargs,
elemType)));
mh =
makeIntrinsic(
mh,
Intrinsic.
NEW_ARRAY);
assert(
assertCorrectArity(
mh,
nargs));
if (
nargs <
cache.length)
cache[
nargs] =
mh;
return
mh;
}
private static
MethodHandle buildArrayProducer(
Class<?>
arrayType) {
Class<?>
elemType =
arrayType.
getComponentType();
assert(
elemType.
isPrimitive());
return
Lazy.
MH_copyAsPrimitiveArray.
bindTo(
Wrapper.
forPrimitiveType(
elemType));
}
/*non-public*/ static void
assertSame(
Object mh1,
Object mh2) {
if (
mh1 !=
mh2) {
String msg =
String.
format("mh1 != mh2: mh1 = %s (form: %s); mh2 = %s (form: %s)",
mh1, ((
MethodHandle)
mh1).
form,
mh2, ((
MethodHandle)
mh2).
form);
throw
newInternalError(
msg);
}
}
}