--- old/src/java.base/share/classes/java/lang/invoke/AbstractConstantGroup.java 2018-09-28 11:55:08.000000000 -0700 +++ /dev/null 2018-09-28 11:55:08.000000000 -0700 @@ -1,341 +0,0 @@ -/* - * 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 { - 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 asList() { - return new AsList(self, offset, offset + size); - } - - @Override - public List 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 { - 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 iterator() { - if (resolving) - return new AsIterator(self, offset, offset + size); - else - return new AsIterator(self, offset, offset + size, ifNotPresent); - } - - @Override public List 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[] 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 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 extends WithCache implements BootstrapCallInfo { - 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; } - } -} --- /dev/null 2018-09-28 11:55:08.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/AbstractBootstrapCallInfo.java 2018-09-28 11:55:08.000000000 -0700 @@ -0,0 +1,447 @@ +/* + * 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 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> + implements BootstrapCallInfo { + /** The size of this constant group, set permanently by the constructor. */ + private final MethodHandle bsm; + private final String name; + private final TypeView typeView; + private final IntFunction> 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> 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 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 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 the type {@code MethodType} or {@code Class} + */ + static + class TypeView> { + private @Stable T type; + private @Stable ConstantDesc typeDesc; + private final Lookup lookup; // used only to resolve typeDesc + + public T invocationType() { + if (type == null) resolve(); + return type; + } + public ConstantDesc invocationTypeDesc() { + if (typeDesc == null) resolve(); + return typeDesc; + } + + private TypeView(T type, ConstantDesc 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 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 argumentList() { + ArgList args = argListF; + if (args == null) + argListF = args = new ArgList(this, 0, argumentCount()); + return args; + } + + @Override + public List 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> argumentDescList() { + List> args = argListS; + if (args == null) + argListS = args = argumentDescList(this); + return args; + } + + static List argumentList(AbstractBootstrapCallInfo self) { + return new ArgList(self, 0, self.argumentCount()); + } + static List argumentList(AbstractBootstrapCallInfo self, Object ifPresent) { + return new ArgList(self, 0, self.argumentCount(), ifPresent); + } + static List> argumentDescList(AbstractBootstrapCallInfo self) { + ArgList args = new ArgList(self, true, 0, self.argumentCount()); + @SuppressWarnings({"unchecked", "rawtypes"}) + List> result = (List) args; + return result; + } + + /** Non-public implementation of the three List views for BootstrapCallInfo. */ + static class ArgList extends AbstractList { + 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; + } + + @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 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[] 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> + extends AbstractBootstrapCallInfo { + protected @Stable final Object[] cache; + protected @Stable ConstantDesc[] symCache; + + WithCache(MethodHandle bsm, String name, TypeView 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 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 typeView, ConstantDesc[] symbolicRefs) { + super(bsm, name, typeView, symbolicRefs.length); + cache = new Object[argumentCount]; + symCache = symbolicRefs; + } + +// WithCache(MethodHandle bsm, String name, TypeView typeView, List allResolved) { +// this(bsm, name, typeView, allResolved.size()); +// initializeCache(allResolved); +// } + + void initializeCache(List cacheContents) { + initializeCache(cacheContents, new Object()); + } + void initializeCache(List 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; + } +}