< 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 >