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 jdk.internal.vm.annotation.Stable;
  29 
  30 import java.lang.constant.ClassDesc;
  31 import java.lang.constant.Constable;
  32 import java.lang.constant.ConstantDesc;
  33 import java.lang.constant.MethodTypeDesc;
  34 import java.lang.invoke.MethodHandles.Lookup;
  35 import java.util.*;
  36 import java.util.function.IntFunction;
  37 
  38 import static java.lang.invoke.BootstrapMethodInvoker.VM_BSCI;
  39 import static java.lang.invoke.MethodHandleNatives.Constants.*;
  40 import static java.lang.invoke.MethodHandleStatics.rangeCheck1;
  41 import static java.lang.invoke.MethodHandleStatics.rangeCheck2;
  42 
  43 /**
  44  * Utility class for implementing BootstrapCallInfo.
  45  * Implements the three list-views on top of the three indexes accessors.
  46  * The {@link WithCache} subclass adds a backing store.
  47  */
  48 /*non-public*/
  49 abstract class AbstractBootstrapCallInfo<T extends TypeDescriptor & Constable<T>>
  50         implements BootstrapCallInfo<T> {
  51     /** The size of this constant group, set permanently by the constructor. */
  52     private final MethodHandle bsm;
  53     private final String name;
  54     private final TypeView<T> typeView;
  55     private final IntFunction<ConstantDesc<?>> symFinder = null; //@@
  56     protected final int argumentCount;
  57 
  58     // view caches:
  59     private @Stable ArgList argListF;
  60     private ArgList argListI;  // can be reassigned for differing ifPresent values
  61     private @Stable List<ConstantDesc<?>> argListS;
  62 
  63     /**
  64      * Constructor, which takes permanent settings for the properties of the BSCI.
  65      * @param bsm  bootstrap method, which must be previously resolved
  66      * @param name  name string used for invocation or constant
  67      * @param typeView  type used for invocation or constant (wrapped as resolved or unresolved)
  68      * @param argumentCount  number of static arguments for bootstrap method
  69      */
  70     AbstractBootstrapCallInfo(MethodHandle bsm, String name, TypeView<T> typeView, int argumentCount) {
  71         this.bsm = bsm;
  72         this.name = name;
  73         this.typeView = typeView;
  74         this.argumentCount = argumentCount;
  75     }
  76 
  77     @Override public MethodHandle bootstrapMethod() { return bsm; }
  78 
  79     @Override public String invocationName() { return name; }
  80 
  81     @Override public T invocationType() { return typeView.invocationType(); }
  82 
  83     @Override public ConstantDesc<T> invocationTypeDesc() { return typeView.invocationTypeDesc(); }
  84 
  85     @Override public final int argumentCount() { return argumentCount; }
  86 
  87     @Override
  88     public abstract Object argument(int index) throws LinkageError;
  89 
  90     @Override
  91     public abstract Object argument(int index, Object ifNotPresent);
  92 
  93     @Override
  94     public abstract ConstantDesc<?> argumentDesc(int index);
  95 
  96     @Override
  97     public abstract boolean argumentIsPresent(int index);
  98 
  99     /**
 100      * Produce a string that briefly reports the BSM, name, type,
 101      * and argument list.  For arguments, use a non-resolving list view,
 102      * with unresolved elements being presented as asterisks.
 103      * @return {@code this.asList("*").toString()}
 104      */
 105     @Override public String toString() {
 106         return BootstrapCallInfo.toString(this);
 107     }
 108     // (Do not override equals or hashCode, since this type is stateful.)
 109 
 110     /**
 111      * Representative of a resolved type mirror, an unresolved type descriptor, or both.
 112      * If one component is null, the {@code TypeView} can lazily resolve it from the other.
 113      * If the type component is null, a {@code Lookup} object must be supplied to resolve the type descriptor.
 114      * @param <T> the type {@code MethodType} or {@code Class}
 115      */
 116     static
 117     class TypeView<T extends TypeDescriptor & Constable<T>> {
 118         private @Stable T type;
 119         private @Stable ConstantDesc<T> typeDesc;
 120         private final Lookup lookup;  // used only to resolve typeDesc
 121 
 122         public T invocationType() {
 123             if (type == null)  resolve();
 124             return type;
 125         }
 126         public ConstantDesc<T> invocationTypeDesc() {
 127             if (typeDesc == null)  resolve();
 128             return typeDesc;
 129         }
 130 
 131         private TypeView(T type, ConstantDesc<T> typeDesc) {
 132             Objects.requireNonNull(type);
 133             // typeDesc can be null, will be lazily reverse-resolved
 134             this.type = type;
 135             if (typeDesc != null)  this.typeDesc = typeDesc;
 136             this.lookup = null;
 137             check();
 138         }
 139 
 140         TypeView(T type) {
 141             this(type, null);
 142         }
 143 
 144         TypeView(ConstantDesc<T> typeDesc, Lookup lookup) {
 145             Objects.requireNonNull(typeDesc);
 146             Objects.requireNonNull(lookup);
 147             this.typeDesc = typeDesc;
 148             this.lookup = lookup;
 149             check();
 150         }
 151         private void check() {
 152             if (!(type == null ||
 153                   type instanceof Class ||
 154                   type instanceof MethodType)) {
 155                 throw new IllegalArgumentException("must be class or method type: " + type);
 156             }
 157             if (!(typeDesc == null ||
 158                     typeDesc instanceof ClassDesc ||
 159                     typeDesc instanceof MethodTypeDesc)) {
 160                 throw new IllegalArgumentException("must be class or method type descriptor: " + typeDesc);
 161             }
 162         }
 163         @Override
 164         public String toString() {
 165             Object res = typeDesc;
 166             if (type != null) {
 167                 res = type;
 168                 if (res instanceof Class)
 169                     res = ((Class<?>)type).getSimpleName();
 170             }
 171             return res.toString();
 172         }
 173 
 174         /** Fill in either field if it is null. */
 175         void resolve() {
 176             if (type != null && typeDesc == null) {
 177                 typeDesc = type.describeConstable().orElse(null);
 178             }
 179             if (typeDesc != null && type == null) {
 180                 try {
 181                     type = typeDesc.resolveConstantDesc(lookup);
 182                 } catch (ReflectiveOperationException ex) {
 183                     Object what;
 184                     if (typeDesc instanceof TypeDescriptor)
 185                         what = ((TypeDescriptor) typeDesc).descriptorString();
 186                     else what = typeDesc.toString();
 187                     throw new NoClassDefFoundError("cannot resolve: " + what);
 188                 }
 189             }
 190             // check required end-state
 191             if (type == null || typeDesc == null) {
 192                 throw new InternalError("cannot resolve type");
 193             }
 194         }
 195     }
 196 
 197     /// List-view machinery
 198 
 199     @Override
 200     public List<Object> argumentList() {
 201         ArgList args = argListF;
 202         if (args == null)
 203             argListF = args = new ArgList(this, 0, argumentCount());
 204         return args;
 205     }
 206 
 207     @Override
 208     public List<Object> argumentList(Object ifNotPresent) {
 209         ArgList args = argListI;
 210         if (args == null || args.ifNotPresent != ifNotPresent)
 211             argListI = args = new ArgList(this, 0, argumentCount(), ifNotPresent);
 212         return args;
 213 
 214     }
 215 
 216     @Override
 217     public List<ConstantDesc<?>> argumentDescList() {
 218         List<ConstantDesc<?>> args = argListS;
 219         if (args == null)
 220             argListS = args = argumentDescList(this);
 221         return args;
 222     }
 223 
 224     static List<Object> argumentList(AbstractBootstrapCallInfo<?> self) {
 225         return new ArgList(self, 0, self.argumentCount());
 226     }
 227     static List<Object> argumentList(AbstractBootstrapCallInfo<?> self, Object ifPresent) {
 228         return new ArgList(self, 0, self.argumentCount(), ifPresent);
 229     }
 230     static List<ConstantDesc<?>> argumentDescList(AbstractBootstrapCallInfo<?> self) {
 231         ArgList args = new ArgList(self, true, 0, self.argumentCount());
 232         @SuppressWarnings({"unchecked", "rawtypes"})
 233         List<ConstantDesc<?>> result = (List) args;
 234         return result;
 235     }
 236 
 237     /** Non-public implementation of the three List views for BootstrapCallInfo. */
 238     static class ArgList extends AbstractList<Object> {
 239         private final BootstrapCallInfo<?> self;
 240         private final int size;
 241         private final int offset;
 242         private final byte resolving;
 243         private final Object ifNotPresent;
 244 
 245         private ArgList(BootstrapCallInfo<?> self, int start, int end,
 246                         byte resolving, Object ifNotPresent) {
 247             this.self = self;
 248             this.size = end - start;
 249             this.offset = start;
 250             this.resolving = resolving;
 251             this.ifNotPresent = ifNotPresent;
 252             rangeCheck2(start, end, self.argumentCount());
 253         }
 254         ArgList(BootstrapCallInfo<?> self, int start, int end) {
 255             this(self, start, end, BAR_FORCE, null);
 256         }
 257         ArgList(BootstrapCallInfo<?> self, int start, int end,
 258                 Object ifNotPresent) {
 259             this(self, start, end, BAR_IFPRESENT, ifNotPresent);
 260         }
 261         ArgList(BootstrapCallInfo<?> self, boolean symRefs, int start, int end) {
 262             this(self, start, end, BAR_SYMREF, null);
 263         }
 264 
 265         private int mapIndex(int index) {
 266             return rangeCheck1(index, size) + offset;
 267         }
 268 
 269         @Override public final int size() {
 270             return size;
 271         }
 272 
 273         @Override public Object get(int index) {
 274             if (resolving == BAR_FORCE)
 275                 return self.argument(mapIndex(index));
 276             else if (resolving == BAR_SYMREF)
 277                 return self.argumentDesc(index);
 278             else
 279                 return self.argument(mapIndex(index), ifNotPresent);
 280         }
 281 
 282         @Override public List<Object> subList(int start, int end) {
 283             rangeCheck2(start, end, size);
 284             return new ArgList(self, offset + start, offset + end,
 285                               resolving, ifNotPresent);
 286         }
 287 
 288         @Override public Object[] toArray() {
 289             return toArray(new Object[size]);
 290         }
 291         @Override public <T> T[] toArray(T[] a) {
 292             if (!(self instanceof VM_BSCI))
 293                 return super.toArray(a);
 294             int pad = a.length - size;
 295             Object[] buf = a;
 296             if (pad < 0) {
 297                 pad = 0;
 298                 buf = a = Arrays.copyOf(a, size);
 299             }
 300             if (a.getClass() != Object[].class || resolving == BAR_SYMREF)
 301                 buf = new Object[size];  // VM might store any kind of Object
 302             ((VM_BSCI<?>) self).copyVMArguments(offset, offset + size, a, 0, resolving, ifNotPresent);
 303             if (buf != a) {
 304                 // If a is the wrong array type, and the VM sends us a bad
 305                 // object, the arraycopy call is where the error will be reported.
 306                 System.arraycopy(buf, 0, a, 0, buf.length);
 307             }
 308             if (pad > 0)  a[size] = null;
 309             return a;
 310         }
 311     }
 312 
 313     abstract static
 314     class WithCache<T extends TypeDescriptor & Constable<T>>
 315             extends AbstractBootstrapCallInfo<T> {
 316         protected @Stable final Object[] cache;
 317         protected @Stable ConstantDesc<?>[] symCache;
 318 
 319         WithCache(MethodHandle bsm, String name, TypeView<T> typeView, int argumentCount) {
 320             super(bsm, name, typeView, argumentCount);
 321             // It is caller's responsibility to initialize the cache.
 322             // Initial contents are all-null, which means nothing is present.
 323             cache = new Object[argumentCount];
 324             // create symCache lazily
 325         }
 326 
 327         WithCache(MethodHandle bsm, String name, TypeView<T> typeView, Object[] arguments) {
 328             super(bsm, name, typeView, arguments.length);
 329             cache = arguments;  // caller may have pre-loaded stuff into the cache
 330         }
 331 
 332         WithCache(MethodHandle bsm, String name, TypeView<T> typeView, ConstantDesc<?>[] symbolicRefs) {
 333             super(bsm, name, typeView, symbolicRefs.length);
 334             cache = new Object[argumentCount];
 335             symCache = symbolicRefs;
 336         }
 337 
 338 //        WithCache(MethodHandle bsm, String name, TypeView<T> typeView, List<Object> allResolved) {
 339 //            this(bsm, name, typeView, allResolved.size());
 340 //            initializeCache(allResolved);
 341 //        }
 342 
 343         void initializeCache(List<Object> cacheContents) {
 344             initializeCache(cacheContents, new Object());
 345         }
 346         void initializeCache(List<Object> cacheContents, Object ifNotPresent) {
 347             // Replace ifNotPresent with null,
 348             // and null with RESOLVED_TO_NULL.
 349             // Then forget about the user-provided ifNotPresent.
 350             for (int i = 0; i < cache.length; i++) {
 351                 Object x = cacheContents.get(i);
 352                 if (x == ifNotPresent)
 353                     continue;  // leave the null in place
 354                 cache[i] = wrapNull(x);
 355             }
 356         }
 357 
 358         @Override public Object argument(int i) {
 359             Object x = cache[i];
 360             // @Stable array must use null for sentinel
 361             if (x == null) {
 362                 x = wrapNull(resolveConstant(i));
 363                 // racy CAS:
 364                 Object x2 = cache[i];
 365                 if (x2 != null)  x = x2;
 366                 else  cache[i] = x;
 367             }
 368             return unwrapNull(x);
 369         }
 370 
 371         @Override public Object argument(int i, Object ifNotAvailable) {
 372             Object x = cache[i];
 373             // @Stable array must use null for sentinel
 374             if (x == null)  return ifNotAvailable;
 375             return unwrapNull(x);
 376         }
 377 
 378         @Override public ConstantDesc<?> argumentDesc(int i) {
 379             ConstantDesc<?>[] symCache = this.symCache;
 380             if (symCache == null) {
 381                 this.symCache = symCache = new ConstantDesc<?>[argumentCount()];
 382             }
 383             ConstantDesc<?> x = symCache[i];
 384             if (x == null) {
 385                 x = findSymbol(i);
 386                 // racy CAS:
 387                 ConstantDesc<?> x2 = symCache[i];
 388                 if (x2 != null)  x = x2;
 389                 else  symCache[i] = x;
 390             }
 391             return x;
 392         }
 393 
 394         @Override
 395         public boolean argumentIsPresent(int i) {
 396             return cache[i] != null;  //NULL_MEANS_NOT_PRESENT
 397         }
 398 
 399         /** hook for local subclasses */
 400         abstract Object resolveConstant(int i) throws LinkageError;
 401 
 402         /** hook for local subclasses */
 403         abstract ConstantDesc<?> findSymbol(int i);
 404 
 405         // carefully try to expose the underlying object array of arguments
 406         // return null if this is not possible
 407         Object[] maybeShareArguments() {
 408             // fill in missing cache items, and look for null
 409             for (int i = 0; i < cache.length; i++) {
 410                 Object cx = cache[i];
 411                 Object rx = cx;
 412                 if (cx == null) {  // empty cache entry
 413                     rx = argument(i);
 414                     cx = cache[i];  // reload filled cache entry
 415                 } else if (cx == RESOLVED_TO_NULL) {
 416                     rx = null;
 417                 }
 418                 // Punt if the cache contains a masked null value.
 419                 // Also punt if cache doesn't contain a value.
 420                 if (cx == null || rx == null)  return null;
 421             }
 422             return cache;
 423         }
 424 
 425         /// routines for mapping between null sentinel and true resolved null
 426 
 427         Object wrapNull(Object x) {
 428             return x == null ? RESOLVED_TO_NULL : x;
 429         }
 430 
 431         Object unwrapNull(Object x) {
 432             assert(x != null);
 433             return x == RESOLVED_TO_NULL ? null : x;
 434         }
 435 
 436         // secret sentinel for an actual null resolved value, in the cache
 437         private static final Object RESOLVED_TO_NULL = new Object();
 438     }
 439 
 440     static Object[] maybeShareArguments(BootstrapCallInfo<?> bsci) {
 441         if (bsci instanceof WithCache) {
 442             WithCache<?> wc = (WithCache<?>) bsci;
 443             return wc.maybeShareArguments();
 444         }
 445         return null;
 446     }
 447 }