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