< 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,182 +23,245 @@
* questions.
*/
package java.lang.invoke;
-import java.util.*;
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 ConstantGroup. */
+/**
+ * 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 AbstractConstantGroup implements ConstantGroup {
+abstract class AbstractBootstrapCallInfo<T extends TypeDescriptor & Constable<T>>
+ implements BootstrapCallInfo<T> {
/** The size of this constant group, set permanently by the constructor. */
- protected final int size;
+ 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;
- /** The constructor requires the size of the constant group being represented.
- * @param size the size of this constant group, set permanently by the constructor
+ /**
+ * 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
*/
- AbstractConstantGroup(int size) {
- this.size = size;
+ AbstractBootstrapCallInfo(MethodHandle bsm, String name, TypeView<T> typeView, int argumentCount) {
+ this.bsm = bsm;
+ this.name = name;
+ this.typeView = typeView;
+ this.argumentCount = argumentCount;
}
- @Override public final int size() {
- return size;
- }
+ @Override public MethodHandle bootstrapMethod() { return bsm; }
- public abstract Object get(int index) throws LinkageError;
+ @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;
- public abstract Object get(int index, Object ifNotPresent);
+ @Override
+ public abstract Object argument(int index, Object ifNotPresent);
- public abstract boolean isPresent(int index);
+ @Override
+ public abstract ConstantDesc<?> argumentDesc(int index);
- // Do not override equals or hashCode, since this type is stateful.
+ @Override
+ public abstract boolean argumentIsPresent(int index);
/**
- * Produce a string using the non-resolving list view,
- * where unresolved elements are presented as asterisks.
+ * 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 asList("*").toString();
+ return BootstrapCallInfo.toString(this);
}
+ // (Do not override equals or hashCode, since this type is stateful.)
- static class AsIterator implements Iterator<Object> {
- private final ConstantGroup self;
- private final int end;
- private final boolean resolving;
- private final Object ifNotPresent;
+ /**
+ * 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();
+ }
- // Mutable state:
- private int index;
+ TypeView(T type) {
+ this(type, null);
+ }
- 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;
+ TypeView(ConstantDesc<T> typeDesc, Lookup lookup) {
+ Objects.requireNonNull(typeDesc);
+ Objects.requireNonNull(lookup);
+ this.typeDesc = typeDesc;
+ this.lookup = lookup;
+ check();
}
- AsIterator(ConstantGroup self, int start, int end) {
- this(self, start, end, true, null);
+ private void check() {
+ if (!(type == null ||
+ type instanceof Class ||
+ type instanceof MethodType)) {
+ throw new IllegalArgumentException("must be class or method type: " + type);
}
- AsIterator(ConstantGroup self, int start, int end,
- Object ifNotPresent) {
- this(self, start, end, false, ifNotPresent);
+ if (!(typeDesc == null ||
+ typeDesc instanceof ClassDesc ||
+ typeDesc instanceof MethodTypeDesc)) {
+ throw new IllegalArgumentException("must be class or method type descriptor: " + typeDesc);
}
-
- @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);
+ public String toString() {
+ Object res = typeDesc;
+ if (type != null) {
+ res = type;
+ if (res instanceof Class)
+ res = ((Class<?>)type).getSimpleName();
+ }
+ return res.toString();
}
- private int bumpIndex() {
- int i = index;
- if (i >= end) throw new NoSuchElementException();
- index = i+1;
- return i;
+ /** 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);
}
-
- 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;
+ // check required end-state
+ if (type == null || typeDesc == null) {
+ throw new InternalError("cannot resolve type");
}
-
- @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));
- }
+ /// List-view machinery
@Override
- public ConstantGroup subGroup(int start, int end) {
- rangeCheck2(start, end, size);
- return new SubGroup(self, offset + start, offset + end);
+ public List<Object> argumentList() {
+ ArgList args = argListF;
+ if (args == null)
+ argListF = args = new ArgList(this, 0, argumentCount());
+ return args;
}
@Override
- public List<Object> asList() {
- return new AsList(self, offset, offset + size);
- }
+ 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<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);
+ public List<ConstantDesc<?>> argumentDescList() {
+ List<ConstantDesc<?>> args = argListS;
+ if (args == null)
+ argListS = args = argumentDescList(this);
+ return args;
}
- @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 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;
}
- static class AsList extends AbstractList<Object> {
- private final ConstantGroup self;
+ /** 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 boolean resolving;
+ private final byte resolving;
private final Object ifNotPresent;
- private AsList(ConstantGroup self, int start, int end,
- boolean resolving, 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.size());
+ rangeCheck2(start, end, self.argumentCount());
}
- AsList(ConstantGroup self, int start, int end) {
- this(self, start, end, true, null);
+ ArgList(BootstrapCallInfo<?> self, int start, int end) {
+ this(self, start, end, BAR_FORCE, null);
}
- AsList(ConstantGroup self, int start, int end,
+ ArgList(BootstrapCallInfo<?> self, int start, int end,
Object ifNotPresent) {
- this(self, start, end, false, 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,136 +269,179 @@
@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);
+ if (resolving == BAR_FORCE)
+ return self.argument(mapIndex(index));
+ else if (resolving == BAR_SYMREF)
+ return self.argumentDesc(index);
else
- return new AsIterator(self, offset, offset + size, ifNotPresent);
+ return self.argument(mapIndex(index), ifNotPresent);
}
@Override public List<Object> subList(int start, int end) {
rangeCheck2(start, end, size);
- return new AsList(self, offset + start, offset + end,
+ 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;
- a = Arrays.copyOf(a, size);
+ 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 (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;
+ abstract static
+ class WithCache<T extends TypeDescriptor & Constable<T>>
+ extends AbstractBootstrapCallInfo<T> {
+ protected @Stable final Object[] cache;
+ protected @Stable ConstantDesc<?>[] symCache;
- WithCache(int size) {
- super(size);
+ 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[size];
+ 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 NOT_PRESENT,
+ // 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
- if (x == null)
- x = RESOLVED_TO_NULL;
- cache[i] = x;
+ cache[i] = wrapNull(x);
}
}
- @Override public Object get(int i) {
+ @Override public Object argument(int i) {
Object x = cache[i];
// @Stable array must use null for sentinel
- if (x == null) x = fillCache(i);
+ 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 get(int i, Object ifNotAvailable) {
+ @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 isPresent(int i) {
- return cache[i] != null;
+ public boolean argumentIsPresent(int i) {
+ return cache[i] != null; //NULL_MEANS_NOT_PRESENT
}
/** hook for local subclasses */
- Object fillCache(int i) {
- throw new NoSuchElementException("constant group does not contain element #"+i);
+ 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
- static Object wrapNull(Object x) {
+ Object wrapNull(Object x) {
return x == null ? RESOLVED_TO_NULL : x;
}
- static Object unwrapNull(Object 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
- 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();
+ private static final Object RESOLVED_TO_NULL = new Object();
}
- 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);
+ static Object[] maybeShareArguments(BootstrapCallInfo<?> bsci) {
+ if (bsci instanceof WithCache) {
+ WithCache<?> wc = (WithCache<?>) bsci;
+ return wc.maybeShareArguments();
}
-
- @Override public MethodHandle bootstrapMethod() { return bsm; }
- @Override public String invocationName() { return name; }
- @Override public T invocationType() { return type; }
+ return null;
}
}
< prev index next >