Add a new Lookup::hasFullPrivilegeAccess method and document the capabilities requiring full privileges Reviewed-by: alanb, plevart
575 lines
27 KiB
Java
575 lines
27 KiB
Java
/*
|
|
* Copyright (c) 2017, 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. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* 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.
|
|
*/
|
|
package java.lang.invoke;
|
|
|
|
import sun.invoke.util.Wrapper;
|
|
|
|
import java.lang.invoke.AbstractConstantGroup.BSCIWithCache;
|
|
import java.util.Arrays;
|
|
|
|
import static java.lang.invoke.BootstrapCallInfo.makeBootstrapCallInfo;
|
|
import static java.lang.invoke.ConstantGroup.makeConstantGroup;
|
|
import static java.lang.invoke.MethodHandleNatives.*;
|
|
import static java.lang.invoke.MethodHandleStatics.TRACE_METHOD_LINKAGE;
|
|
import static java.lang.invoke.MethodHandles.Lookup;
|
|
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
|
|
|
final class BootstrapMethodInvoker {
|
|
|
|
/**
|
|
* Factored code for invoking a bootstrap method for invokedynamic
|
|
* or a dynamic constant.
|
|
* @param resultType the expected return type (either CallSite or a constant type)
|
|
* @param bootstrapMethod the BSM to call
|
|
* @param name the method name or constant name
|
|
* @param type the method type or constant type
|
|
* @param info information passed up from the JVM, to derive static arguments
|
|
* @param callerClass the class containing the resolved method call or constant load
|
|
* @param <T> the expected return type
|
|
* @return the expected value, either a CallSite or a constant value
|
|
*/
|
|
static <T> T invoke(Class<T> resultType,
|
|
MethodHandle bootstrapMethod,
|
|
// Callee information:
|
|
String name, Object type,
|
|
// Extra arguments for BSM, if any:
|
|
Object info,
|
|
// Caller information:
|
|
Class<?> callerClass) {
|
|
MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass);
|
|
Object result;
|
|
boolean pullMode = isPullModeBSM(bootstrapMethod); // default value is false
|
|
boolean vmIsPushing = !staticArgumentsPulled(info); // default value is true
|
|
MethodHandle pullModeBSM;
|
|
// match the VM with the BSM
|
|
if (vmIsPushing) {
|
|
// VM is pushing arguments at us
|
|
pullModeBSM = null;
|
|
if (pullMode) {
|
|
bootstrapMethod = pushMePullYou(bootstrapMethod, true);
|
|
}
|
|
} else {
|
|
// VM wants us to pull args from it
|
|
pullModeBSM = pullMode ? bootstrapMethod :
|
|
pushMePullYou(bootstrapMethod, false);
|
|
bootstrapMethod = null;
|
|
}
|
|
try {
|
|
// As an optimization we special case various known BSMs,
|
|
// such as LambdaMetafactory::metafactory and
|
|
// StringConcatFactory::makeConcatWithConstants.
|
|
//
|
|
// By providing static type information or even invoking
|
|
// exactly, we avoid emitting code to perform runtime
|
|
// checking.
|
|
info = maybeReBox(info);
|
|
if (info == null) {
|
|
// VM is allowed to pass up a null meaning no BSM args
|
|
result = invoke(bootstrapMethod, caller, name, type);
|
|
}
|
|
else if (!info.getClass().isArray()) {
|
|
// VM is allowed to pass up a single BSM arg directly
|
|
|
|
// Call to StringConcatFactory::makeConcatWithConstants
|
|
// with empty constant arguments?
|
|
if (isStringConcatFactoryBSM(bootstrapMethod.type())) {
|
|
result = (CallSite)bootstrapMethod
|
|
.invokeExact(caller, name, (MethodType)type,
|
|
(String)info, new Object[0]);
|
|
} else {
|
|
result = invoke(bootstrapMethod, caller, name, type, info);
|
|
}
|
|
}
|
|
else if (info.getClass() == int[].class) {
|
|
// VM is allowed to pass up a pair {argc, index}
|
|
// referring to 'argc' BSM args at some place 'index'
|
|
// in the guts of the VM (associated with callerClass).
|
|
// The format of this index pair is private to the
|
|
// handshake between the VM and this class only.
|
|
// This supports "pulling" of arguments.
|
|
// The VM is allowed to do this for any reason.
|
|
// The code in this method makes up for any mismatches.
|
|
BootstrapCallInfo<Object> bsci
|
|
= new VM_BSCI<>(bootstrapMethod, name, type, caller, (int[])info);
|
|
// Pull-mode API is (Lookup, BootstrapCallInfo) -> Object
|
|
result = pullModeBSM.invoke(caller, bsci);
|
|
}
|
|
else {
|
|
// VM is allowed to pass up a full array of resolved BSM args
|
|
Object[] argv = (Object[]) info;
|
|
maybeReBoxElements(argv);
|
|
|
|
MethodType bsmType = bootstrapMethod.type();
|
|
if (isLambdaMetafactoryIndyBSM(bsmType) && argv.length == 3) {
|
|
result = (CallSite)bootstrapMethod
|
|
.invokeExact(caller, name, (MethodType)type, (MethodType)argv[0],
|
|
(MethodHandle)argv[1], (MethodType)argv[2]);
|
|
} else if (isLambdaMetafactoryCondyBSM(bsmType) && argv.length == 3) {
|
|
result = bootstrapMethod
|
|
.invokeExact(caller, name, (Class<?>)type, (MethodType)argv[0],
|
|
(MethodHandle)argv[1], (MethodType)argv[2]);
|
|
} else if (isStringConcatFactoryBSM(bsmType) && argv.length >= 1) {
|
|
String recipe = (String)argv[0];
|
|
Object[] shiftedArgs = Arrays.copyOfRange(argv, 1, argv.length);
|
|
result = (CallSite)bootstrapMethod.invokeExact(caller, name, (MethodType)type, recipe, shiftedArgs);
|
|
} else if (isLambdaMetafactoryAltMetafactoryBSM(bsmType)) {
|
|
result = (CallSite)bootstrapMethod.invokeExact(caller, name, (MethodType)type, argv);
|
|
} else {
|
|
switch (argv.length) {
|
|
case 0:
|
|
result = invoke(bootstrapMethod, caller, name, type);
|
|
break;
|
|
case 1:
|
|
result = invoke(bootstrapMethod, caller, name, type,
|
|
argv[0]);
|
|
break;
|
|
case 2:
|
|
result = invoke(bootstrapMethod, caller, name, type,
|
|
argv[0], argv[1]);
|
|
break;
|
|
case 3:
|
|
result = invoke(bootstrapMethod, caller, name, type,
|
|
argv[0], argv[1], argv[2]);
|
|
break;
|
|
case 4:
|
|
result = invoke(bootstrapMethod, caller, name, type,
|
|
argv[0], argv[1], argv[2], argv[3]);
|
|
break;
|
|
case 5:
|
|
result = invoke(bootstrapMethod, caller, name, type,
|
|
argv[0], argv[1], argv[2], argv[3], argv[4]);
|
|
break;
|
|
case 6:
|
|
result = invoke(bootstrapMethod, caller, name, type,
|
|
argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
|
|
break;
|
|
default:
|
|
result = invokeWithManyArguments(bootstrapMethod, caller, name, type, argv);
|
|
}
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
catch (Error e) {
|
|
// Pass through an Error, including BootstrapMethodError, any other
|
|
// form of linkage error, such as IllegalAccessError if the bootstrap
|
|
// method is inaccessible, or say ThreadDeath/OutOfMemoryError
|
|
// See the "Linking Exceptions" section for the invokedynamic
|
|
// instruction in JVMS 6.5.
|
|
throw e;
|
|
}
|
|
catch (Throwable ex) {
|
|
// Wrap anything else in BootstrapMethodError
|
|
throw new BootstrapMethodError("bootstrap method initialization exception", ex);
|
|
}
|
|
}
|
|
|
|
// If we don't provide static type information for type, we'll generate runtime
|
|
// checks. Let's try not to...
|
|
|
|
private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
|
|
String name, Object type) throws Throwable {
|
|
if (type instanceof Class) {
|
|
return bootstrapMethod.invoke(caller, name, (Class<?>)type);
|
|
} else {
|
|
return bootstrapMethod.invoke(caller, name, (MethodType)type);
|
|
}
|
|
}
|
|
|
|
private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
|
|
String name, Object type, Object arg0) throws Throwable {
|
|
if (type instanceof Class) {
|
|
return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0);
|
|
} else {
|
|
return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0);
|
|
}
|
|
}
|
|
|
|
private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
|
|
Object type, Object arg0, Object arg1) throws Throwable {
|
|
if (type instanceof Class) {
|
|
return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1);
|
|
} else {
|
|
return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1);
|
|
}
|
|
}
|
|
|
|
private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
|
|
Object type, Object arg0, Object arg1,
|
|
Object arg2) throws Throwable {
|
|
if (type instanceof Class) {
|
|
return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2);
|
|
} else {
|
|
return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2);
|
|
}
|
|
}
|
|
|
|
private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
|
|
Object type, Object arg0, Object arg1,
|
|
Object arg2, Object arg3) throws Throwable {
|
|
if (type instanceof Class) {
|
|
return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3);
|
|
} else {
|
|
return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3);
|
|
}
|
|
}
|
|
|
|
private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
|
|
String name, Object type, Object arg0, Object arg1,
|
|
Object arg2, Object arg3, Object arg4) throws Throwable {
|
|
if (type instanceof Class) {
|
|
return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3, arg4);
|
|
} else {
|
|
return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3, arg4);
|
|
}
|
|
}
|
|
|
|
private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
|
|
String name, Object type, Object arg0, Object arg1,
|
|
Object arg2, Object arg3, Object arg4, Object arg5) throws Throwable {
|
|
if (type instanceof Class) {
|
|
return bootstrapMethod.invoke(caller, name, (Class<?>)type, arg0, arg1, arg2, arg3, arg4, arg5);
|
|
} else {
|
|
return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3, arg4, arg5);
|
|
}
|
|
}
|
|
|
|
private static Object invokeWithManyArguments(MethodHandle bootstrapMethod, Lookup caller,
|
|
String name, Object type, Object[] argv) throws Throwable {
|
|
final int NON_SPREAD_ARG_COUNT = 3; // (caller, name, type)
|
|
final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT;
|
|
if (argv.length >= MAX_SAFE_SIZE) {
|
|
// to be on the safe side, use invokeWithArguments which handles jumbo lists
|
|
Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argv.length];
|
|
newargv[0] = caller;
|
|
newargv[1] = name;
|
|
newargv[2] = type;
|
|
System.arraycopy(argv, 0, newargv, NON_SPREAD_ARG_COUNT, argv.length);
|
|
return bootstrapMethod.invokeWithArguments(newargv);
|
|
} else {
|
|
MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
|
|
MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
|
|
MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
|
|
return spreader.invokeExact(typedBSM, (Object) caller, (Object) name, type, argv);
|
|
}
|
|
}
|
|
|
|
private static final MethodType LMF_INDY_MT = MethodType.methodType(CallSite.class,
|
|
Lookup.class, String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class);
|
|
|
|
private static final MethodType LMF_ALT_MT = MethodType.methodType(CallSite.class,
|
|
Lookup.class, String.class, MethodType.class, Object[].class);
|
|
|
|
private static final MethodType LMF_CONDY_MT = MethodType.methodType(Object.class,
|
|
Lookup.class, String.class, Class.class, MethodType.class, MethodHandle.class, MethodType.class);
|
|
|
|
private static final MethodType SCF_MT = MethodType.methodType(CallSite.class,
|
|
Lookup.class, String.class, MethodType.class, String.class, Object[].class);
|
|
|
|
/**
|
|
* @return true iff the BSM method type exactly matches
|
|
* {@see java.lang.invoke.StringConcatFactory#makeConcatWithConstants(MethodHandles.Lookup,
|
|
* String,MethodType,String,Object...))}
|
|
*/
|
|
private static boolean isStringConcatFactoryBSM(MethodType bsmType) {
|
|
return bsmType == SCF_MT;
|
|
}
|
|
|
|
/**
|
|
* @return true iff the BSM method type exactly matches
|
|
* {@see java.lang.invoke.LambdaMetafactory#metafactory(
|
|
* MethodHandles.Lookup,String,Class,MethodType,MethodHandle,MethodType)}
|
|
*/
|
|
private static boolean isLambdaMetafactoryCondyBSM(MethodType bsmType) {
|
|
return bsmType == LMF_CONDY_MT;
|
|
}
|
|
|
|
/**
|
|
* @return true iff the BSM method type exactly matches
|
|
* {@see java.lang.invoke.LambdaMetafactory#metafactory(
|
|
* MethodHandles.Lookup,String,MethodType,MethodType,MethodHandle,MethodType)}
|
|
*/
|
|
private static boolean isLambdaMetafactoryIndyBSM(MethodType bsmType) {
|
|
return bsmType == LMF_INDY_MT;
|
|
}
|
|
|
|
/**
|
|
* @return true iff the BSM method type exactly matches
|
|
* {@see java.lang.invoke.LambdaMetafactory#altMetafactory(
|
|
* MethodHandles.Lookup,String,MethodType,Object[])}
|
|
*/
|
|
private static boolean isLambdaMetafactoryAltMetafactoryBSM(MethodType bsmType) {
|
|
return bsmType == LMF_ALT_MT;
|
|
}
|
|
|
|
/** The JVM produces java.lang.Integer values to box
|
|
* CONSTANT_Integer boxes but does not intern them.
|
|
* Let's intern them. This is slightly wrong for
|
|
* a {@code CONSTANT_Dynamic} which produces an
|
|
* un-interned integer (e.g., {@code new Integer(0)}).
|
|
*/
|
|
private static Object maybeReBox(Object x) {
|
|
if (x instanceof Integer) {
|
|
int xi = (int) x;
|
|
if (xi == (byte) xi)
|
|
x = xi; // must rebox; see JLS 5.1.7
|
|
}
|
|
return x;
|
|
}
|
|
|
|
private static void maybeReBoxElements(Object[] xa) {
|
|
for (int i = 0; i < xa.length; i++) {
|
|
xa[i] = maybeReBox(xa[i]);
|
|
}
|
|
}
|
|
|
|
/** Canonical VM-aware implementation of BootstrapCallInfo.
|
|
* Knows how to dig into the JVM for lazily resolved (pull-mode) constants.
|
|
*/
|
|
private static final class VM_BSCI<T> extends BSCIWithCache<T> {
|
|
private final int[] indexInfo;
|
|
private final Class<?> caller; // for index resolution only
|
|
|
|
VM_BSCI(MethodHandle bsm, String name, T type,
|
|
Lookup lookup, int[] indexInfo) {
|
|
super(bsm, name, type, indexInfo[0]);
|
|
if (!lookup.hasFullPrivilegeAccess()) //D.I.D.
|
|
throw new AssertionError("bad Lookup object");
|
|
this.caller = lookup.lookupClass();
|
|
this.indexInfo = indexInfo;
|
|
// scoop up all the easy stuff right away:
|
|
prefetchIntoCache(0, size());
|
|
}
|
|
|
|
@Override Object fillCache(int i) {
|
|
Object[] buf = { null };
|
|
copyConstants(i, i+1, buf, 0);
|
|
Object res = wrapNull(buf[0]);
|
|
cache[i] = res;
|
|
int next = i + 1;
|
|
if (next < cache.length && cache[next] == null)
|
|
maybePrefetchIntoCache(next, false); // try to prefetch
|
|
return res;
|
|
}
|
|
|
|
@Override public int copyConstants(int start, int end,
|
|
Object[] buf, int pos) {
|
|
int i = start, bufi = pos;
|
|
while (i < end) {
|
|
Object x = cache[i];
|
|
if (x == null) break;
|
|
buf[bufi++] = unwrapNull(x);
|
|
i++;
|
|
}
|
|
// give up at first null and grab the rest in one big block
|
|
if (i >= end) return i;
|
|
Object[] temp = new Object[end - i];
|
|
if (TRACE_METHOD_LINKAGE) {
|
|
System.out.println("resolving more BSM arguments: " +
|
|
Arrays.asList(caller.getSimpleName(), Arrays.toString(indexInfo), i, end));
|
|
}
|
|
copyOutBootstrapArguments(caller, indexInfo,
|
|
i, end, temp, 0,
|
|
true, null);
|
|
for (Object x : temp) {
|
|
x = maybeReBox(x);
|
|
buf[bufi++] = x;
|
|
cache[i++] = wrapNull(x);
|
|
}
|
|
if (end < cache.length && cache[end] == null)
|
|
maybePrefetchIntoCache(end, true); // try to prefetch
|
|
return i;
|
|
}
|
|
|
|
private static final int MIN_PF = 4;
|
|
private void maybePrefetchIntoCache(int i, boolean bulk) {
|
|
int len = cache.length;
|
|
assert(0 <= i && i <= len);
|
|
int pfLimit = i;
|
|
if (bulk) pfLimit += i; // exponential prefetch expansion
|
|
// try to prefetch at least MIN_PF elements
|
|
if (pfLimit < i + MIN_PF) pfLimit = i + MIN_PF;
|
|
if (pfLimit > len || pfLimit < 0) pfLimit = len;
|
|
// stop prefetching where cache is more full than empty
|
|
int empty = 0, nonEmpty = 0, lastEmpty = i;
|
|
for (int j = i; j < pfLimit; j++) {
|
|
if (cache[j] == null) {
|
|
empty++;
|
|
lastEmpty = j;
|
|
} else {
|
|
nonEmpty++;
|
|
if (nonEmpty > empty) {
|
|
pfLimit = lastEmpty + 1;
|
|
break;
|
|
}
|
|
if (pfLimit < len) pfLimit++;
|
|
}
|
|
}
|
|
if (bulk && empty < MIN_PF && pfLimit < len)
|
|
return; // not worth the effort
|
|
prefetchIntoCache(i, pfLimit);
|
|
}
|
|
|
|
private void prefetchIntoCache(int i, int pfLimit) {
|
|
if (pfLimit <= i) return; // corner case
|
|
Object[] temp = new Object[pfLimit - i];
|
|
if (TRACE_METHOD_LINKAGE) {
|
|
System.out.println("prefetching BSM arguments: " +
|
|
Arrays.asList(caller.getSimpleName(), Arrays.toString(indexInfo), i, pfLimit));
|
|
}
|
|
copyOutBootstrapArguments(caller, indexInfo,
|
|
i, pfLimit, temp, 0,
|
|
false, NOT_PRESENT);
|
|
for (Object x : temp) {
|
|
if (x != NOT_PRESENT && cache[i] == null) {
|
|
cache[i] = wrapNull(maybeReBox(x));
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*non-public*/
|
|
static final class PushAdapter {
|
|
// skeleton for push-mode BSM which wraps a pull-mode BSM:
|
|
static Object pushToBootstrapMethod(MethodHandle pullModeBSM,
|
|
MethodHandles.Lookup lookup, String name, Object type,
|
|
Object... arguments) throws Throwable {
|
|
ConstantGroup cons = makeConstantGroup(Arrays.asList(arguments));
|
|
BootstrapCallInfo<?> bsci = makeBootstrapCallInfo(pullModeBSM, name, type, cons);
|
|
if (TRACE_METHOD_LINKAGE)
|
|
System.out.println("pull-mode BSM gets pushed arguments from fake BSCI");
|
|
return pullModeBSM.invoke(lookup, bsci);
|
|
}
|
|
|
|
static final MethodHandle MH_pushToBootstrapMethod;
|
|
static {
|
|
final Class<?> THIS_CLASS = PushAdapter.class;
|
|
try {
|
|
MH_pushToBootstrapMethod = IMPL_LOOKUP
|
|
.findStatic(THIS_CLASS, "pushToBootstrapMethod",
|
|
MethodType.methodType(Object.class, MethodHandle.class,
|
|
Lookup.class, String.class, Object.class, Object[].class));
|
|
} catch (Throwable ex) {
|
|
throw new InternalError(ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*non-public*/
|
|
static final class PullAdapter {
|
|
// skeleton for pull-mode BSM which wraps a push-mode BSM:
|
|
static Object pullFromBootstrapMethod(MethodHandle pushModeBSM,
|
|
MethodHandles.Lookup lookup,
|
|
BootstrapCallInfo<?> bsci)
|
|
throws Throwable {
|
|
int argc = bsci.size();
|
|
switch (argc) {
|
|
case 0:
|
|
return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType());
|
|
case 1:
|
|
return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
|
|
bsci.get(0));
|
|
case 2:
|
|
return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
|
|
bsci.get(0), bsci.get(1));
|
|
case 3:
|
|
return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
|
|
bsci.get(0), bsci.get(1), bsci.get(2));
|
|
case 4:
|
|
return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
|
|
bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3));
|
|
case 5:
|
|
return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
|
|
bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4));
|
|
case 6:
|
|
return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
|
|
bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4), bsci.get(5));
|
|
default:
|
|
final int NON_SPREAD_ARG_COUNT = 3; // (lookup, name, type)
|
|
final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT;
|
|
if (argc >= MAX_SAFE_SIZE) {
|
|
// to be on the safe side, use invokeWithArguments which handles jumbo lists
|
|
Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argc];
|
|
newargv[0] = lookup;
|
|
newargv[1] = bsci.invocationName();
|
|
newargv[2] = bsci.invocationType();
|
|
bsci.copyConstants(0, argc, newargv, NON_SPREAD_ARG_COUNT);
|
|
return pushModeBSM.invokeWithArguments(newargv);
|
|
}
|
|
MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argc);
|
|
MethodHandle typedBSM = pushModeBSM.asType(invocationType);
|
|
MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
|
|
Object[] argv = new Object[argc];
|
|
bsci.copyConstants(0, argc, argv, 0);
|
|
return spreader.invokeExact(typedBSM, (Object) lookup, (Object) bsci.invocationName(), bsci.invocationType(), argv);
|
|
}
|
|
}
|
|
|
|
static final MethodHandle MH_pullFromBootstrapMethod;
|
|
|
|
static {
|
|
final Class<?> THIS_CLASS = PullAdapter.class;
|
|
try {
|
|
MH_pullFromBootstrapMethod = IMPL_LOOKUP
|
|
.findStatic(THIS_CLASS, "pullFromBootstrapMethod",
|
|
MethodType.methodType(Object.class, MethodHandle.class,
|
|
Lookup.class, BootstrapCallInfo.class));
|
|
} catch (Throwable ex) {
|
|
throw new InternalError(ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Given a push-mode BSM (taking one argument) convert it to a
|
|
* pull-mode BSM (taking N pre-resolved arguments).
|
|
* This method is used when, in fact, the JVM is passing up
|
|
* pre-resolved arguments, but the BSM is expecting lazy stuff.
|
|
* Or, when goToPushMode is true, do the reverse transform.
|
|
* (The two transforms are exactly inverse.)
|
|
*/
|
|
static MethodHandle pushMePullYou(MethodHandle bsm, boolean goToPushMode) {
|
|
if (TRACE_METHOD_LINKAGE) {
|
|
System.out.println("converting BSM of type " + bsm.type() + " to "
|
|
+ (goToPushMode ? "push mode" : "pull mode"));
|
|
}
|
|
assert(isPullModeBSM(bsm) == goToPushMode); // there must be a change
|
|
if (goToPushMode) {
|
|
return PushAdapter.MH_pushToBootstrapMethod.bindTo(bsm).withVarargs(true);
|
|
} else {
|
|
return PullAdapter.MH_pullFromBootstrapMethod.bindTo(bsm).withVarargs(false);
|
|
}
|
|
}
|
|
}
|