1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.lang.invoke;
  27 
  28 import java.util.*;
  29 import jdk.internal.vm.annotation.Stable;
  30 
  31 import static java.lang.invoke.MethodHandleStatics.rangeCheck1;
  32 import static java.lang.invoke.MethodHandleStatics.rangeCheck2;
  33 
  34 /** Utility class for implementing ConstantGroup. */
  35 /*non-public*/
  36 abstract class AbstractConstantGroup implements ConstantGroup {
  37     /** The size of this constant group, set permanently by the constructor. */
  38     protected final int size;
  39 
  40     /** The constructor requires the size of the constant group being represented.
  41      * @param size the size of this constant group, set permanently by the constructor
  42      */
  43     AbstractConstantGroup(int size) {
  44         this.size = size;
  45     }
  46 
  47     @Override public final int size() {
  48         return size;
  49     }
  50 
  51     public abstract Object get(int index) throws LinkageError;
  52 
  53     public abstract Object get(int index, Object ifNotPresent);
  54 
  55     public abstract boolean isPresent(int index);
  56 
  57     // Do not override equals or hashCode, since this type is stateful.
  58 
  59     /**
  60      * Produce a string using the non-resolving list view,
  61      * where unresolved elements are presented as asterisks.
  62      * @return {@code this.asList("*").toString()}
  63      */
  64     @Override public String toString() {
  65         return asList("*").toString();
  66     }
  67 
  68     static class AsIterator implements Iterator<Object> {
  69         private final ConstantGroup self;
  70         private final int end;
  71         private final boolean resolving;
  72         private final Object ifNotPresent;
  73 
  74         // Mutable state:
  75         private int index;
  76 
  77         private AsIterator(ConstantGroup self, int start, int end,
  78                          boolean resolving, Object ifNotPresent) {
  79             this.self = self;
  80             this.end = end;
  81             this.index = start;
  82             this.resolving = resolving;
  83             this.ifNotPresent = ifNotPresent;
  84         }
  85         AsIterator(ConstantGroup self, int start, int end) {
  86             this(self, start, end, true, null);
  87         }
  88         AsIterator(ConstantGroup self, int start, int end,
  89                  Object ifNotPresent) {
  90             this(self, start, end, false, ifNotPresent);
  91         }
  92 
  93         @Override
  94         public boolean hasNext() {
  95             return index < end;
  96         }
  97 
  98         @Override
  99         public Object next() {
 100             int i = bumpIndex();
 101             if (resolving)
 102                 return self.get(i);
 103             else
 104                 return self.get(i, ifNotPresent);
 105         }
 106 
 107         private int bumpIndex() {
 108             int i = index;
 109             if (i >= end)  throw new NoSuchElementException();
 110             index = i+1;
 111             return i;
 112         }
 113     }
 114 
 115     static class SubGroup extends AbstractConstantGroup {
 116         private final ConstantGroup self;  // the real CG
 117         private final int offset;  // offset within myself
 118         SubGroup(ConstantGroup self, int start, int end) {
 119             super(end - start);
 120             this.self = self;
 121             this.offset = start;
 122             rangeCheck2(start, end, size);
 123         }
 124 
 125         private int mapIndex(int index) {
 126             return rangeCheck1(index, size) + offset;
 127         }
 128 
 129         @Override
 130         public Object get(int index) {
 131             return self.get(mapIndex(index));
 132         }
 133 
 134         @Override
 135         public Object get(int index, Object ifNotPresent) {
 136             return self.get(mapIndex(index), ifNotPresent);
 137         }
 138 
 139         @Override
 140         public boolean isPresent(int index) {
 141             return self.isPresent(mapIndex(index));
 142         }
 143 
 144         @Override
 145         public ConstantGroup subGroup(int start, int end) {
 146             rangeCheck2(start, end, size);
 147             return new SubGroup(self, offset + start, offset + end);
 148         }
 149 
 150         @Override
 151         public List<Object> asList() {
 152             return new AsList(self, offset, offset + size);
 153         }
 154 
 155         @Override
 156         public List<Object> asList(Object ifNotPresent) {
 157             return new AsList(self, offset, offset + size, ifNotPresent);
 158         }
 159 
 160         @Override
 161         public int copyConstants(int start, int end,
 162                                   Object[] buf, int pos) throws LinkageError {
 163             rangeCheck2(start, end, size);
 164             return self.copyConstants(offset + start, offset + end,
 165                                       buf, pos);
 166         }
 167 
 168         @Override
 169         public int copyConstants(int start, int end,
 170                                   Object[] buf, int pos,
 171                                   Object ifNotPresent) {
 172             rangeCheck2(start, end, size);
 173             return self.copyConstants(offset + start, offset + end,
 174                                       buf, pos, ifNotPresent);
 175         }
 176     }
 177 
 178     static class AsList extends AbstractList<Object> {
 179         private final ConstantGroup self;
 180         private final int size;
 181         private final int offset;
 182         private final boolean resolving;
 183         private final Object ifNotPresent;
 184 
 185         private AsList(ConstantGroup self, int start, int end,
 186                        boolean resolving, Object ifNotPresent) {
 187             this.self = self;
 188             this.size = end - start;
 189             this.offset = start;
 190             this.resolving = resolving;
 191             this.ifNotPresent = ifNotPresent;
 192             rangeCheck2(start, end, self.size());
 193         }
 194         AsList(ConstantGroup self, int start, int end) {
 195             this(self, start, end, true, null);
 196         }
 197         AsList(ConstantGroup self, int start, int end,
 198                Object ifNotPresent) {
 199             this(self, start, end, false, ifNotPresent);
 200         }
 201 
 202         private int mapIndex(int index) {
 203             return rangeCheck1(index, size) + offset;
 204         }
 205 
 206         @Override public final int size() {
 207             return size;
 208         }
 209 
 210         @Override public Object get(int index) {
 211             if (resolving)
 212                 return self.get(mapIndex(index));
 213             else
 214                 return self.get(mapIndex(index), ifNotPresent);
 215         }
 216 
 217         @Override
 218         public Iterator<Object> iterator() {
 219             if (resolving)
 220                 return new AsIterator(self, offset, offset + size);
 221             else
 222                 return new AsIterator(self, offset, offset + size, ifNotPresent);
 223         }
 224 
 225         @Override public List<Object> subList(int start, int end) {
 226             rangeCheck2(start, end, size);
 227             return new AsList(self, offset + start, offset + end,
 228                               resolving, ifNotPresent);
 229         }
 230 
 231         @Override public Object[] toArray() {
 232             return toArray(new Object[size]);
 233         }
 234         @Override public <T> T[] toArray(T[] a) {
 235             int pad = a.length - size;
 236             if (pad < 0) {
 237                 pad = 0;
 238                 a = Arrays.copyOf(a, size);
 239             }
 240             if (resolving)
 241                 self.copyConstants(offset, offset + size, a, 0);
 242             else
 243                 self.copyConstants(offset, offset + size, a, 0,
 244                                    ifNotPresent);
 245             if (pad > 0)  a[size] = null;
 246             return a;
 247         }
 248     }
 249 
 250     static abstract
 251     class WithCache extends AbstractConstantGroup {
 252         @Stable final Object[] cache;
 253 
 254         WithCache(int size) {
 255             super(size);
 256             // It is caller's responsibility to initialize the cache.
 257             // Initial contents are all-null, which means nothing is present.
 258             cache = new Object[size];
 259         }
 260 
 261         void initializeCache(List<Object> cacheContents, Object ifNotPresent) {
 262             // Replace ifNotPresent with NOT_PRESENT,
 263             // and null with RESOLVED_TO_NULL.
 264             // Then forget about the user-provided ifNotPresent.
 265             for (int i = 0; i < cache.length; i++) {
 266                 Object x = cacheContents.get(i);
 267                 if (x == ifNotPresent)
 268                     continue;  // leave the null in place
 269                 if (x == null)
 270                     x = RESOLVED_TO_NULL;
 271                 cache[i] = x;
 272             }
 273         }
 274 
 275         @Override public Object get(int i) {
 276             Object x = cache[i];
 277             // @Stable array must use null for sentinel
 278             if (x == null)  x = fillCache(i);
 279             return unwrapNull(x);
 280         }
 281 
 282         @Override public Object get(int i, Object ifNotAvailable) {
 283             Object x = cache[i];
 284             // @Stable array must use null for sentinel
 285             if (x == null)  return ifNotAvailable;
 286             return unwrapNull(x);
 287         }
 288 
 289         @Override
 290         public boolean isPresent(int i) {
 291             return cache[i] != null;
 292         }
 293 
 294         /** hook for local subclasses */
 295         Object fillCache(int i) {
 296             throw new NoSuchElementException("constant group does not contain element #"+i);
 297         }
 298 
 299         /// routines for mapping between null sentinel and true resolved null
 300 
 301         static Object wrapNull(Object x) {
 302             return x == null ? RESOLVED_TO_NULL : x;
 303         }
 304 
 305         static Object unwrapNull(Object x) {
 306             assert(x != null);
 307             return x == RESOLVED_TO_NULL ? null : x;
 308         }
 309 
 310         // secret sentinel for an actual null resolved value, in the cache
 311         static final Object RESOLVED_TO_NULL = new Object();
 312 
 313         // secret sentinel for a "hole" in the cache:
 314         static final Object NOT_PRESENT = new Object();
 315 
 316     }
 317 
 318     /** Skeleton implementation of BootstrapCallInfo. */
 319     static
 320     class BSCIWithCache<T> extends WithCache implements BootstrapCallInfo<T> {
 321         private final MethodHandle bsm;
 322         private final String name;
 323         private final T type;
 324 
 325         @Override public String toString() {
 326             return bsm+"/"+name+":"+type+super.toString();
 327         }
 328 
 329         BSCIWithCache(MethodHandle bsm, String name, T type, int size) {
 330             super(size);
 331             this.type = type;
 332             this.bsm = bsm;
 333             this.name = name;
 334             assert(type instanceof Class || type instanceof MethodType);
 335         }
 336 
 337         @Override public MethodHandle bootstrapMethod() { return bsm; }
 338         @Override public String invocationName() { return name; }
 339         @Override public T invocationType() { return type; }
 340     }
 341 }