diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/java.base/share/classes/java/lang/invoke/AbstractConstantGroup.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/java.base/share/classes/java/lang/invoke/AbstractConstantGroup.java Fri Sep 08 10:46:46 2017 -0700 @@ -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 { + 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; } + } +}