8186209: Tool support for ConstantDynamic

8186046: Minimal ConstantDynamic support
8190972: Ensure that AOT/Graal filters out class files containing CONSTANT_Dynamic ahead of full AOT support

Co-authored-by: Lois Foltan <lois.foltan@oracle.com>
Co-authored-by: John Rose <john.r.rose@oracle.com>
Reviewed-by: acorn, coleenp, kvn
This commit is contained in:
Paul Sandoz 2017-09-08 10:46:46 -07:00
parent 52d3bf29b2
commit e55a05957d
114 changed files with 11762 additions and 404 deletions

View file

@ -26,11 +26,15 @@
package java.lang;
/**
* Thrown to indicate that an {@code invokedynamic} instruction has
* failed to find its bootstrap method,
* or the bootstrap method has failed to provide a
* {@linkplain java.lang.invoke.CallSite call site} with a {@linkplain java.lang.invoke.CallSite#getTarget target}
* of the correct {@linkplain java.lang.invoke.MethodHandle#type() method type}.
* Thrown to indicate that an {@code invokedynamic} instruction or a dynamic
* constant failed to resolve its bootstrap method and arguments,
* or for {@code invokedynamic} instruction the bootstrap method has failed to
* provide a
* {@linkplain java.lang.invoke.CallSite call site} with a
* {@linkplain java.lang.invoke.CallSite#getTarget target}
* of the correct {@linkplain java.lang.invoke.MethodHandle#type() method type},
* or for a dynamic constant the bootstrap method has failed to provide a
* constant value of the required type.
*
* @author John Rose, JSR 292 EG
* @since 1.7

View file

@ -0,0 +1,341 @@
/*
* 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 java.util.*;
import jdk.internal.vm.annotation.Stable;
import static java.lang.invoke.MethodHandleStatics.rangeCheck1;
import static java.lang.invoke.MethodHandleStatics.rangeCheck2;
/** Utility class for implementing ConstantGroup. */
/*non-public*/
abstract class AbstractConstantGroup implements ConstantGroup {
/** The size of this constant group, set permanently by the constructor. */
protected final int size;
/** The constructor requires the size of the constant group being represented.
* @param size the size of this constant group, set permanently by the constructor
*/
AbstractConstantGroup(int size) {
this.size = size;
}
@Override public final int size() {
return size;
}
public abstract Object get(int index) throws LinkageError;
public abstract Object get(int index, Object ifNotPresent);
public abstract boolean isPresent(int index);
// Do not override equals or hashCode, since this type is stateful.
/**
* Produce a string using the non-resolving list view,
* where unresolved elements are presented as asterisks.
* @return {@code this.asList("*").toString()}
*/
@Override public String toString() {
return asList("*").toString();
}
static class AsIterator implements Iterator<Object> {
private final ConstantGroup self;
private final int end;
private final boolean resolving;
private final Object ifNotPresent;
// Mutable state:
private int index;
private AsIterator(ConstantGroup self, int start, int end,
boolean resolving, Object ifNotPresent) {
this.self = self;
this.end = end;
this.index = start;
this.resolving = resolving;
this.ifNotPresent = ifNotPresent;
}
AsIterator(ConstantGroup self, int start, int end) {
this(self, start, end, true, null);
}
AsIterator(ConstantGroup self, int start, int end,
Object ifNotPresent) {
this(self, start, end, false, ifNotPresent);
}
@Override
public boolean hasNext() {
return index < end;
}
@Override
public Object next() {
int i = bumpIndex();
if (resolving)
return self.get(i);
else
return self.get(i, ifNotPresent);
}
private int bumpIndex() {
int i = index;
if (i >= end) throw new NoSuchElementException();
index = i+1;
return i;
}
}
static class SubGroup extends AbstractConstantGroup {
private final ConstantGroup self; // the real CG
private final int offset; // offset within myself
SubGroup(ConstantGroup self, int start, int end) {
super(end - start);
this.self = self;
this.offset = start;
rangeCheck2(start, end, size);
}
private int mapIndex(int index) {
return rangeCheck1(index, size) + offset;
}
@Override
public Object get(int index) {
return self.get(mapIndex(index));
}
@Override
public Object get(int index, Object ifNotPresent) {
return self.get(mapIndex(index), ifNotPresent);
}
@Override
public boolean isPresent(int index) {
return self.isPresent(mapIndex(index));
}
@Override
public ConstantGroup subGroup(int start, int end) {
rangeCheck2(start, end, size);
return new SubGroup(self, offset + start, offset + end);
}
@Override
public List<Object> asList() {
return new AsList(self, offset, offset + size);
}
@Override
public List<Object> asList(Object ifNotPresent) {
return new AsList(self, offset, offset + size, ifNotPresent);
}
@Override
public int copyConstants(int start, int end,
Object[] buf, int pos) throws LinkageError {
rangeCheck2(start, end, size);
return self.copyConstants(offset + start, offset + end,
buf, pos);
}
@Override
public int copyConstants(int start, int end,
Object[] buf, int pos,
Object ifNotPresent) {
rangeCheck2(start, end, size);
return self.copyConstants(offset + start, offset + end,
buf, pos, ifNotPresent);
}
}
static class AsList extends AbstractList<Object> {
private final ConstantGroup self;
private final int size;
private final int offset;
private final boolean resolving;
private final Object ifNotPresent;
private AsList(ConstantGroup self, int start, int end,
boolean resolving, Object ifNotPresent) {
this.self = self;
this.size = end - start;
this.offset = start;
this.resolving = resolving;
this.ifNotPresent = ifNotPresent;
rangeCheck2(start, end, self.size());
}
AsList(ConstantGroup self, int start, int end) {
this(self, start, end, true, null);
}
AsList(ConstantGroup self, int start, int end,
Object ifNotPresent) {
this(self, start, end, false, ifNotPresent);
}
private int mapIndex(int index) {
return rangeCheck1(index, size) + offset;
}
@Override public final int size() {
return size;
}
@Override public Object get(int index) {
if (resolving)
return self.get(mapIndex(index));
else
return self.get(mapIndex(index), ifNotPresent);
}
@Override
public Iterator<Object> iterator() {
if (resolving)
return new AsIterator(self, offset, offset + size);
else
return new AsIterator(self, offset, offset + size, ifNotPresent);
}
@Override public List<Object> subList(int start, int end) {
rangeCheck2(start, end, size);
return new AsList(self, offset + start, offset + end,
resolving, ifNotPresent);
}
@Override public Object[] toArray() {
return toArray(new Object[size]);
}
@Override public <T> T[] toArray(T[] a) {
int pad = a.length - size;
if (pad < 0) {
pad = 0;
a = Arrays.copyOf(a, size);
}
if (resolving)
self.copyConstants(offset, offset + size, a, 0);
else
self.copyConstants(offset, offset + size, a, 0,
ifNotPresent);
if (pad > 0) a[size] = null;
return a;
}
}
static abstract
class WithCache extends AbstractConstantGroup {
@Stable final Object[] cache;
WithCache(int size) {
super(size);
// It is caller's responsibility to initialize the cache.
// Initial contents are all-null, which means nothing is present.
cache = new Object[size];
}
void initializeCache(List<Object> cacheContents, Object ifNotPresent) {
// Replace ifNotPresent with NOT_PRESENT,
// and null with RESOLVED_TO_NULL.
// Then forget about the user-provided ifNotPresent.
for (int i = 0; i < cache.length; i++) {
Object x = cacheContents.get(i);
if (x == ifNotPresent)
continue; // leave the null in place
if (x == null)
x = RESOLVED_TO_NULL;
cache[i] = x;
}
}
@Override public Object get(int i) {
Object x = cache[i];
// @Stable array must use null for sentinel
if (x == null) x = fillCache(i);
return unwrapNull(x);
}
@Override public Object get(int i, Object ifNotAvailable) {
Object x = cache[i];
// @Stable array must use null for sentinel
if (x == null) return ifNotAvailable;
return unwrapNull(x);
}
@Override
public boolean isPresent(int i) {
return cache[i] != null;
}
/** hook for local subclasses */
Object fillCache(int i) {
throw new NoSuchElementException("constant group does not contain element #"+i);
}
/// routines for mapping between null sentinel and true resolved null
static Object wrapNull(Object x) {
return x == null ? RESOLVED_TO_NULL : x;
}
static Object unwrapNull(Object x) {
assert(x != null);
return x == RESOLVED_TO_NULL ? null : x;
}
// secret sentinel for an actual null resolved value, in the cache
static final Object RESOLVED_TO_NULL = new Object();
// secret sentinel for a "hole" in the cache:
static final Object NOT_PRESENT = new Object();
}
/** Skeleton implementation of BootstrapCallInfo. */
static
class BSCIWithCache<T> extends WithCache implements BootstrapCallInfo<T> {
private final MethodHandle bsm;
private final String name;
private final T type;
@Override public String toString() {
return bsm+"/"+name+":"+type+super.toString();
}
BSCIWithCache(MethodHandle bsm, String name, T type, int size) {
super(size);
this.type = type;
this.bsm = bsm;
this.name = name;
assert(type instanceof Class || type instanceof MethodType);
}
@Override public MethodHandle bootstrapMethod() { return bsm; }
@Override public String invocationName() { return name; }
@Override public T invocationType() { return type; }
}
}

View file

@ -0,0 +1,142 @@
/*
* 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 java.lang.invoke.MethodHandles.Lookup;
/**
* An interface providing full static information about a particular
* call to a
* <a href="package-summary.html#bsm">bootstrap method</a> of an
* dynamic call site or dynamic constant.
* This information includes the method itself, the associated
* name and type, and any associated static arguments.
* <p>
* If a bootstrap method declares exactly two arguments, and is
* not of variable arity, then it is fed only two arguments by
* the JVM, the {@linkplain Lookup lookup object} and an instance
* of {@code BootstrapCallInfo} which supplies the rest of the
* information about the call.
* <p>
* The API for accessing the static arguments allows the bootstrap
* method to reorder the resolution (in the constant pool) of the
* static arguments, and to catch errors resulting from the resolution.
* This mode of evaluation <em>pulls</em> bootstrap parameters from
* the JVM under control of the bootstrap method, as opposed to
* the JVM <em>pushing</em> parameters to a bootstrap method
* by resolving them all before the bootstrap method is called.
* @apiNote
* <p>
* The {@linkplain Lookup lookup object} is <em>not</em> included in this
* bundle of information, so as not to obscure the access control
* logic of the program.
* In cases where there are many thousands of parameters, it may
* be preferable to pull their resolved values, either singly or in
* batches, rather than wait until all of them have been resolved
* before a constant or call site can be used.
* <p>
* A push mode bootstrap method can be adapted to a pull mode
* bootstrap method, and vice versa. For example, this generic
* adapter pops a push-mode bootstrap method from the beginning
* of the static argument list, eagerly resolves all the remaining
* static arguments, and invokes the popped method in push mode.
* The callee has no way of telling that it was not called directly
* from the JVM.
* <blockquote><pre>{@code
static Object genericBSM(Lookup lookup, BootstrapCallInfo<Object> bsci)
throws Throwable {
ArrayList<Object> args = new ArrayList<>();
args.add(lookup);
args.add(bsci.invocationName());
args.add(bsci.invocationType());
MethodHandle bsm = (MethodHandle) bsci.get(0);
List<Object> restOfArgs = bsci.asList().subList(1, bsci.size();
// the next line eagerly resolves all remaining static arguments:
args.addAll(restOfArgs);
return bsm.invokeWithArguments(args);
}
* }</pre></blockquote>
*
* <p>
* In the other direction, here is a combinator which pops
* a pull-mode bootstrap method from the beginning of a list of
* static argument values (already resolved), reformats all of
* the arguments into a pair of a lookup and a {@code BootstrapCallInfo},
* and invokes the popped method. Again the callee has no way of
* telling it was not called directly by the JVM, except that
* all of the constant values will appear as resolved.
* Put another way, if any constant fails to resolve, the
* callee will not be able to catch the resulting error,
* since the error will be thrown by the JVM before the
* bootstrap method is entered.
* <blockquote><pre>{@code
static Object genericBSM(Lookup lookup, String name, Object type,
MethodHandle bsm, Object... args)
throws Throwable {
ConstantGroup cons = ConstantGroup.makeConstantGroup(Arrays.asList(args));
BootstrapCallInfo<Object> bsci = makeBootstrapCallInfo(bsm, name, type, cons);
return bsm.invoke(lookup, bsci);
}
* }</pre></blockquote>
*
* @since 1.10
*/
// public
interface BootstrapCallInfo<T> extends ConstantGroup {
/** Returns the bootstrap method for this call.
* @return the bootstrap method
*/
MethodHandle bootstrapMethod();
/** Returns the method name or constant name for this call.
* @return the method name or constant name
*/
String invocationName();
/** Returns the method type or constant type for this call.
* @return the method type or constant type
*/
T invocationType();
/**
* Make a new bootstrap call descriptor with the given components.
* @param bsm bootstrap method
* @param name invocation name
* @param type invocation type
* @param constants the additional static arguments for the bootstrap method
* @param <T> the type of the invocation type, either {@link MethodHandle} or {@link Class}
* @return a new bootstrap call descriptor with the given components
*/
static <T> BootstrapCallInfo<T> makeBootstrapCallInfo(MethodHandle bsm,
String name,
T type,
ConstantGroup constants) {
AbstractConstantGroup.BSCIWithCache<T> bsci = new AbstractConstantGroup.BSCIWithCache<>(bsm, name, type, constants.size());
final Object NP = AbstractConstantGroup.BSCIWithCache.NOT_PRESENT;
bsci.initializeCache(constants.asList(NP), NP);
return bsci;
}
}

View file

@ -0,0 +1,366 @@
/*
* 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 = Adapters.pushMePullYou(bootstrapMethod, true);
}
} else {
// VM wants us to pull args from it
pullModeBSM = pullMode ? bootstrapMethod :
Adapters.pushMePullYou(bootstrapMethod, false);
bootstrapMethod = null;
}
try {
info = maybeReBox(info);
if (info == null) {
// VM is allowed to pass up a null meaning no BSM args
result = bootstrapMethod.invoke(caller, name, type);
}
else if (!info.getClass().isArray()) {
// VM is allowed to pass up a single BSM arg directly
result = bootstrapMethod.invoke(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);
switch (argv.length) {
case 0:
result = bootstrapMethod.invoke(caller, name, type);
break;
case 1:
result = bootstrapMethod.invoke(caller, name, type,
argv[0]);
break;
case 2:
result = bootstrapMethod.invoke(caller, name, type,
argv[0], argv[1]);
break;
case 3:
result = bootstrapMethod.invoke(caller, name, type,
argv[0], argv[1], argv[2]);
break;
case 4:
result = bootstrapMethod.invoke(caller, name, type,
argv[0], argv[1], argv[2], argv[3]);
break;
case 5:
result = bootstrapMethod.invoke(caller, name, type,
argv[0], argv[1], argv[2], argv[3], argv[4]);
break;
case 6:
result = bootstrapMethod.invoke(caller, name, type,
argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
break;
default:
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);
result = bootstrapMethod.invokeWithArguments(newargv);
break;
}
MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
result = spreader.invokeExact(typedBSM, (Object) caller, (Object) 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);
}
}
/** 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.hasPrivateAccess()) //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 Adapters {
// 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);
}
// 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();
Object arguments[] = new Object[3 + argc];
arguments[0] = lookup;
arguments[1] = bsci.invocationName();
arguments[2] = bsci.invocationType();
bsci.copyConstants(0, argc, arguments, 3);
if (TRACE_METHOD_LINKAGE)
System.out.println("pulled arguments from VM for push-mode BSM");
return pushModeBSM.invokeWithArguments(arguments);
}
static final MethodHandle MH_pushToBootstrapMethod;
static final MethodHandle MH_pullFromBootstrapMethod;
static {
final Class<?> THIS_CLASS = Adapters.class;
try {
MH_pushToBootstrapMethod = IMPL_LOOKUP
.findStatic(THIS_CLASS, "pushToBootstrapMethod",
MethodType.methodType(Object.class, MethodHandle.class,
Lookup.class, String.class, Object.class, Object[].class));
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 to "+(goToPushMode ? "push mode" : "pull mode"));
assert(isPullModeBSM(bsm) == goToPushMode); //there must be a change
if (goToPushMode) {
return Adapters.MH_pushToBootstrapMethod.bindTo(bsm).withVarargs(true);
} else {
return Adapters.MH_pullFromBootstrapMethod.bindTo(bsm).withVarargs(false);
}
}
}
}

View file

@ -302,65 +302,10 @@ public class CallSite {
Object info,
// Caller information:
Class<?> callerClass) {
MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass);
CallSite site;
try {
Object binding;
info = maybeReBox(info);
if (info == null) {
binding = bootstrapMethod.invoke(caller, name, type);
} else if (!info.getClass().isArray()) {
binding = bootstrapMethod.invoke(caller, name, type, info);
} else {
Object[] argv = (Object[]) info;
maybeReBoxElements(argv);
switch (argv.length) {
case 0:
binding = bootstrapMethod.invoke(caller, name, type);
break;
case 1:
binding = bootstrapMethod.invoke(caller, name, type,
argv[0]);
break;
case 2:
binding = bootstrapMethod.invoke(caller, name, type,
argv[0], argv[1]);
break;
case 3:
binding = bootstrapMethod.invoke(caller, name, type,
argv[0], argv[1], argv[2]);
break;
case 4:
binding = bootstrapMethod.invoke(caller, name, type,
argv[0], argv[1], argv[2], argv[3]);
break;
case 5:
binding = bootstrapMethod.invoke(caller, name, type,
argv[0], argv[1], argv[2], argv[3], argv[4]);
break;
case 6:
binding = bootstrapMethod.invoke(caller, name, type,
argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
break;
default:
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);
binding = 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);
binding = spreader.invokeExact(typedBSM, (Object) caller, (Object) name, (Object) type, argv);
}
}
}
Object binding = BootstrapMethodInvoker.invoke(
CallSite.class, bootstrapMethod, name, type, info, callerClass);
if (binding instanceof CallSite) {
site = (CallSite) binding;
} else {
@ -369,7 +314,7 @@ public class CallSite {
// Throws a runtime exception defining the cause that is then
// in the "catch (Throwable ex)" a few lines below wrapped in
// BootstrapMethodError
throw new ClassCastException("bootstrap method failed to produce a CallSite");
throw new ClassCastException("CallSite bootstrap method failed to produce an instance of CallSite");
}
if (!site.getTarget().type().equals(type)) {
// See the "Linking Exceptions" section for the invokedynamic
@ -388,22 +333,8 @@ public class CallSite {
throw e;
} catch (Throwable ex) {
// Wrap anything else in BootstrapMethodError
throw new BootstrapMethodError("call site initialization exception", ex);
throw new BootstrapMethodError("CallSite bootstrap method initialization exception", ex);
}
return site;
}
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]);
}
}
}

View file

@ -0,0 +1,287 @@
/*
* 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 java.util.List;
import java.util.NoSuchElementException;
import java.util.function.IntFunction;
/**
* An ordered sequence of constants, some of which may not yet
* be present. This type is used by {@link BootstrapCallInfo}
* to represent the sequence of bootstrap arguments associated
* with a bootstrap method, without forcing their immediate
* resolution.
* <p>
* If you use the
* {@linkplain ConstantGroup#get(int) simple get method},
* the constant will be resolved, if this has not already
* happened. An occasional side effect of resolution is a
* {@code LinkageError}, which happens if the system
* could not resolve the constant in question.
* <p>
* In order to peek at a constant without necessarily
* resolving it, use the
* {@linkplain ConstantGroup#get(int,Object)
* non-throwing get method}.
* This method will never throw a resolution error.
* Instead, if the resolution would result in an error,
* or if the implementation elects not to attempt
* resolution at this point, then the method will
* return the user-supplied sentinel value.
* <p>
* To iterate through the constants, resolving as you go,
* use the iterator provided on the {@link List}-typed view.
* If you supply a sentinel, resolution will be suppressed.
* <p>
* Typically the constant is drawn from a constant pool entry
* in the virtual machine. Constant pool entries undergo a
* one-time state transition from unresolved to resolved,
* with a permanently recorded result. Usually that result
* is the desired constant value, but it may also be an error.
* In any case, the results displayed by a {@code ConstantGroup}
* are stable in the same way. If a query to a particular
* constant in a {@code ConstantGroup} throws an exception once,
* it will throw the same kind of exception forever after.
* If the query returns a constant value once, it will return
* the same value forever after.
* <p>
* The only possible change in the status of a constant is
* from the unresolved to the resolved state, and that
* happens exactly once. A constant will never revert to
* an unlinked state. However, from the point of view of
* this interface, constants may appear to spontaneously
* resolve. This is so because constant pools are global
* structures shared across threads, and because
* prefetching of some constants may occur, there are no
* strong guarantees when the virtual machine may resolve
* constants.
* <p>
* When choosing sentinel values, be aware that a constant
* pool which has {@code CONSTANT_Dynamic} entries
* can contain potentially any representable value,
* and arbitrary implementations of {@code ConstantGroup}
* are also free to produce arbitrary values.
* This means some obvious choices for sentinel values,
* such as {@code null}, may sometimes fail to distinguish
* a resolved from an unresolved constant in the group.
* The most reliable sentinel is a privately created object,
* or perhaps the {@code ConstantGroup} itself.
* @since 1.10
*/
// public
interface ConstantGroup {
/// Access
/**
* Returns the number of constants in this group.
* This value never changes, for any particular group.
* @return the number of constants in this group
*/
int size();
/**
* Returns the selected constant, resolving it if necessary.
* Throws a linkage error if resolution proves impossible.
* @param index which constant to select
* @return the selected constant
* @throws LinkageError if the selected constant needs resolution and cannot be resolved
*/
Object get(int index) throws LinkageError;
/**
* Returns the selected constant,
* or the given sentinel value if there is none available.
* If the constant cannot be resolved, the sentinel will be returned.
* If the constant can (perhaps) be resolved, but has not yet been resolved,
* then the sentinel <em>may</em> be returned, at the implementation's discretion.
* To force resolution (and a possible exception), call {@link #get(int)}.
* @param index the selected constant
* @param ifNotPresent the sentinel value to return if the constant is not present
* @return the selected constant, if available, else the sentinel value
*/
Object get(int index, Object ifNotPresent);
/**
* Returns an indication of whether a constant may be available.
* If it returns {@code true}, it will always return true in the future,
* and a call to {@link #get(int)} will never throw an exception.
* <p>
* After a normal return from {@link #get(int)} or a present
* value is reported from {@link #get(int,Object)}, this method
* must always return true.
* <p>
* If this method returns {@code false}, nothing in particular
* can be inferred, since the query only concerns the internal
* logic of the {@code ConstantGroup} object which ensures that
a successful * query to a constant will always remain successful.
* The only way to force a permanent decision about whether
* a constant is available is to call {@link #get(int)} and
* be ready for an exception if the constant is unavailable.
* @param index the selected constant
* @return {@code true} if the selected constant is known by
* this object to be present, {@code false} if it is known
* not to be present or
*/
boolean isPresent(int index);
/// Views
/**
* Create a view on this group as a {@link List} view.
* Any request for a constant through this view will
* force resolution.
* @return a {@code List} view on this group which will force resolution
*/
default List<Object> asList() {
return new AbstractConstantGroup.AsList(this, 0, size());
}
/**
* Create a view on this group as a {@link List} view.
* Any request for a constant through this view will
* return the given sentinel value, if the corresponding
* call to {@link #get(int,Object)} would do so.
* @param ifNotPresent the sentinel value to return if a constant is not present
* @return a {@code List} view on this group which will not force resolution
*/
default List<Object> asList(Object ifNotPresent) {
return new AbstractConstantGroup.AsList(this, 0, size(), ifNotPresent);
}
/**
* Create a view on a sub-sequence of this group.
* @param start the index to begin the view
* @param end the index to end the view
* @return a view on the selected sub-group
*/
default ConstantGroup subGroup(int start, int end) {
return new AbstractConstantGroup.SubGroup(this, start, end);
}
/// Bulk operations
/**
* Copy a sequence of constant values into a given buffer.
* This is equivalent to {@code end-offset} separate calls to {@code get},
* for each index in the range from {@code offset} up to but not including {@code end}.
* For the first constant that cannot be resolved,
* a {@code LinkageError} is thrown, but only after
* preceding constant value have been stored.
* @param start index of first constant to retrieve
* @param end limiting index of constants to retrieve
* @param buf array to receive the requested values
* @param pos position in the array to offset storing the values
* @return the limiting index, {@code end}
* @throws LinkageError if a constant cannot be resolved
*/
default int copyConstants(int start, int end,
Object[] buf, int pos)
throws LinkageError
{
int bufBase = pos - start; // buf[bufBase + i] = get(i)
for (int i = start; i < end; i++) {
buf[bufBase + i] = get(i);
}
return end;
}
/**
* Copy a sequence of constant values into a given buffer.
* This is equivalent to {@code end-offset} separate calls to {@code get},
* for each index in the range from {@code offset} up to but not including {@code end}.
* Any constants that cannot be resolved are replaced by the
* given sentinel value.
* @param start index of first constant to retrieve
* @param end limiting index of constants to retrieve
* @param buf array to receive the requested values
* @param pos position in the array to offset storing the values
* @param ifNotPresent sentinel value to store if a value is not available
* @return the limiting index, {@code end}
* @throws LinkageError if {@code resolve} is true and a constant cannot be resolved
*/
default int copyConstants(int start, int end,
Object[] buf, int pos,
Object ifNotPresent) {
int bufBase = pos - start; // buf[bufBase + i] = get(i)
for (int i = start; i < end; i++) {
buf[bufBase + i] = get(i, ifNotPresent);
}
return end;
}
/**
* Make a new constant group with the given constants.
* The value of {@code ifNotPresent} may be any reference.
* If this value is encountered as an element of the
* {@code constants} list, the new constant group will
* regard that element of the list as logically missing.
* If the new constant group is called upon to resolve
* a missing element of the group, it will refer to the
* given {@code constantProvider}, by calling it on the
* index of the missing element.
* The {@code constantProvider} must be stable, in the sense
* that the outcome of calling it on the same index twice
* will produce equivalent results.
* If {@code constantProvider} is the null reference, then
* it will be treated as if it were a function which raises
* {@link NoSuchElementException}.
* @param constants the elements of this constant group
* @param ifNotPresent sentinel value provided instead of a missing constant
* @param constantProvider function to call when a missing constant is resolved
* @return a new constant group with the given constants and resolution behavior
*/
static ConstantGroup makeConstantGroup(List<Object> constants,
Object ifNotPresent,
IntFunction<Object> constantProvider) {
class Impl extends AbstractConstantGroup.WithCache {
Impl() {
super(constants.size());
initializeCache(constants, ifNotPresent);
}
@Override
Object fillCache(int index) {
if (constantProvider == null) super.fillCache(index);
return constantProvider.apply(index);
}
}
return new Impl();
}
/**
* Make a new constant group with the given constant values.
* The constants will be copied from the given list into the
* new constant group, forcing resolution if any are missing.
* @param constants the constants of this constant group
* @return a new constant group with the given constants
*/
static ConstantGroup makeConstantGroup(List<Object> constants) {
final Object NP = AbstractConstantGroup.WithCache.NOT_PRESENT;
assert(!constants.contains(NP)); // secret value
return makeConstantGroup(constants, NP, null);
}
}

View file

@ -0,0 +1,46 @@
/*
* 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;
/**
* Bootstrap methods for dynamically-computed constant.
*/
final class DynamicConstant {
// implements the upcall from the JVM, MethodHandleNatives.linkDynamicConstant:
/*non-public*/
static Object makeConstant(MethodHandle bootstrapMethod,
// Callee information:
String name, Class<?> type,
// Extra arguments for BSM, if any:
Object info,
// Caller information:
Class<?> callerClass) {
// BSMI.invoke handles all type checking and exception translation.
// If type is not a reference type, the JVM is expecting a boxed
// version, and will manage unboxing on the other side.
return BootstrapMethodInvoker.invoke(
type, bootstrapMethod, name, type, info, callerClass);
}
}

View file

@ -65,6 +65,12 @@ class MethodHandleNatives {
static native void setCallSiteTargetNormal(CallSite site, MethodHandle target);
static native void setCallSiteTargetVolatile(CallSite site, MethodHandle target);
static native void copyOutBootstrapArguments(Class<?> caller, int[] indexInfo,
int start, int end,
Object[] buf, int pos,
boolean resolve,
Object ifNotAvailable);
/** Represents a context to track nmethod dependencies on CallSite instance target. */
static class CallSiteContext implements Runnable {
//@Injected JVM_nmethodBucket* vmdependencies;
@ -228,6 +234,7 @@ class MethodHandleNatives {
* The JVM is linking an invokedynamic instruction. Create a reified call site for it.
*/
static MemberName linkCallSite(Object callerObj,
int indexInCP,
Object bootstrapMethodObj,
Object nameObj, Object typeObj,
Object staticArguments,
@ -268,9 +275,7 @@ class MethodHandleNatives {
Object[] appendixResult) {
Object bsmReference = bootstrapMethod.internalMemberName();
if (bsmReference == null) bsmReference = bootstrapMethod;
Object staticArglist = (staticArguments instanceof Object[] ?
java.util.Arrays.asList((Object[]) staticArguments) :
staticArguments);
String staticArglist = staticArglistForTrace(staticArguments);
System.out.println("linkCallSite "+caller.getName()+" "+
bsmReference+" "+
name+type+"/"+staticArglist);
@ -280,11 +285,89 @@ class MethodHandleNatives {
System.out.println("linkCallSite => "+res+" + "+appendixResult[0]);
return res;
} catch (Throwable ex) {
ex.printStackTrace(); // print now in case exception is swallowed
System.out.println("linkCallSite => throw "+ex);
throw ex;
}
}
// this implements the upcall from the JVM, MethodHandleNatives.linkDynamicConstant:
static Object linkDynamicConstant(Object callerObj,
int indexInCP,
Object bootstrapMethodObj,
Object nameObj, Object typeObj,
Object staticArguments) {
MethodHandle bootstrapMethod = (MethodHandle)bootstrapMethodObj;
Class<?> caller = (Class<?>)callerObj;
String name = nameObj.toString().intern();
Class<?> type = (Class<?>)typeObj;
if (!TRACE_METHOD_LINKAGE)
return linkDynamicConstantImpl(caller, bootstrapMethod, name, type, staticArguments);
return linkDynamicConstantTracing(caller, bootstrapMethod, name, type, staticArguments);
}
static Object linkDynamicConstantImpl(Class<?> caller,
MethodHandle bootstrapMethod,
String name, Class<?> type,
Object staticArguments) {
return DynamicConstant.makeConstant(bootstrapMethod, name, type, staticArguments, caller);
}
private static String staticArglistForTrace(Object staticArguments) {
if (staticArguments instanceof Object[])
return "BSA="+java.util.Arrays.asList((Object[]) staticArguments);
if (staticArguments instanceof int[])
return "BSA@"+java.util.Arrays.toString((int[]) staticArguments);
if (staticArguments == null)
return "BSA0=null";
return "BSA1="+staticArguments;
}
// Tracing logic:
static Object linkDynamicConstantTracing(Class<?> caller,
MethodHandle bootstrapMethod,
String name, Class<?> type,
Object staticArguments) {
Object bsmReference = bootstrapMethod.internalMemberName();
if (bsmReference == null) bsmReference = bootstrapMethod;
String staticArglist = staticArglistForTrace(staticArguments);
System.out.println("linkDynamicConstant "+caller.getName()+" "+
bsmReference+" "+
name+type+"/"+staticArglist);
try {
Object res = linkDynamicConstantImpl(caller, bootstrapMethod, name, type, staticArguments);
System.out.println("linkDynamicConstantImpl => "+res);
return res;
} catch (Throwable ex) {
ex.printStackTrace(); // print now in case exception is swallowed
System.out.println("linkDynamicConstant => throw "+ex);
throw ex;
}
}
/** The JVM is requesting pull-mode bootstrap when it provides
* a tuple of the form int[]{ argc, vmindex }.
* The BSM is expected to call back to the JVM using the caller
* class and vmindex to resolve the static arguments.
*/
static boolean staticArgumentsPulled(Object staticArguments) {
return staticArguments instanceof int[];
}
/** A BSM runs in pull-mode if and only if its sole arguments
* are (Lookup, BootstrapCallInfo), or can be converted pairwise
* to those types, and it is not of variable arity.
* Excluding error cases, we can just test that the arity is a constant 2.
*
* NOTE: This method currently returns false, since pulling is not currently
* exposed to a BSM. When pull mode is supported the method block will be
* replaced with currently commented out code.
*/
static boolean isPullModeBSM(MethodHandle bsm) {
return false;
// return bsm.type().parameterCount() == 2 && !bsm.isVarargsCollector();
}
/**
* The JVM wants a pointer to a MethodType. Oblige it by finding or creating one.
*/
@ -506,34 +589,43 @@ class MethodHandleNatives {
Lookup lookup = IMPL_LOOKUP.in(callerClass);
assert(refKindIsValid(refKind));
return lookup.linkMethodHandleConstant((byte) refKind, defc, name, type);
} catch (IllegalAccessException ex) {
} catch (ReflectiveOperationException ex) {
throw mapLookupExceptionToError(ex);
}
}
/**
* Map a reflective exception to a linkage error.
*/
static LinkageError mapLookupExceptionToError(ReflectiveOperationException ex) {
LinkageError err;
if (ex instanceof IllegalAccessException) {
Throwable cause = ex.getCause();
if (cause instanceof AbstractMethodError) {
throw (AbstractMethodError) cause;
return (AbstractMethodError) cause;
} else {
Error err = new IllegalAccessError(ex.getMessage());
throw initCauseFrom(err, ex);
err = new IllegalAccessError(ex.getMessage());
}
} catch (NoSuchMethodException ex) {
Error err = new NoSuchMethodError(ex.getMessage());
throw initCauseFrom(err, ex);
} catch (NoSuchFieldException ex) {
Error err = new NoSuchFieldError(ex.getMessage());
throw initCauseFrom(err, ex);
} catch (ReflectiveOperationException ex) {
Error err = new IncompatibleClassChangeError();
throw initCauseFrom(err, ex);
} else if (ex instanceof NoSuchMethodException) {
err = new NoSuchMethodError(ex.getMessage());
} else if (ex instanceof NoSuchFieldException) {
err = new NoSuchFieldError(ex.getMessage());
} else {
err = new IncompatibleClassChangeError();
}
return initCauseFrom(err, ex);
}
/**
* Use best possible cause for err.initCause(), substituting the
* cause for err itself if the cause has the same (or better) type.
*/
private static Error initCauseFrom(Error err, Exception ex) {
static <E extends Error> E initCauseFrom(E err, Exception ex) {
Throwable th = ex.getCause();
if (err.getClass().isInstance(th))
return (Error) th;
@SuppressWarnings("unchecked")
final Class<E> Eclass = (Class<E>) err.getClass();
if (Eclass.isInstance(th))
return Eclass.cast(th);
err.initCause(th == null ? ex : th);
return err;
}

View file

@ -142,4 +142,13 @@ import java.util.Properties;
if (obj != null || obj2 != null) message = message + ": " + obj + ", " + obj2;
return message;
}
/*non-public*/ static void rangeCheck2(int start, int end, int size) {
if (0 > start || start > end || end > size)
throw new IndexOutOfBoundsException(start+".."+end);
}
/*non-public*/ static int rangeCheck1(int index, int size) {
if (0 > index || index >= size)
throw new IndexOutOfBoundsException(index);
return index;
}
}

View file

@ -98,12 +98,6 @@ public class MethodHandles {
* <p>
* This method is caller sensitive, which means that it may return different
* values to different callers.
* <p>
* For any given caller class {@code C}, the lookup object returned by this call
* has equivalent capabilities to any lookup object
* supplied by the JVM to the bootstrap method of an
* <a href="package-summary.html#indyinsn">invokedynamic instruction</a>
* executing in the same caller class {@code C}.
* @return a lookup object for the caller of this method, with private access
*/
@CallerSensitive

View file

@ -24,13 +24,12 @@
*/
/**
* The {@code java.lang.invoke} package contains dynamic language support provided directly by
* the Java core class libraries and virtual machine.
* The {@code java.lang.invoke} package provides low-level primitives for interacting
* with the Java Virtual Machine.
*
* <p>
* As described in the Java Virtual Machine Specification,
* certain types in this package have special relations to dynamic
* language support in the virtual machine:
* As described in the Java Virtual Machine Specification, certain types in this package
* are given special treatment by the virtual machine:
* <ul>
* <li>The classes {@link java.lang.invoke.MethodHandle MethodHandle}
* {@link java.lang.invoke.VarHandle VarHandle} contain
@ -40,77 +39,106 @@
* </li>
*
* <li>The JVM bytecode format supports immediate constants of
* the classes {@link java.lang.invoke.MethodHandle MethodHandle} and {@link java.lang.invoke.MethodType MethodType}.
* the classes {@link java.lang.invoke.MethodHandle MethodHandle} and
* {@link java.lang.invoke.MethodType MethodType}.
* </li>
*
* <li>The {@code invokedynamic} instruction makes use of bootstrap {@code MethodHandle}
* constants to dynamically resolve {@code CallSite} objects for custom method invocation
* behavior.
* </li>
*
* <li>The {@code ldc} instruction makes use of bootstrap {@code MethodHandle} constants
* to dynamically resolve custom constant values.
* </li>
* </ul>
*
* <h1><a id="jvm_mods"></a>Summary of relevant Java Virtual Machine changes</h1>
* <h1><a id="jvm_mods"></a>Dynamic resolution of call sites and constants</h1>
* The following low-level information summarizes relevant parts of the
* Java Virtual Machine specification. For full details, please see the
* current version of that specification.
*
* Each occurrence of an {@code invokedynamic} instruction is called a <em>dynamic call site</em>.
* <h2><a id="indyinsn"></a>{@code invokedynamic} instructions</h2>
* A dynamic call site is originally in an unlinked state. In this state, there is
* no target method for the call site to invoke.
* <h2><a id="indyinsn"></a>Dynamically-computed call sites</h2>
* An {@code invokedynamic} instruction is originally in an unlinked state.
* In this state, there is no target method for the instruction to invoke.
* <p>
* Before the JVM can execute a dynamic call site (an {@code invokedynamic} instruction),
* the call site must first be <em>linked</em>.
* Before the JVM can execute an {@code invokedynamic} instruction,
* the instruction must first be <em>linked</em>.
* Linking is accomplished by calling a <em>bootstrap method</em>
* which is given the static information content of the call site,
* and which must produce a {@link java.lang.invoke.MethodHandle method handle}
* that gives the behavior of the call site.
* which is given the static information content of the call,
* and which must produce a {@link java.lang.invoke.CallSite}
* that gives the behavior of the invocation.
* <p>
* Each {@code invokedynamic} instruction statically specifies its own
* bootstrap method as a constant pool reference.
* The constant pool reference also specifies the call site's name and type descriptor,
* just like {@code invokevirtual} and the other invoke instructions.
* The constant pool reference also specifies the invocation's name and method type descriptor,
* just like {@code invokestatic} and the other invoke instructions.
*
* <h2><a id="condycon"></a>Dynamically-computed constants</h2>
* The constant pool may contain constants tagged {@code CONSTANT_Dynamic},
* equipped with bootstrap methods which perform their resolution.
* Such a <em>dynamic constant</em> is originally in an unresolved state.
* Before the JVM can use a dynamically-computed constant, it must first be <em>resolved</em>.
* Dynamically-computed constant resolution is accomplished by calling a <em>bootstrap method</em>
* which is given the static information content of the constant,
* and which must produce a value of the constant's statically declared type.
* <p>
* Linking starts with resolving the constant pool entry for the
* bootstrap method, and resolving a {@link java.lang.invoke.MethodType MethodType} object for
* the type descriptor of the dynamic call site.
* This resolution process may trigger class loading.
* It may therefore throw an error if a class fails to load.
* This error becomes the abnormal termination of the dynamic
* call site execution.
* Linkage does not trigger class initialization.
* <p>
* The bootstrap method is invoked on at least three values:
* Each dynamically-computed constant statically specifies its own
* bootstrap method as a constant pool reference.
* The constant pool reference also specifies the constant's name and field type descriptor,
* just like {@code getstatic} and the other field reference instructions.
* (Roughly speaking, a dynamically-computed constant is to a dynamically-computed call site
* as a {@code CONSTANT_Fieldref} is to a {@code CONSTANT_Methodref}.)
*
* <h2><a id="bsm"></a>Execution of bootstrap methods</h2>
* Resolving a dynamically-computed call site or constant
* starts with resolving constants from the constant pool for the
* following items:
* <ul>
* <li>a {@code MethodHandles.Lookup}, a lookup object on the <em>caller class</em>
* in which dynamic call site occurs </li>
* <li>a {@code String}, the method name mentioned in the call site </li>
* <li>a {@code MethodType}, the resolved type descriptor of the call </li>
* <li>optionally, any number of additional static arguments taken from the constant pool </li>
* <li>the bootstrap method, a {@code CONSTANT_MethodHandle}</li>
* <li>the {@code Class} or {@code MethodType} derived from
* type component of the {@code CONSTANT_NameAndType} descriptor</li>
* <li>static arguments, if any (note that static arguments can themselves be
* dynamically-computed constants)</li>
* </ul>
* <p>
* In all cases, bootstrap method invocation is as if by
* {@link java.lang.invoke.MethodHandle#invokeWithArguments MethodHandle.invokeWithArguments},
* (This is also equivalent to
* {@linkplain java.lang.invoke.MethodHandle#invoke generic invocation}
* if the number of arguments is small enough.)
* The bootstrap method is then invoked, as if by
* {@link java.lang.invoke.MethodHandle#invoke MethodHandle.invoke},
* with the following arguments:
* <ul>
* <li>a {@code MethodHandles.Lookup}, which is a lookup object on the <em>caller class</em>
* in which dynamically-computed constant or call site occurs</li>
* <li>a {@code String}, the name mentioned in the {@code CONSTANT_NameAndType}</li>
* <li>a {@code MethodType} or {@code Class}, the resolved type descriptor of the {@code CONSTANT_NameAndType}</li>
* <li>a {@code Class}, the resolved type descriptor of the constant, if it is a dynamic constant </li>
* <li>the additional resolved static arguments, if any</li>
* </ul>
* <p>
* For an {@code invokedynamic} instruction, the
* returned result must be convertible to a non-null reference to a
* For a dynamically-computed call site, the returned result must be a non-null reference to a
* {@link java.lang.invoke.CallSite CallSite}.
* If the returned result cannot be converted to the expected type,
* {@link java.lang.BootstrapMethodError BootstrapMethodError} is thrown.
* The type of the call site's target must be exactly equal to the type
* derived from the dynamic call site's type descriptor and passed to
* the bootstrap method, otherwise a {@code BootstrapMethodError} is thrown.
* On success the call site then becomes permanently linked to the dynamic call
* site.
* derived from the invocation's type descriptor and passed to
* the bootstrap method. If these conditions are not met, a {@code BootstrapMethodError} is thrown.
* On success the call site then becomes permanently linked to the {@code invokedynamic}
* instruction.
* <p>
* If an exception, {@code E} say, occurs when linking the call site then the
* linkage fails and terminates abnormally. {@code E} is rethrown if the type of
* For a dynamically-computed constant, the result of the bootstrap method is cached
* as the resolved constant value.
* <p>
* If an exception, {@code E} say, occurs during execution of the bootstrap method, then
* resolution fails and terminates abnormally. {@code E} is rethrown if the type of
* {@code E} is {@code Error} or a subclass, otherwise a
* {@code BootstrapMethodError} that wraps {@code E} is thrown.
* If this happens, the same {@code Error} or subclass will the thrown for all
* subsequent attempts to execute the dynamic call site.
* <h2>timing of linkage</h2>
* A dynamic call site is linked just before its first execution.
* If this happens, the same error will be thrown for all
* subsequent attempts to execute the {@code invokedynamic} instruction or load the
* dynamically-computed constant.
*
* <h2>Timing of resolution</h2>
* An {@code invokedynamic} instruction is linked just before its first execution.
* A dynamically-computed constant is resolved just before the first time it is used
* (by pushing it on the stack or linking it as a bootstrap method parameter).
* The bootstrap method call implementing the linkage occurs within
* a thread that is attempting a first execution.
* a thread that is attempting a first execution or first use.
* <p>
* If there are several such threads, the bootstrap method may be
* invoked in several threads concurrently.
@ -119,7 +147,7 @@
* In any case, every {@code invokedynamic} instruction is either
* unlinked or linked to a unique {@code CallSite} object.
* <p>
* In an application which requires dynamic call sites with individually
* In an application which requires {@code invokedynamic} instructions with individually
* mutable behaviors, their bootstrap methods should produce distinct
* {@link java.lang.invoke.CallSite CallSite} objects, one for each linkage request.
* Alternatively, an application can link a single {@code CallSite} object
@ -127,53 +155,46 @@
* a change to the target method will become visible at each of
* the instructions.
* <p>
* If several threads simultaneously execute a bootstrap method for a single dynamic
* call site, the JVM must choose one {@code CallSite} object and install it visibly to
* If several threads simultaneously execute a bootstrap method for a single dynamically-computed
* call site or constant, the JVM must choose one bootstrap method result and install it visibly to
* all threads. Any other bootstrap method calls are allowed to complete, but their
* results are ignored, and their dynamic call site invocations proceed with the originally
* chosen target object.
* results are ignored.
* <p style="font-size:smaller;">
* <em>Discussion:</em>
* These rules do not enable the JVM to duplicate dynamic call sites,
* These rules do not enable the JVM to share call sites,
* or to issue &ldquo;causeless&rdquo; bootstrap method calls.
* Every dynamic call site transitions at most once from unlinked to linked,
* Every {@code invokedynamic} instruction transitions at most once from unlinked to linked,
* just before its first invocation.
* There is no way to undo the effect of a completed bootstrap method call.
*
* <h2>types of bootstrap methods</h2>
* As long as each bootstrap method can be correctly invoked
* by {@code MethodHandle.invoke}, its detailed type is arbitrary.
* <h2>Types of bootstrap methods</h2>
* For a dynamically-computed call site, the bootstrap method is invoked with parameter
* types {@code MethodHandles.Lookup}, {@code String}, {@code MethodType}, and the types
* of any static arguments; the return type is {@code CallSite}. For a
* dynamically-computed constant, the bootstrap method is invoked with parameter types
* {@code MethodHandles.Lookup}, {@code String}, {@code Class}, and the types of any
* static arguments; the return type is the type represented by the {@code Class}.
*
* Because {@link java.lang.invoke.MethodHandle#invoke MethodHandle.invoke} allows for
* adaptations between the invoked method type and the method handle's method type,
* there is flexibility in the declaration of the bootstrap method.
* For example, the first argument could be {@code Object}
* instead of {@code MethodHandles.Lookup}, and the return type
* could also be {@code Object} instead of {@code CallSite}.
* (Note that the types and number of the stacked arguments limit
* the legal kinds of bootstrap methods to appropriately typed
* static methods and constructors of {@code CallSite} subclasses.)
* static methods and constructors.)
* <p>
* If a given {@code invokedynamic} instruction specifies no static arguments,
* the instruction's bootstrap method will be invoked on three arguments,
* conveying the instruction's caller class, name, and method type.
* If the {@code invokedynamic} instruction specifies one or more static arguments,
* those values will be passed as additional arguments to the method handle.
* (Note that because there is a limit of 255 arguments to any method,
* at most 251 extra arguments can be supplied to a non-varargs bootstrap method,
* since the bootstrap method
* handle itself and its first three arguments must also be stacked.)
* The bootstrap method will be invoked as if by {@code MethodHandle.invokeWithArguments}.
* A variable-arity bootstrap method can accept thousands of static arguments,
* subject only by limits imposed by the class-file format.
* <p>
* The normal argument conversion rules for {@code MethodHandle.invoke} apply to all stacked arguments.
* For example, if a pushed value is a primitive type, it may be converted to a reference by boxing conversion.
* If a pushed value is a primitive type, it may be converted to a reference by boxing conversion.
* If the bootstrap method is a variable arity method (its modifier bit {@code 0x0080} is set),
* then some or all of the arguments specified here may be collected into a trailing array parameter.
* (This is not a special rule, but rather a useful consequence of the interaction
* between {@code CONSTANT_MethodHandle} constants, the modifier bit for variable arity methods,
* and the {@link java.lang.invoke.MethodHandle#asVarargsCollector asVarargsCollector} transformation.)
* <p>
* Given these rules, here are examples of legal bootstrap method declarations,
* given various numbers {@code N} of extra arguments.
* Given these rules, here are examples of legal bootstrap method declarations for
* dynamically-computed call sites, given various numbers {@code N} of extra arguments.
* The first row (marked {@code *}) will work for any number of extra arguments.
* <table class="plain" style="vertical-align:top">
* <caption style="display:none">Static argument types</caption>
@ -208,28 +229,27 @@
* {@code String}.
* The other examples work with all types of extra arguments.
* <p>
* As noted above, the actual method type of the bootstrap method can vary.
* For example, the fourth argument could be {@code MethodHandle},
* if that is the type of the corresponding constant in
* the {@code CONSTANT_InvokeDynamic} entry.
* In that case, the {@code MethodHandle.invoke} call will pass the extra method handle
* constant as an {@code Object}, but the type matching machinery of {@code MethodHandle.invoke}
* will cast the reference back to {@code MethodHandle} before invoking the bootstrap method.
* (If a string constant were passed instead, by badly generated code, that cast would then fail,
* resulting in a {@code BootstrapMethodError}.)
* <p>
* Note that, as a consequence of the above rules, the bootstrap method may accept a primitive
* argument, if it can be represented by a constant pool entry.
* Since dynamically-computed constants can be provided as static arguments to bootstrap
* methods, there are no limitations on the types of bootstrap arguments.
* However, arguments of type {@code boolean}, {@code byte}, {@code short}, or {@code char}
* cannot be created for bootstrap methods, since such constants cannot be directly
* represented in the constant pool, and the invocation of the bootstrap method will
* cannot be <em>directly</em> supplied by {@code CONSTANT_Integer}
* constant pool entries, since the {@code asType} conversions do
* not perform the necessary narrowing primitive conversions.
* <p>
* Extra bootstrap method arguments are intended to allow language implementors
* to safely and compactly encode metadata.
* In principle, the name and extra arguments are redundant,
* since each call site could be given its own unique bootstrap method.
* Such a practice would be likely to produce large class files and constant pools.
* In the above examples, the return type is always {@code CallSite},
* but that is not a necessary feature of bootstrap methods.
* In the case of a dynamically-computed call site, the only requirement is that
* the return type of the bootstrap method must be convertible
* (using the {@code asType} conversions) to {@code CallSite}, which
* means the bootstrap method return type might be {@code Object} or
* {@code ConstantCallSite}.
* In the case of a dynamically-resolved constant, the return type of the bootstrap
* method must be convertible to the type of the constant, as
* represented by its field type descriptor. For example, if the
* dynamic constant has a field type descriptor of {@code "C"}
* ({@code char}) then the bootstrap method return type could be
* {@code Object}, {@code Character}, or {@code char}, but not
* {@code int} or {@code Integer}.
*
* @author John Rose, JSR 292 EG
* @since 1.7