< prev index next >
src/java.base/share/classes/java/lang/invoke/AbstractBootstrapCallInfo.java
Print this page
rev 52749 : Bootstrap method consolidation
* clean up and simplify JDK support code for BSM invocation
* simplify JVM bootstrap handshake: use BootstrapCallInfo only
* remove unused JVM paths and data fields
* move bootstrap argument processing from MethodHandleNatives to ConstantPool
* remove ConstantGroup; merge argument access into BootstrapCallInfo
* adjust BSM argument access: remove copyArguments, add argumentRef API
* add metadata-free BSM modes, including symbolic arguments from CP
*** 23,204 ****
* 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;
}
--- 23,267 ----
* questions.
*/
package java.lang.invoke;
import jdk.internal.vm.annotation.Stable;
+ import java.lang.constant.ClassDesc;
+ import java.lang.constant.Constable;
+ import java.lang.constant.ConstantDesc;
+ import java.lang.constant.MethodTypeDesc;
+ import java.lang.invoke.MethodHandles.Lookup;
+ import java.util.*;
+ import java.util.function.IntFunction;
+
+ import static java.lang.invoke.BootstrapMethodInvoker.VM_BSCI;
+ import static java.lang.invoke.MethodHandleNatives.Constants.*;
import static java.lang.invoke.MethodHandleStatics.rangeCheck1;
import static java.lang.invoke.MethodHandleStatics.rangeCheck2;
! /**
! * Utility class for implementing BootstrapCallInfo.
! * Implements the three list-views on top of the three indexes accessors.
! * The {@link WithCache} subclass adds a backing store.
! */
/*non-public*/
! abstract class AbstractBootstrapCallInfo<T extends TypeDescriptor & Constable<T>>
! implements BootstrapCallInfo<T> {
/** The size of this constant group, set permanently by the constructor. */
! private final MethodHandle bsm;
! private final String name;
! private final TypeView<T> typeView;
! private final IntFunction<ConstantDesc<?>> symFinder = null; //@@
! protected final int argumentCount;
!
! // view caches:
! private @Stable ArgList argListF;
! private ArgList argListI; // can be reassigned for differing ifPresent values
! private @Stable List<ConstantDesc<?>> argListS;
! /**
! * Constructor, which takes permanent settings for the properties of the BSCI.
! * @param bsm bootstrap method, which must be previously resolved
! * @param name name string used for invocation or constant
! * @param typeView type used for invocation or constant (wrapped as resolved or unresolved)
! * @param argumentCount number of static arguments for bootstrap method
*/
! AbstractBootstrapCallInfo(MethodHandle bsm, String name, TypeView<T> typeView, int argumentCount) {
! this.bsm = bsm;
! this.name = name;
! this.typeView = typeView;
! this.argumentCount = argumentCount;
}
! @Override public MethodHandle bootstrapMethod() { return bsm; }
! @Override public String invocationName() { return name; }
!
! @Override public T invocationType() { return typeView.invocationType(); }
!
! @Override public ConstantDesc<T> invocationTypeDesc() { return typeView.invocationTypeDesc(); }
!
! @Override public final int argumentCount() { return argumentCount; }
!
! @Override
! public abstract Object argument(int index) throws LinkageError;
! @Override
! public abstract Object argument(int index, Object ifNotPresent);
! @Override
! public abstract ConstantDesc<?> argumentDesc(int index);
! @Override
! public abstract boolean argumentIsPresent(int index);
/**
! * Produce a string that briefly reports the BSM, name, type,
! * and argument list. For arguments, use a non-resolving list view,
! * with unresolved elements being presented as asterisks.
* @return {@code this.asList("*").toString()}
*/
@Override public String toString() {
! return BootstrapCallInfo.toString(this);
}
+ // (Do not override equals or hashCode, since this type is stateful.)
! /**
! * Representative of a resolved type mirror, an unresolved type descriptor, or both.
! * If one component is null, the {@code TypeView} can lazily resolve it from the other.
! * If the type component is null, a {@code Lookup} object must be supplied to resolve the type descriptor.
! * @param <T> the type {@code MethodType} or {@code Class}
! */
! static
! class TypeView<T extends TypeDescriptor & Constable<T>> {
! private @Stable T type;
! private @Stable ConstantDesc<T> typeDesc;
! private final Lookup lookup; // used only to resolve typeDesc
!
! public T invocationType() {
! if (type == null) resolve();
! return type;
! }
! public ConstantDesc<T> invocationTypeDesc() {
! if (typeDesc == null) resolve();
! return typeDesc;
! }
!
! private TypeView(T type, ConstantDesc<T> typeDesc) {
! Objects.requireNonNull(type);
! // typeDesc can be null, will be lazily reverse-resolved
! this.type = type;
! if (typeDesc != null) this.typeDesc = typeDesc;
! this.lookup = null;
! check();
! }
! TypeView(T type) {
! this(type, null);
! }
! TypeView(ConstantDesc<T> typeDesc, Lookup lookup) {
! Objects.requireNonNull(typeDesc);
! Objects.requireNonNull(lookup);
! this.typeDesc = typeDesc;
! this.lookup = lookup;
! check();
}
! private void check() {
! if (!(type == null ||
! type instanceof Class ||
! type instanceof MethodType)) {
! throw new IllegalArgumentException("must be class or method type: " + type);
}
! if (!(typeDesc == null ||
! typeDesc instanceof ClassDesc ||
! typeDesc instanceof MethodTypeDesc)) {
! throw new IllegalArgumentException("must be class or method type descriptor: " + typeDesc);
}
}
@Override
! public String toString() {
! Object res = typeDesc;
! if (type != null) {
! res = type;
! if (res instanceof Class)
! res = ((Class<?>)type).getSimpleName();
! }
! return res.toString();
}
! /** Fill in either field if it is null. */
! void resolve() {
! if (type != null && typeDesc == null) {
! typeDesc = type.describeConstable().orElse(null);
}
+ if (typeDesc != null && type == null) {
+ try {
+ type = typeDesc.resolveConstantDesc(lookup);
+ } catch (ReflectiveOperationException ex) {
+ Object what;
+ if (typeDesc instanceof TypeDescriptor)
+ what = ((TypeDescriptor) typeDesc).descriptorString();
+ else what = typeDesc.toString();
+ throw new NoClassDefFoundError("cannot resolve: " + what);
}
}
! // check required end-state
! if (type == null || typeDesc == null) {
! throw new InternalError("cannot resolve type");
}
}
}
! /// List-view machinery
@Override
! public List<Object> argumentList() {
! ArgList args = argListF;
! if (args == null)
! argListF = args = new ArgList(this, 0, argumentCount());
! return args;
}
@Override
! public List<Object> argumentList(Object ifNotPresent) {
! ArgList args = argListI;
! if (args == null || args.ifNotPresent != ifNotPresent)
! argListI = args = new ArgList(this, 0, argumentCount(), ifNotPresent);
! return args;
}
@Override
! public List<ConstantDesc<?>> argumentDescList() {
! List<ConstantDesc<?>> args = argListS;
! if (args == null)
! argListS = args = argumentDescList(this);
! return args;
}
! static List<Object> argumentList(AbstractBootstrapCallInfo<?> self) {
! return new ArgList(self, 0, self.argumentCount());
}
+ static List<Object> argumentList(AbstractBootstrapCallInfo<?> self, Object ifPresent) {
+ return new ArgList(self, 0, self.argumentCount(), ifPresent);
+ }
+ static List<ConstantDesc<?>> argumentDescList(AbstractBootstrapCallInfo<?> self) {
+ ArgList args = new ArgList(self, true, 0, self.argumentCount());
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ List<ConstantDesc<?>> result = (List) args;
+ return result;
}
! /** Non-public implementation of the three List views for BootstrapCallInfo. */
! static class ArgList extends AbstractList<Object> {
! private final BootstrapCallInfo<?> self;
private final int size;
private final int offset;
! private final byte resolving;
private final Object ifNotPresent;
! private ArgList(BootstrapCallInfo<?> self, int start, int end,
! byte resolving, Object ifNotPresent) {
this.self = self;
this.size = end - start;
this.offset = start;
this.resolving = resolving;
this.ifNotPresent = ifNotPresent;
! rangeCheck2(start, end, self.argumentCount());
}
! ArgList(BootstrapCallInfo<?> self, int start, int end) {
! this(self, start, end, BAR_FORCE, null);
}
! ArgList(BootstrapCallInfo<?> self, int start, int end,
Object ifNotPresent) {
! this(self, start, end, BAR_IFPRESENT, ifNotPresent);
! }
! ArgList(BootstrapCallInfo<?> self, boolean symRefs, int start, int end) {
! this(self, start, end, BAR_SYMREF, null);
}
private int mapIndex(int index) {
return rangeCheck1(index, size) + offset;
}
*** 206,341 ****
@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; }
}
}
--- 269,447 ----
@Override public final int size() {
return size;
}
@Override public Object get(int index) {
! if (resolving == BAR_FORCE)
! return self.argument(mapIndex(index));
! else if (resolving == BAR_SYMREF)
! return self.argumentDesc(index);
else
! return self.argument(mapIndex(index), ifNotPresent);
}
@Override public List<Object> subList(int start, int end) {
rangeCheck2(start, end, size);
! return new ArgList(self, offset + start, offset + end,
resolving, ifNotPresent);
}
@Override public Object[] toArray() {
return toArray(new Object[size]);
}
@Override public <T> T[] toArray(T[] a) {
+ if (!(self instanceof VM_BSCI))
+ return super.toArray(a);
int pad = a.length - size;
+ Object[] buf = a;
if (pad < 0) {
pad = 0;
! buf = a = Arrays.copyOf(a, size);
! }
! if (a.getClass() != Object[].class || resolving == BAR_SYMREF)
! buf = new Object[size]; // VM might store any kind of Object
! ((VM_BSCI<?>) self).copyVMArguments(offset, offset + size, a, 0, resolving, ifNotPresent);
! if (buf != a) {
! // If a is the wrong array type, and the VM sends us a bad
! // object, the arraycopy call is where the error will be reported.
! System.arraycopy(buf, 0, a, 0, buf.length);
}
if (pad > 0) a[size] = null;
return a;
}
}
! abstract static
! class WithCache<T extends TypeDescriptor & Constable<T>>
! extends AbstractBootstrapCallInfo<T> {
! protected @Stable final Object[] cache;
! protected @Stable ConstantDesc<?>[] symCache;
! WithCache(MethodHandle bsm, String name, TypeView<T> typeView, int argumentCount) {
! super(bsm, name, typeView, argumentCount);
// It is caller's responsibility to initialize the cache.
// Initial contents are all-null, which means nothing is present.
! cache = new Object[argumentCount];
! // create symCache lazily
! }
!
! WithCache(MethodHandle bsm, String name, TypeView<T> typeView, Object[] arguments) {
! super(bsm, name, typeView, arguments.length);
! cache = arguments; // caller may have pre-loaded stuff into the cache
! }
!
! WithCache(MethodHandle bsm, String name, TypeView<T> typeView, ConstantDesc<?>[] symbolicRefs) {
! super(bsm, name, typeView, symbolicRefs.length);
! cache = new Object[argumentCount];
! symCache = symbolicRefs;
}
+ // WithCache(MethodHandle bsm, String name, TypeView<T> typeView, List<Object> allResolved) {
+ // this(bsm, name, typeView, allResolved.size());
+ // initializeCache(allResolved);
+ // }
+
+ void initializeCache(List<Object> cacheContents) {
+ initializeCache(cacheContents, new Object());
+ }
void initializeCache(List<Object> cacheContents, Object ifNotPresent) {
! // Replace ifNotPresent with null,
// 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
! cache[i] = wrapNull(x);
}
}
! @Override public Object argument(int i) {
Object x = cache[i];
// @Stable array must use null for sentinel
! if (x == null) {
! x = wrapNull(resolveConstant(i));
! // racy CAS:
! Object x2 = cache[i];
! if (x2 != null) x = x2;
! else cache[i] = x;
! }
return unwrapNull(x);
}
! @Override public Object argument(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 ConstantDesc<?> argumentDesc(int i) {
+ ConstantDesc<?>[] symCache = this.symCache;
+ if (symCache == null) {
+ this.symCache = symCache = new ConstantDesc<?>[argumentCount()];
+ }
+ ConstantDesc<?> x = symCache[i];
+ if (x == null) {
+ x = findSymbol(i);
+ // racy CAS:
+ ConstantDesc<?> x2 = symCache[i];
+ if (x2 != null) x = x2;
+ else symCache[i] = x;
+ }
+ return x;
+ }
+
@Override
! public boolean argumentIsPresent(int i) {
! return cache[i] != null; //NULL_MEANS_NOT_PRESENT
}
/** hook for local subclasses */
! abstract Object resolveConstant(int i) throws LinkageError;
!
! /** hook for local subclasses */
! abstract ConstantDesc<?> findSymbol(int i);
!
! // carefully try to expose the underlying object array of arguments
! // return null if this is not possible
! Object[] maybeShareArguments() {
! // fill in missing cache items, and look for null
! for (int i = 0; i < cache.length; i++) {
! Object cx = cache[i];
! Object rx = cx;
! if (cx == null) { // empty cache entry
! rx = argument(i);
! cx = cache[i]; // reload filled cache entry
! } else if (cx == RESOLVED_TO_NULL) {
! rx = null;
! }
! // Punt if the cache contains a masked null value.
! // Also punt if cache doesn't contain a value.
! if (cx == null || rx == null) return null;
! }
! return cache;
}
/// routines for mapping between null sentinel and true resolved null
! Object wrapNull(Object x) {
return x == null ? RESOLVED_TO_NULL : x;
}
! 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
! private static final Object RESOLVED_TO_NULL = new Object();
}
! static Object[] maybeShareArguments(BootstrapCallInfo<?> bsci) {
! if (bsci instanceof WithCache) {
! WithCache<?> wc = (WithCache<?>) bsci;
! return wc.maybeShareArguments();
}
! return null;
}
}
< prev index next >