mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 14:24:46 +02:00
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:
parent
52d3bf29b2
commit
e55a05957d
114 changed files with 11762 additions and 404 deletions
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
287
src/java.base/share/classes/java/lang/invoke/ConstantGroup.java
Normal file
287
src/java.base/share/classes/java/lang/invoke/ConstantGroup.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 “causeless” 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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue