1 /*
   2  * Copyright (c) 2015, 2020, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package jdk.vm.ci.hotspot;
  24 
  25 import static jdk.vm.ci.common.InitTimer.timer;
  26 import static jdk.vm.ci.hotspot.HotSpotJVMCICompilerFactory.CompilationLevelAdjustment.None;
  27 import static jdk.vm.ci.services.Services.IS_BUILDING_NATIVE_IMAGE;
  28 import static jdk.vm.ci.services.Services.IS_IN_NATIVE_IMAGE;
  29 
  30 import java.io.IOException;
  31 import java.io.OutputStream;
  32 import java.io.PrintStream;
  33 import java.io.Serializable;
  34 import java.lang.invoke.CallSite;
  35 import java.lang.invoke.ConstantCallSite;
  36 import java.lang.invoke.MethodHandle;
  37 import java.lang.ref.WeakReference;
  38 import java.nio.ByteBuffer;
  39 import java.nio.ByteOrder;
  40 import java.util.ArrayList;
  41 import java.util.Collections;
  42 import java.util.Formatter;
  43 import java.util.HashMap;
  44 import java.util.List;
  45 import java.util.Map;
  46 import java.util.Objects;
  47 import java.util.ServiceLoader;
  48 import java.util.function.Predicate;
  49 
  50 import jdk.internal.misc.Unsafe;
  51 import jdk.vm.ci.code.Architecture;
  52 import jdk.vm.ci.code.CompilationRequest;
  53 import jdk.vm.ci.code.CompilationRequestResult;
  54 import jdk.vm.ci.code.CompiledCode;
  55 import jdk.vm.ci.code.InstalledCode;
  56 import jdk.vm.ci.common.InitTimer;
  57 import jdk.vm.ci.common.JVMCIError;
  58 import jdk.vm.ci.common.NativeImageReinitialize;
  59 import jdk.vm.ci.meta.JavaKind;
  60 import jdk.vm.ci.meta.JavaType;
  61 import jdk.vm.ci.meta.ResolvedJavaType;
  62 import jdk.vm.ci.meta.UnresolvedJavaType;
  63 import jdk.vm.ci.runtime.JVMCI;
  64 import jdk.vm.ci.runtime.JVMCIBackend;
  65 import jdk.vm.ci.runtime.JVMCICompiler;
  66 import jdk.vm.ci.runtime.JVMCICompilerFactory;
  67 import jdk.vm.ci.runtime.JVMCIRuntime;
  68 import jdk.vm.ci.services.JVMCIServiceLocator;
  69 
  70 /**
  71  * HotSpot implementation of a JVMCI runtime.
  72  */
  73 public final class HotSpotJVMCIRuntime implements JVMCIRuntime {
  74 
  75     /**
  76      * Singleton instance lazily initialized via double-checked locking.
  77      */
  78     @NativeImageReinitialize private static volatile HotSpotJVMCIRuntime instance;
  79 
  80     private HotSpotResolvedObjectTypeImpl javaLangObject;
  81     private HotSpotResolvedObjectTypeImpl javaLangInvokeMethodHandle;
  82     private HotSpotResolvedObjectTypeImpl constantCallSiteType;
  83     private HotSpotResolvedObjectTypeImpl callSiteType;
  84     private HotSpotResolvedObjectTypeImpl javaLangString;
  85     private HotSpotResolvedObjectTypeImpl javaLangClass;
  86     private HotSpotResolvedObjectTypeImpl throwableType;
  87     private HotSpotResolvedObjectTypeImpl serializableType;
  88     private HotSpotResolvedObjectTypeImpl cloneableType;
  89     private HotSpotResolvedObjectTypeImpl enumType;
  90 
  91     HotSpotResolvedObjectTypeImpl getJavaLangObject() {
  92         if (javaLangObject == null) {
  93             javaLangObject = (HotSpotResolvedObjectTypeImpl) fromClass(Object.class);
  94         }
  95         return javaLangObject;
  96     }
  97 
  98     HotSpotResolvedObjectTypeImpl getJavaLangString() {
  99         if (javaLangString == null) {
 100             javaLangString = (HotSpotResolvedObjectTypeImpl) fromClass(String.class);
 101         }
 102         return javaLangString;
 103     }
 104 
 105     HotSpotResolvedObjectTypeImpl getJavaLangClass() {
 106         if (javaLangClass == null) {
 107             javaLangClass = (HotSpotResolvedObjectTypeImpl) fromClass(Class.class);
 108         }
 109         return javaLangClass;
 110     }
 111 
 112     HotSpotResolvedObjectTypeImpl getJavaLangCloneable() {
 113         if (cloneableType == null) {
 114             cloneableType = (HotSpotResolvedObjectTypeImpl) fromClass(Cloneable.class);
 115         }
 116         return cloneableType;
 117     }
 118 
 119     HotSpotResolvedObjectTypeImpl getJavaLangSerializable() {
 120         if (serializableType == null) {
 121             serializableType = (HotSpotResolvedObjectTypeImpl) fromClass(Serializable.class);
 122         }
 123         return serializableType;
 124     }
 125 
 126     HotSpotResolvedObjectTypeImpl getJavaLangThrowable() {
 127         if (throwableType == null) {
 128             throwableType = (HotSpotResolvedObjectTypeImpl) fromClass(Throwable.class);
 129         }
 130         return throwableType;
 131     }
 132 
 133     HotSpotResolvedObjectTypeImpl getJavaLangEnum() {
 134         if (enumType == null) {
 135             enumType = (HotSpotResolvedObjectTypeImpl) fromClass(Enum.class);
 136         }
 137         return enumType;
 138     }
 139 
 140     HotSpotResolvedObjectTypeImpl getConstantCallSite() {
 141         if (constantCallSiteType == null) {
 142             constantCallSiteType = (HotSpotResolvedObjectTypeImpl) fromClass(ConstantCallSite.class);
 143         }
 144         return constantCallSiteType;
 145     }
 146 
 147     HotSpotResolvedObjectTypeImpl getCallSite() {
 148         if (callSiteType == null) {
 149             callSiteType = (HotSpotResolvedObjectTypeImpl) fromClass(CallSite.class);
 150         }
 151         return callSiteType;
 152     }
 153 
 154     HotSpotResolvedObjectType getMethodHandleClass() {
 155         if (javaLangInvokeMethodHandle == null) {
 156             javaLangInvokeMethodHandle = (HotSpotResolvedObjectTypeImpl) fromClass(MethodHandle.class);
 157         }
 158         return javaLangInvokeMethodHandle;
 159     }
 160 
 161     /**
 162      * Gets the singleton {@link HotSpotJVMCIRuntime} object.
 163      */
 164     @VMEntryPoint
 165     @SuppressWarnings("try")
 166     public static HotSpotJVMCIRuntime runtime() {
 167         HotSpotJVMCIRuntime result = instance;
 168         if (result == null) {
 169             // Synchronize on JVMCI.class to avoid deadlock
 170             // between the two JVMCI initialization paths:
 171             // HotSpotJVMCIRuntime.runtime() and JVMCI.getRuntime().
 172             synchronized (JVMCI.class) {
 173                 result = instance;
 174                 if (result == null) {
 175                     try (InitTimer t = timer("HotSpotJVMCIRuntime.<init>")) {
 176                         instance = result = new HotSpotJVMCIRuntime();
 177 
 178                         // Can only do eager initialization of the JVMCI compiler
 179                         // once the singleton instance is available.
 180                         if (result.config.getFlag("EagerJVMCI", Boolean.class)) {
 181                             result.getCompiler();
 182                         }
 183                     }
 184                     // Ensures JVMCIRuntime::_HotSpotJVMCIRuntime_instance is
 185                     // initialized.
 186                     JVMCI.getRuntime();
 187                 }
 188                 // Make sure all the primitive box caches are populated (required to properly
 189                 // materialize boxed primitives
 190                 // during deoptimization).
 191                 Boolean.valueOf(false);
 192                 Byte.valueOf((byte) 0);
 193                 Short.valueOf((short) 0);
 194                 Character.valueOf((char) 0);
 195                 Integer.valueOf(0);
 196                 Long.valueOf(0);
 197             }
 198         }
 199         return result;
 200     }
 201 
 202     @VMEntryPoint
 203     static Throwable decodeThrowable(String encodedThrowable) throws Throwable {
 204         return TranslatedException.decodeThrowable(encodedThrowable);
 205     }
 206 
 207     @VMEntryPoint
 208     static String encodeThrowable(Throwable throwable) throws Throwable {
 209         return TranslatedException.encodeThrowable(throwable);
 210     }
 211 
 212     @VMEntryPoint
 213     static String callToString(Object o) {
 214         return o.toString();
 215     }
 216 
 217     /**
 218      * Set of recognized {@code "jvmci.*"} system properties. Entries not associated with an
 219      * {@link Option} have this object as their value.
 220      */
 221     static final Map<String, Object> options = new HashMap<>();
 222     static {
 223         options.put("jvmci.class.path.append", options);
 224     }
 225 
 226     /**
 227      * A list of all supported JVMCI options.
 228      */
 229     public enum Option {
 230         // @formatter:off
 231         Compiler(String.class, null, "Selects the system compiler. This must match the getCompilerName() value returned " +
 232                 "by a jdk.vm.ci.runtime.JVMCICompilerFactory provider. " +
 233                 "An empty string or the value \"null\" selects a compiler " +
 234                 "that will raise an exception upon receiving a compilation request."),
 235         // Note: The following one is not used (see InitTimer.ENABLED). It is added here
 236         // so that -XX:+JVMCIPrintProperties shows the option.
 237         InitTimer(Boolean.class, false, "Specifies if initialization timing is enabled."),
 238         PrintConfig(Boolean.class, false, "Prints VM configuration available via JVMCI."),
 239         AuditHandles(Boolean.class, false, "Record stack trace along with scoped foreign object reference wrappers " +
 240                 "to debug issue with a wrapper being used after its scope has closed."),
 241         TraceMethodDataFilter(String.class, null,
 242                 "Enables tracing of profiling info when read by JVMCI.",
 243                 "Empty value: trace all methods",
 244                         "Non-empty value: trace methods whose fully qualified name contains the value."),
 245         UseProfilingInformation(Boolean.class, true, "");
 246         // @formatter:on
 247 
 248         /**
 249          * The prefix for system properties that are JVMCI options.
 250          */
 251         private static final String JVMCI_OPTION_PROPERTY_PREFIX = "jvmci.";
 252 
 253         /**
 254          * Sentinel for value initialized to {@code null} since {@code null} means uninitialized.
 255          */
 256         private static final String NULL_VALUE = "NULL";
 257 
 258         private final Class<?> type;
 259         @NativeImageReinitialize private Object value;
 260         private final Object defaultValue;
 261         private boolean isDefault = true;
 262         private final String[] helpLines;
 263 
 264         Option(Class<?> type, Object defaultValue, String... helpLines) {
 265             assert Character.isUpperCase(name().charAt(0)) : "Option name must start with upper-case letter: " + name();
 266             this.type = type;
 267             this.defaultValue = defaultValue;
 268             this.helpLines = helpLines;
 269             Object existing = options.put(getPropertyName(), this);
 270             assert existing == null : getPropertyName();
 271         }
 272 
 273         @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "sentinel must be String since it's a static final in an enum")
 274         private void init(String propertyValue) {
 275             assert value == null : "cannot re-initialize " + name();
 276             if (propertyValue == null) {
 277                 this.value = defaultValue == null ? NULL_VALUE : defaultValue;
 278                 this.isDefault = true;
 279             } else {
 280                 if (type == Boolean.class) {
 281                     this.value = Boolean.parseBoolean(propertyValue);
 282                 } else if (type == String.class) {
 283                     this.value = propertyValue;
 284                 } else {
 285                     throw new JVMCIError("Unexpected option type " + type);
 286                 }
 287                 this.isDefault = false;
 288             }
 289         }
 290 
 291         @SuppressFBWarnings(value = "ES_COMPARING_STRINGS_WITH_EQ", justification = "sentinel must be String since it's a static final in an enum")
 292         private Object getValue() {
 293             if (value == NULL_VALUE) {
 294                 return null;
 295             }
 296             if (value == null) {
 297                 return defaultValue;
 298             }
 299             return value;
 300         }
 301 
 302         /**
 303          * Gets the name of system property from which this option gets its value.
 304          */
 305         public String getPropertyName() {
 306             return JVMCI_OPTION_PROPERTY_PREFIX + name();
 307         }
 308 
 309         /**
 310          * Returns the option's value as boolean.
 311          *
 312          * @return option's value
 313          */
 314         public boolean getBoolean() {
 315             return (boolean) getValue();
 316         }
 317 
 318         /**
 319          * Returns the option's value as String.
 320          *
 321          * @return option's value
 322          */
 323         public String getString() {
 324             return (String) getValue();
 325         }
 326 
 327         private static final int PROPERTY_LINE_WIDTH = 80;
 328         private static final int PROPERTY_HELP_INDENT = 10;
 329 
 330         /**
 331          * Prints a description of the properties used to configure shared JVMCI code.
 332          *
 333          * @param out stream to print to
 334          */
 335         public static void printProperties(PrintStream out) {
 336             out.println("[JVMCI properties]");
 337             Option[] values = values();
 338             for (Option option : values) {
 339                 Object value = option.getValue();
 340                 if (value instanceof String) {
 341                     value = '"' + String.valueOf(value) + '"';
 342                 }
 343 
 344                 String name = option.getPropertyName();
 345                 String assign = option.isDefault ? "=" : ":=";
 346                 String typeName = option.type.getSimpleName();
 347                 String linePrefix = String.format("%s %s %s ", name, assign, value);
 348                 int typeStartPos = PROPERTY_LINE_WIDTH - typeName.length();
 349                 int linePad = typeStartPos - linePrefix.length();
 350                 if (linePad > 0) {
 351                     out.printf("%s%-" + linePad + "s[%s]%n", linePrefix, "", typeName);
 352                 } else {
 353                     out.printf("%s[%s]%n", linePrefix, typeName);
 354                 }
 355                 for (String line : option.helpLines) {
 356                     out.printf("%" + PROPERTY_HELP_INDENT + "s%s%n", "", line);
 357                 }
 358             }
 359         }
 360 
 361         /**
 362          * Compute string similarity based on Dice's coefficient.
 363          *
 364          * Ported from str_similar() in globals.cpp.
 365          */
 366         static float stringSimiliarity(String str1, String str2) {
 367             int hit = 0;
 368             for (int i = 0; i < str1.length() - 1; ++i) {
 369                 for (int j = 0; j < str2.length() - 1; ++j) {
 370                     if ((str1.charAt(i) == str2.charAt(j)) && (str1.charAt(i + 1) == str2.charAt(j + 1))) {
 371                         ++hit;
 372                         break;
 373                     }
 374                 }
 375             }
 376             return 2.0f * hit / (str1.length() + str2.length());
 377         }
 378 
 379         private static final float FUZZY_MATCH_THRESHOLD = 0.7F;
 380 
 381         /**
 382          * Parses all system properties starting with {@value #JVMCI_OPTION_PROPERTY_PREFIX} and
 383          * initializes the options based on their values.
 384          *
 385          * @param runtime
 386          */
 387         static void parse(HotSpotJVMCIRuntime runtime) {
 388             Map<String, String> savedProps = jdk.vm.ci.services.Services.getSavedProperties();
 389             for (Map.Entry<String, String> e : savedProps.entrySet()) {
 390                 String name = e.getKey();
 391                 if (name.startsWith(Option.JVMCI_OPTION_PROPERTY_PREFIX)) {
 392                     Object value = options.get(name);
 393                     if (value == null) {
 394                         List<String> matches = new ArrayList<>();
 395                         for (String pn : options.keySet()) {
 396                             float score = stringSimiliarity(pn, name);
 397                             if (score >= FUZZY_MATCH_THRESHOLD) {
 398                                 matches.add(pn);
 399                             }
 400                         }
 401                         Formatter msg = new Formatter();
 402                         msg.format("Error parsing JVMCI options: Could not find option %s", name);
 403                         if (!matches.isEmpty()) {
 404                             msg.format("%nDid you mean one of the following?");
 405                             for (String match : matches) {
 406                                 msg.format("%n    %s=<value>", match);
 407                             }
 408                         }
 409                         msg.format("%nError: A fatal exception has occurred. Program will exit.%n");
 410                         runtime.exitHotSpotWithMessage(1, msg.toString());
 411                     } else if (value instanceof Option) {
 412                         Option option = (Option) value;
 413                         option.init(e.getValue());
 414                     }
 415                 }
 416             }
 417         }
 418     }
 419 
 420     private static HotSpotJVMCIBackendFactory findFactory(String architecture) {
 421         Iterable<HotSpotJVMCIBackendFactory> factories = getHotSpotJVMCIBackendFactories();
 422         assert factories != null : "sanity";
 423         for (HotSpotJVMCIBackendFactory factory : factories) {
 424             if (factory.getArchitecture().equalsIgnoreCase(architecture)) {
 425                 return factory;
 426             }
 427         }
 428 
 429         throw new JVMCIError("No JVMCI runtime available for the %s architecture", architecture);
 430     }
 431 
 432     private static volatile List<HotSpotJVMCIBackendFactory> cachedHotSpotJVMCIBackendFactories;
 433 
 434     @SuppressFBWarnings(value = "LI_LAZY_INIT_UPDATE_STATIC", justification = "not sure about this")
 435     private static Iterable<HotSpotJVMCIBackendFactory> getHotSpotJVMCIBackendFactories() {
 436         if (IS_IN_NATIVE_IMAGE || cachedHotSpotJVMCIBackendFactories != null) {
 437             return cachedHotSpotJVMCIBackendFactories;
 438         }
 439         Iterable<HotSpotJVMCIBackendFactory> result = ServiceLoader.load(HotSpotJVMCIBackendFactory.class, ClassLoader.getSystemClassLoader());
 440         if (IS_BUILDING_NATIVE_IMAGE) {
 441             cachedHotSpotJVMCIBackendFactories = new ArrayList<>();
 442             for (HotSpotJVMCIBackendFactory factory : result) {
 443                 cachedHotSpotJVMCIBackendFactories.add(factory);
 444             }
 445         }
 446         return result;
 447     }
 448 
 449     /**
 450      * Gets the kind of a word value on the {@linkplain #getHostJVMCIBackend() host} backend.
 451      */
 452     public static JavaKind getHostWordKind() {
 453         return runtime().getHostJVMCIBackend().getCodeCache().getTarget().wordJavaKind;
 454     }
 455 
 456     protected final CompilerToVM compilerToVm;
 457 
 458     protected final HotSpotVMConfigStore configStore;
 459     protected final HotSpotVMConfig config;
 460     private final JVMCIBackend hostBackend;
 461 
 462     private final JVMCICompilerFactory compilerFactory;
 463     private final HotSpotJVMCICompilerFactory hsCompilerFactory;
 464     private volatile JVMCICompiler compiler;
 465     protected final HotSpotJVMCIReflection reflection;
 466 
 467     @NativeImageReinitialize private volatile boolean creatingCompiler;
 468 
 469     /**
 470      * Cache for speeding up {@link #fromClass(Class)}.
 471      */
 472     @NativeImageReinitialize private volatile ClassValue<WeakReferenceHolder<HotSpotResolvedJavaType>> resolvedJavaType;
 473 
 474     /**
 475      * To avoid calling ClassValue.remove to refresh the weak reference, which under certain
 476      * circumstances can lead to an infinite loop, we use a permanent holder with a mutable field
 477      * that we refresh.
 478      */
 479     private static class WeakReferenceHolder<T> {
 480         private volatile WeakReference<T> ref;
 481 
 482         WeakReferenceHolder(T value) {
 483             set(value);
 484         }
 485 
 486         void set(T value) {
 487             ref = new WeakReference<>(value);
 488         }
 489 
 490         T get() {
 491             return ref.get();
 492         }
 493     }
 494 
 495     @NativeImageReinitialize private HashMap<Long, WeakReference<ResolvedJavaType>> resolvedJavaTypes;
 496 
 497     /**
 498      * Stores the value set by {@link #excludeFromJVMCICompilation(Module...)} so that it can be
 499      * read from the VM.
 500      */
 501     @SuppressWarnings("unused")//
 502     @NativeImageReinitialize private Module[] excludeFromJVMCICompilation;
 503 
 504     private final Map<Class<? extends Architecture>, JVMCIBackend> backends = new HashMap<>();
 505 
 506     private volatile List<HotSpotVMEventListener> vmEventListeners;
 507 
 508     private Iterable<HotSpotVMEventListener> getVmEventListeners() {
 509         if (vmEventListeners == null) {
 510             synchronized (this) {
 511                 if (vmEventListeners == null) {
 512                     vmEventListeners = JVMCIServiceLocator.getProviders(HotSpotVMEventListener.class);
 513                 }
 514             }
 515         }
 516         return vmEventListeners;
 517     }
 518 
 519     @SuppressWarnings("try")
 520     private HotSpotJVMCIRuntime() {
 521         compilerToVm = new CompilerToVM();
 522 
 523         try (InitTimer t = timer("HotSpotVMConfig<init>")) {
 524             configStore = new HotSpotVMConfigStore(compilerToVm);
 525             config = new HotSpotVMConfig(configStore);
 526         }
 527 
 528         reflection = IS_IN_NATIVE_IMAGE ? new SharedLibraryJVMCIReflection() : new HotSpotJDKReflection();
 529 
 530         PrintStream vmLogStream = null;
 531         if (IS_IN_NATIVE_IMAGE) {
 532             // Redirect System.out and System.err to HotSpot's TTY stream
 533             vmLogStream = new PrintStream(getLogStream());
 534             System.setOut(vmLogStream);
 535             System.setErr(vmLogStream);
 536         }
 537 
 538         // Initialize the Option values.
 539         Option.parse(this);
 540 
 541         String hostArchitecture = config.getHostArchitectureName();
 542 
 543         HotSpotJVMCIBackendFactory factory;
 544         try (InitTimer t = timer("find factory:", hostArchitecture)) {
 545             factory = findFactory(hostArchitecture);
 546         }
 547 
 548         try (InitTimer t = timer("create JVMCI backend:", hostArchitecture)) {
 549             hostBackend = registerBackend(factory.createJVMCIBackend(this, null));
 550         }
 551 
 552         compilerFactory = HotSpotJVMCICompilerConfig.getCompilerFactory(this);
 553         if (compilerFactory instanceof HotSpotJVMCICompilerFactory) {
 554             hsCompilerFactory = (HotSpotJVMCICompilerFactory) compilerFactory;
 555             if (hsCompilerFactory.getCompilationLevelAdjustment() != None) {
 556                 String name = HotSpotJVMCICompilerFactory.class.getName();
 557                 String msg = String.format("%s.getCompilationLevelAdjustment() is no longer supported. " +
 558                                 "Use %s.excludeFromJVMCICompilation() instead.", name, name);
 559                 throw new UnsupportedOperationException(msg);
 560             }
 561         } else {
 562             hsCompilerFactory = null;
 563         }
 564 
 565         if (config.getFlag("JVMCIPrintProperties", Boolean.class)) {
 566             if (vmLogStream == null) {
 567                 vmLogStream = new PrintStream(getLogStream());
 568             }
 569             Option.printProperties(vmLogStream);
 570             compilerFactory.printProperties(vmLogStream);
 571             System.exit(0);
 572         }
 573 
 574         if (Option.PrintConfig.getBoolean()) {
 575             configStore.printConfig(this);
 576         }
 577     }
 578 
 579     /**
 580      * Sets the current thread's {@code JavaThread::_jvmci_reserved_oop<id>} field to {@code value}.
 581      *
 582      * @throws IllegalArgumentException if the {@code JavaThread::_jvmci_reserved_oop<id>} field
 583      *             does not exist
 584      */
 585     public void setThreadLocalObject(int id, Object value) {
 586         compilerToVm.setThreadLocalObject(id, value);
 587     }
 588 
 589     /**
 590      * Get the value of the current thread's {@code JavaThread::_jvmci_reserved_oop<id>} field.
 591      *
 592      * @throws IllegalArgumentException if the {@code JavaThread::_jvmci_reserved_oop<id>} field
 593      *             does not exist
 594      */
 595     public Object getThreadLocalObject(int id) {
 596         return compilerToVm.getThreadLocalObject(id);
 597     }
 598 
 599     /**
 600      * Sets the current thread's {@code JavaThread::_jvmci_reserved<id>} field to {@code value}.
 601      *
 602      * @throws IllegalArgumentException if the {@code JavaThread::_jvmci_reserved<id>} field does
 603      *             not exist
 604      */
 605     public void setThreadLocalLong(int id, long value) {
 606         compilerToVm.setThreadLocalLong(id, value);
 607     }
 608 
 609     /**
 610      * Get the value of the current thread's {@code JavaThread::_jvmci_reserved<id>} field.
 611      *
 612      * @throws IllegalArgumentException if the {@code JavaThread::_jvmci_reserved<id>} field does
 613      *             not exist
 614      */
 615     public long getThreadLocalLong(int id) {
 616         return compilerToVm.getThreadLocalLong(id);
 617     }
 618 
 619     HotSpotResolvedJavaType createClass(Class<?> javaClass) {
 620         if (javaClass.isPrimitive()) {
 621             return HotSpotResolvedPrimitiveType.forKind(JavaKind.fromJavaClass(javaClass));
 622         }
 623         if (IS_IN_NATIVE_IMAGE) {
 624             try {
 625                 return compilerToVm.lookupType(javaClass.getName().replace('.', '/'), null, true);
 626             } catch (ClassNotFoundException e) {
 627                 throw new JVMCIError(e);
 628             }
 629         }
 630         return compilerToVm.lookupClass(javaClass);
 631     }
 632 
 633     private HotSpotResolvedJavaType fromClass0(Class<?> javaClass) {
 634         if (resolvedJavaType == null) {
 635             synchronized (this) {
 636                 if (resolvedJavaType == null) {
 637                     resolvedJavaType = new ClassValue<>() {
 638                         @Override
 639                         protected WeakReferenceHolder<HotSpotResolvedJavaType> computeValue(Class<?> type) {
 640                             return new WeakReferenceHolder<>(createClass(type));
 641                         }
 642                     };
 643                 }
 644             }
 645         }
 646 
 647         WeakReferenceHolder<HotSpotResolvedJavaType> ref = resolvedJavaType.get(javaClass);
 648         HotSpotResolvedJavaType javaType = ref.get();
 649         if (javaType == null) {
 650             /*
 651              * If the referent has become null, create a new value and update cached weak reference.
 652              */
 653             javaType = createClass(javaClass);
 654             ref.set(javaType);
 655         }
 656         return javaType;
 657     }
 658 
 659     /**
 660      * Gets the JVMCI mirror for a {@link Class} object.
 661      *
 662      * @return the {@link ResolvedJavaType} corresponding to {@code javaClass}
 663      */
 664     HotSpotResolvedJavaType fromClass(Class<?> javaClass) {
 665         if (javaClass == null) {
 666             return null;
 667         }
 668         return fromClass0(javaClass);
 669     }
 670 
 671     synchronized HotSpotResolvedObjectTypeImpl fromMetaspace(long klassPointer, String signature) {
 672         if (resolvedJavaTypes == null) {
 673             resolvedJavaTypes = new HashMap<>();
 674         }
 675         assert klassPointer != 0;
 676         WeakReference<ResolvedJavaType> klassReference = resolvedJavaTypes.get(klassPointer);
 677         HotSpotResolvedObjectTypeImpl javaType = null;
 678         if (klassReference != null) {
 679             javaType = (HotSpotResolvedObjectTypeImpl) klassReference.get();
 680         }
 681         if (javaType == null) {
 682             javaType = new HotSpotResolvedObjectTypeImpl(klassPointer, signature);
 683             resolvedJavaTypes.put(klassPointer, new WeakReference<>(javaType));
 684         }
 685         return javaType;
 686     }
 687 
 688     private JVMCIBackend registerBackend(JVMCIBackend backend) {
 689         Class<? extends Architecture> arch = backend.getCodeCache().getTarget().arch.getClass();
 690         JVMCIBackend oldValue = backends.put(arch, backend);
 691         assert oldValue == null : "cannot overwrite existing backend for architecture " + arch.getSimpleName();
 692         return backend;
 693     }
 694 
 695     public HotSpotVMConfigStore getConfigStore() {
 696         return configStore;
 697     }
 698 
 699     public HotSpotVMConfig getConfig() {
 700         return config;
 701     }
 702 
 703     public CompilerToVM getCompilerToVM() {
 704         return compilerToVm;
 705     }
 706 
 707     HotSpotJVMCIReflection getReflection() {
 708         return reflection;
 709     }
 710 
 711     /**
 712      * Gets a predicate that determines if a given type can be considered trusted for the purpose of
 713      * intrinsifying methods it declares.
 714      *
 715      * @param compilerLeafClasses classes in the leaves of the module graph comprising the JVMCI
 716      *            compiler.
 717      */
 718     public Predicate<ResolvedJavaType> getIntrinsificationTrustPredicate(Class<?>... compilerLeafClasses) {
 719         return new Predicate<>() {
 720             @Override
 721             public boolean test(ResolvedJavaType type) {
 722                 if (type instanceof HotSpotResolvedObjectTypeImpl) {
 723                     HotSpotResolvedObjectTypeImpl hsType = (HotSpotResolvedObjectTypeImpl) type;
 724                     return compilerToVm.isTrustedForIntrinsics(hsType);
 725                 } else {
 726                     return false;
 727                 }
 728             }
 729         };
 730     }
 731 
 732     /**
 733      * Get the {@link Class} corresponding to {@code type}.
 734      *
 735      * @param type the type for which a {@link Class} is requested
 736      * @return the original Java class corresponding to {@code type} or {@code null} if this runtime
 737      *         does not support mapping {@link ResolvedJavaType} instances to {@link Class}
 738      *         instances
 739      */
 740     public Class<?> getMirror(ResolvedJavaType type) {
 741         if (type instanceof HotSpotResolvedJavaType && reflection instanceof HotSpotJDKReflection) {
 742             return ((HotSpotJDKReflection) reflection).getMirror((HotSpotResolvedJavaType) type);
 743         }
 744         return null;
 745     }
 746 
 747     static class ErrorCreatingCompiler implements JVMCICompiler {
 748         private final RuntimeException t;
 749 
 750         ErrorCreatingCompiler(RuntimeException t) {
 751             this.t = t;
 752         }
 753 
 754         @Override
 755         public CompilationRequestResult compileMethod(CompilationRequest request) {
 756             throw t;
 757         }
 758 
 759         @Override
 760         public boolean isGCSupported(int gcIdentifier) {
 761             return false;
 762         }
 763     }
 764 
 765     @Override
 766     public JVMCICompiler getCompiler() {
 767         if (compiler == null) {
 768             synchronized (this) {
 769                 if (compiler == null) {
 770                     assert !creatingCompiler : "recursive compiler creation";
 771                     creatingCompiler = true;
 772                     try {
 773                         compiler = compilerFactory.createCompiler(this);
 774                     } catch (RuntimeException t) {
 775                         compiler = new ErrorCreatingCompiler(t);
 776                     } finally {
 777                         creatingCompiler = false;
 778                     }
 779                 }
 780             }
 781         }
 782         if (compiler instanceof ErrorCreatingCompiler) {
 783             throw ((ErrorCreatingCompiler) compiler).t;
 784         }
 785         return compiler;
 786     }
 787 
 788     /**
 789      * Converts a name to a Java type. This method attempts to resolve {@code name} to a
 790      * {@link ResolvedJavaType}.
 791      *
 792      * @param name a well formed Java type in {@linkplain JavaType#getName() internal} format
 793      * @param accessingType the context of resolution which must be non-null
 794      * @param resolve specifies whether resolution failure results in an unresolved type being
 795      *            return or a {@link LinkageError} being thrown
 796      * @return a Java type for {@code name} which is guaranteed to be of type
 797      *         {@link ResolvedJavaType} if {@code resolve == true}
 798      * @throws LinkageError if {@code resolve == true} and the resolution failed
 799      * @throws NullPointerException if {@code accessingClass} is {@code null}
 800      */
 801     public JavaType lookupType(String name, HotSpotResolvedObjectType accessingType, boolean resolve) {
 802         Objects.requireNonNull(accessingType, "cannot resolve type without an accessing class");
 803         return lookupTypeInternal(name, accessingType, resolve);
 804     }
 805 
 806     JavaType lookupTypeInternal(String name, HotSpotResolvedObjectType accessingType, boolean resolve) {
 807         // If the name represents a primitive type we can short-circuit the lookup.
 808         if (name.length() == 1) {
 809             JavaKind kind = JavaKind.fromPrimitiveOrVoidTypeChar(name.charAt(0));
 810             return HotSpotResolvedPrimitiveType.forKind(kind);
 811         }
 812 
 813         // Resolve non-primitive types in the VM.
 814         HotSpotResolvedObjectTypeImpl hsAccessingType = (HotSpotResolvedObjectTypeImpl) accessingType;
 815         try {
 816             final HotSpotResolvedJavaType klass = compilerToVm.lookupType(name, hsAccessingType, resolve);
 817 
 818             if (klass == null) {
 819                 assert resolve == false : name;
 820                 return UnresolvedJavaType.create(name);
 821             }
 822             return klass;
 823         } catch (ClassNotFoundException e) {
 824             throw (NoClassDefFoundError) new NoClassDefFoundError().initCause(e);
 825         }
 826     }
 827 
 828     @Override
 829     public JVMCIBackend getHostJVMCIBackend() {
 830         return hostBackend;
 831     }
 832 
 833     @Override
 834     public <T extends Architecture> JVMCIBackend getJVMCIBackend(Class<T> arch) {
 835         assert arch != Architecture.class;
 836         return backends.get(arch);
 837     }
 838 
 839     public Map<Class<? extends Architecture>, JVMCIBackend> getJVMCIBackends() {
 840         return Collections.unmodifiableMap(backends);
 841     }
 842 
 843     @SuppressWarnings("try")
 844     @VMEntryPoint
 845     private HotSpotCompilationRequestResult compileMethod(HotSpotResolvedJavaMethod method, int entryBCI, long compileState, int id) {
 846         HotSpotCompilationRequest request = new HotSpotCompilationRequest(method, entryBCI, compileState, id);
 847         CompilationRequestResult result = getCompiler().compileMethod(request);
 848         assert result != null : "compileMethod must always return something";
 849         HotSpotCompilationRequestResult hsResult;
 850         if (result instanceof HotSpotCompilationRequestResult) {
 851             hsResult = (HotSpotCompilationRequestResult) result;
 852         } else {
 853             Object failure = result.getFailure();
 854             if (failure != null) {
 855                 boolean retry = false; // Be conservative with unknown compiler
 856                 hsResult = HotSpotCompilationRequestResult.failure(failure.toString(), retry);
 857             } else {
 858                 int inlinedBytecodes = -1;
 859                 hsResult = HotSpotCompilationRequestResult.success(inlinedBytecodes);
 860             }
 861         }
 862         return hsResult;
 863     }
 864 
 865     @SuppressWarnings("try")
 866     @VMEntryPoint
 867     private boolean isGCSupported(int gcIdentifier) {
 868         return getCompiler().isGCSupported(gcIdentifier);
 869     }
 870 
 871     /**
 872      * Guard to ensure shut down actions are performed at most once.
 873      */
 874     private boolean isShutdown;
 875 
 876     /**
 877      * Shuts down the runtime.
 878      */
 879     @VMEntryPoint
 880     private synchronized void shutdown() throws Exception {
 881         if (!isShutdown) {
 882             isShutdown = true;
 883             // Cleaners are normally only processed when a new Cleaner is
 884             // instantiated so process all remaining cleaners now.
 885             Cleaner.clean();
 886 
 887             for (HotSpotVMEventListener vmEventListener : getVmEventListeners()) {
 888                 vmEventListener.notifyShutdown();
 889             }
 890         }
 891     }
 892 
 893     /**
 894      * Notify on completion of a bootstrap.
 895      */
 896     @VMEntryPoint
 897     private void bootstrapFinished() throws Exception {
 898         for (HotSpotVMEventListener vmEventListener : getVmEventListeners()) {
 899             vmEventListener.notifyBootstrapFinished();
 900         }
 901     }
 902 
 903     /**
 904      * Notify on successful install into the CodeCache.
 905      *
 906      * @param hotSpotCodeCacheProvider
 907      * @param installedCode
 908      * @param compiledCode
 909      */
 910     void notifyInstall(HotSpotCodeCacheProvider hotSpotCodeCacheProvider, InstalledCode installedCode, CompiledCode compiledCode) {
 911         for (HotSpotVMEventListener vmEventListener : getVmEventListeners()) {
 912             vmEventListener.notifyInstall(hotSpotCodeCacheProvider, installedCode, compiledCode);
 913         }
 914     }
 915 
 916     /**
 917      * Writes {@code length} bytes from {@code bytes} starting at offset {@code offset} to HotSpot's
 918      * log stream.
 919      *
 920      * @param flush specifies if the log stream should be flushed after writing
 921      * @param canThrow specifies if an error in the {@code bytes}, {@code offset} or {@code length}
 922      *            arguments should result in an exception or a negative return value. If
 923      *            {@code false}, this call will not perform any heap allocation
 924      * @return 0 on success, -1 if {@code bytes == null && !canThrow}, -2 if {@code !canThrow} and
 925      *         copying would cause access of data outside array bounds
 926      * @throws NullPointerException if {@code bytes == null}
 927      * @throws IndexOutOfBoundsException if copying would cause access of data outside array bounds
 928      */
 929     public int writeDebugOutput(byte[] bytes, int offset, int length, boolean flush, boolean canThrow) {
 930         return writeDebugOutput0(compilerToVm, bytes, offset, length, flush, canThrow);
 931     }
 932 
 933     /**
 934      * @see #writeDebugOutput
 935      */
 936     static int writeDebugOutput0(CompilerToVM vm, byte[] bytes, int offset, int length, boolean flush, boolean canThrow) {
 937         if (bytes == null) {
 938             if (!canThrow) {
 939                 return -1;
 940             }
 941             throw new NullPointerException();
 942         }
 943         if (offset < 0 || length < 0 || offset + length > bytes.length) {
 944             if (!canThrow) {
 945                 return -2;
 946             }
 947             throw new ArrayIndexOutOfBoundsException();
 948         }
 949         if (length <= 8) {
 950             ByteBuffer buffer = ByteBuffer.wrap(bytes, offset, length);
 951             if (length != 8) {
 952                 ByteBuffer buffer8 = ByteBuffer.allocate(8);
 953                 buffer8.put(buffer);
 954                 buffer8.position(8);
 955                 buffer = buffer8;
 956             }
 957             buffer.order(ByteOrder.nativeOrder());
 958             vm.writeDebugOutput(buffer.getLong(0), length, flush);
 959         } else {
 960             Unsafe unsafe = UnsafeAccess.UNSAFE;
 961             long buffer = unsafe.allocateMemory(length);
 962             try {
 963                 unsafe.copyMemory(bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, buffer, length);
 964                 vm.writeDebugOutput(buffer, length, flush);
 965             } finally {
 966                 unsafe.freeMemory(buffer);
 967             }
 968         }
 969         return 0;
 970     }
 971 
 972     /**
 973      * Gets an output stream that writes to HotSpot's {@code tty} stream.
 974      */
 975     public OutputStream getLogStream() {
 976         return new OutputStream() {
 977 
 978             @Override
 979             public void write(byte[] b, int off, int len) throws IOException {
 980                 if (b == null) {
 981                     throw new NullPointerException();
 982                 } else if (off < 0 || off > b.length || len < 0 || (off + len) > b.length || (off + len) < 0) {
 983                     throw new IndexOutOfBoundsException();
 984                 } else if (len == 0) {
 985                     return;
 986                 }
 987                 writeDebugOutput(b, off, len, false, true);
 988             }
 989 
 990             @Override
 991             public void write(int b) throws IOException {
 992                 write(new byte[]{(byte) b}, 0, 1);
 993             }
 994 
 995             @Override
 996             public void flush() throws IOException {
 997                 compilerToVm.flushDebugOutput();
 998             }
 999         };
1000     }
1001 
1002     /**
1003      * Collects the current values of all JVMCI benchmark counters, summed up over all threads.
1004      */
1005     public long[] collectCounters() {
1006         return compilerToVm.collectCounters();
1007     }
1008 
1009     /**
1010      * @return the current number of per thread counters. May be set through
1011      *         {@code -XX:JVMCICompilerSize=} command line option or the
1012      *         {@link #setCountersSize(int)} call.
1013      */
1014     public int getCountersSize() {
1015         return compilerToVm.getCountersSize();
1016     }
1017 
1018     /**
1019      * Attempt to enlarge the number of per thread counters available. Requires a safepoint so
1020      * resizing should be rare to avoid performance effects.
1021      *
1022      * @param newSize
1023      * @return false if the resizing failed
1024      */
1025     public boolean setCountersSize(int newSize) {
1026         return compilerToVm.setCountersSize(newSize);
1027     }
1028 
1029     /**
1030      * The offset from the origin of an array to the first element.
1031      *
1032      * @return the offset in bytes
1033      */
1034     public int getArrayBaseOffset(JavaKind kind) {
1035         switch (kind) {
1036             case Boolean:
1037                 return compilerToVm.ARRAY_BOOLEAN_BASE_OFFSET;
1038             case Byte:
1039                 return compilerToVm.ARRAY_BYTE_BASE_OFFSET;
1040             case Char:
1041                 return compilerToVm.ARRAY_CHAR_BASE_OFFSET;
1042             case Short:
1043                 return compilerToVm.ARRAY_SHORT_BASE_OFFSET;
1044             case Int:
1045                 return compilerToVm.ARRAY_INT_BASE_OFFSET;
1046             case Long:
1047                 return compilerToVm.ARRAY_LONG_BASE_OFFSET;
1048             case Float:
1049                 return compilerToVm.ARRAY_FLOAT_BASE_OFFSET;
1050             case Double:
1051                 return compilerToVm.ARRAY_DOUBLE_BASE_OFFSET;
1052             case Object:
1053                 return compilerToVm.ARRAY_OBJECT_BASE_OFFSET;
1054             default:
1055                 throw new JVMCIError("%s", kind);
1056         }
1057 
1058     }
1059 
1060     /**
1061      * The scale used for the index when accessing elements of an array of this kind.
1062      *
1063      * @return the scale in order to convert the index into a byte offset
1064      */
1065     public int getArrayIndexScale(JavaKind kind) {
1066         switch (kind) {
1067             case Boolean:
1068                 return compilerToVm.ARRAY_BOOLEAN_INDEX_SCALE;
1069             case Byte:
1070                 return compilerToVm.ARRAY_BYTE_INDEX_SCALE;
1071             case Char:
1072                 return compilerToVm.ARRAY_CHAR_INDEX_SCALE;
1073             case Short:
1074                 return compilerToVm.ARRAY_SHORT_INDEX_SCALE;
1075             case Int:
1076                 return compilerToVm.ARRAY_INT_INDEX_SCALE;
1077             case Long:
1078                 return compilerToVm.ARRAY_LONG_INDEX_SCALE;
1079             case Float:
1080                 return compilerToVm.ARRAY_FLOAT_INDEX_SCALE;
1081             case Double:
1082                 return compilerToVm.ARRAY_DOUBLE_INDEX_SCALE;
1083             case Object:
1084                 return compilerToVm.ARRAY_OBJECT_INDEX_SCALE;
1085             default:
1086                 throw new JVMCIError("%s", kind);
1087 
1088         }
1089     }
1090 
1091     /**
1092      * Links each native method in {@code clazz} to an implementation in the JVMCI shared library.
1093      * <p>
1094      * A use case for this is a JVMCI compiler implementation that offers an API to Java code
1095      * executing in HotSpot to exercise functionality (mostly) in the JVMCI shared library. For
1096      * example:
1097      *
1098      * <pre>
1099      * package com.jcompile;
1100      *
1101      * import java.lang.reflect.Method;
1102      *
1103      * public static class JCompile {
1104      *     static {
1105      *         HotSpotJVMCIRuntime.runtime().registerNativeMethods(JCompile.class);
1106      *     }
1107      *     public static boolean compile(Method method, String[] options) {
1108      *         // Convert to simpler data types for passing/serializing across native interface
1109      *         long metaspaceMethodHandle = getHandle(method);
1110      *         char[] opts = convertToCharArray(options);
1111      *         return compile(metaspaceMethodHandle, opts);
1112      *     }
1113      *     private static native boolean compile0(long metaspaceMethodHandle, char[] options);
1114      *
1115      *     private static long getHandle(Method method) { ... }
1116      *     private static char[] convertToCharArray(String[] a) { ... }
1117      * }
1118      * </pre>
1119      *
1120      * The implementation of the native {@code JCompile.compile0} method would be in the JVMCI
1121      * shared library that contains the JVMCI compiler. The {@code JCompile.compile0} implementation
1122      * must be exported as the following JNI-compatible symbol:
1123      *
1124      * <pre>
1125      * Java_com_jcompile_JCompile_compile0
1126      * </pre>
1127      *
1128      * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#resolving_native_method_names"
1129      * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html#creating_the_vm"
1130      * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/invocation.html#invocation_api_functions"
1131      *
1132      *
1133      * @return info about the Java VM in the JVMCI shared library {@code JavaVM*}. The info is
1134      *         encoded in a long array as follows:
1135      *
1136      *         <pre>
1137      *     long[] info = {
1138      *         javaVM, // the {@code JavaVM*} value
1139      *         javaVM->functions->reserved0,
1140      *         javaVM->functions->reserved1,
1141      *         javaVM->functions->reserved2
1142      *     }
1143      *         </pre>
1144      *
1145      * @throws NullPointerException if {@code clazz == null}
1146      * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e.
1147      *             {@code -XX:-UseJVMCINativeLibrary})
1148      * @throws IllegalStateException if the current execution context is the JVMCI shared library
1149      * @throws IllegalArgumentException if {@code clazz} is {@link Class#isPrimitive()}
1150      * @throws UnsatisfiedLinkError if there's a problem linking a native method in {@code clazz}
1151      *             (no matching JNI symbol or the native method is already linked to a different
1152      *             address)
1153      */
1154     public long[] registerNativeMethods(Class<?> clazz) {
1155         return compilerToVm.registerNativeMethods(clazz);
1156     }
1157 
1158     /**
1159      * Creates or retrieves an object in the peer runtime that mirrors {@code obj}. The types whose
1160      * objects can be translated are:
1161      * <ul>
1162      * <li>{@link HotSpotResolvedJavaMethodImpl},</li>
1163      * <li>{@link HotSpotResolvedObjectTypeImpl},</li>
1164      * <li>{@link HotSpotResolvedPrimitiveType},</li>
1165      * <li>{@link IndirectHotSpotObjectConstantImpl},</li>
1166      * <li>{@link DirectHotSpotObjectConstantImpl} and</li>
1167      * <li>{@link HotSpotNmethod}</li>
1168      * </ul>
1169      *
1170      * This mechanism can be used to pass and return values between the HotSpot and JVMCI shared
1171      * library runtimes. In the receiving runtime, the value can be converted back to an object with
1172      * {@link #unhand(Class, long)}.
1173      *
1174      * @param obj an object for which an equivalent instance in the peer runtime is requested
1175      * @return a JNI global reference to the mirror of {@code obj} in the peer runtime
1176      * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e.
1177      *             {@code -XX:-UseJVMCINativeLibrary})
1178      * @throws IllegalArgumentException if {@code obj} is not of a translatable type
1179      *
1180      * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#global_and_local_references"
1181      */
1182     public long translate(Object obj) {
1183         return compilerToVm.translate(obj);
1184     }
1185 
1186     /**
1187      * Dereferences and returns the object referred to by the JNI global reference {@code handle}.
1188      * The global reference is deleted prior to returning. Any further use of {@code handle} is
1189      * invalid.
1190      *
1191      * @param handle a JNI global reference to an object in the current runtime
1192      * @return the object referred to by {@code handle}
1193      * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e.
1194      *             {@code -XX:-UseJVMCINativeLibrary})
1195      * @throws ClassCastException if the returned object cannot be cast to {@code type}
1196      *
1197      * @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#global_and_local_references"
1198      *
1199      */
1200     public <T> T unhand(Class<T> type, long handle) {
1201         return type.cast(compilerToVm.unhand(handle));
1202     }
1203 
1204     /**
1205      * Determines if the current thread is attached to the peer runtime.
1206      *
1207      * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e.
1208      *             {@code -XX:-UseJVMCINativeLibrary})
1209      * @throws IllegalStateException if the peer runtime has not been initialized
1210      */
1211     public boolean isCurrentThreadAttached() {
1212         return compilerToVm.isCurrentThreadAttached();
1213     }
1214 
1215     /**
1216      * Gets the address of the HotSpot {@code JavaThread} C++ object for the current thread. This
1217      * will return {@code 0} if called from an unattached JVMCI shared library thread.
1218      */
1219     public long getCurrentJavaThread() {
1220         return compilerToVm.getCurrentJavaThread();
1221     }
1222 
1223     /**
1224      * Ensures the current thread is attached to the peer runtime.
1225      *
1226      * @param asDaemon if the thread is not yet attached, should it be attached as a daemon
1227      * @return {@code true} if this call attached the current thread, {@code false} if the current
1228      *         thread was already attached
1229      * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e.
1230      *             {@code -XX:-UseJVMCINativeLibrary})
1231      * @throws IllegalStateException if the peer runtime has not been initialized or there is an
1232      *             error while trying to attach the thread
1233      * @throws ArrayIndexOutOfBoundsException if {@code javaVMInfo} is non-null and is shorter than
1234      *             the length of the array returned by {@link #registerNativeMethods}
1235      */
1236     public boolean attachCurrentThread(boolean asDaemon) {
1237         byte[] name = IS_IN_NATIVE_IMAGE ? Thread.currentThread().getName().getBytes() : null;
1238         return compilerToVm.attachCurrentThread(name, asDaemon);
1239     }
1240 
1241     /**
1242      * Detaches the current thread from the peer runtime.
1243      *
1244      * @throws UnsupportedOperationException if the JVMCI shared library is not enabled (i.e.
1245      *             {@code -XX:-UseJVMCINativeLibrary})
1246      * @throws IllegalStateException if the peer runtime has not been initialized or if the current
1247      *             thread is not attached or if there is an error while trying to detach the thread
1248      */
1249     public void detachCurrentThread() {
1250         compilerToVm.detachCurrentThread();
1251     }
1252 
1253     /**
1254      * Informs HotSpot that no method whose module is in {@code modules} is to be compiled with
1255      * {@link #compileMethod}.
1256      *
1257      * @param modules the set of modules containing JVMCI compiler classes
1258      */
1259     public void excludeFromJVMCICompilation(Module... modules) {
1260         this.excludeFromJVMCICompilation = modules.clone();
1261     }
1262 
1263     /**
1264      * Calls {@link System#exit(int)} in HotSpot's runtime.
1265      */
1266     public void exitHotSpot(int status) {
1267         if (!IS_IN_NATIVE_IMAGE) {
1268             System.exit(status);
1269         }
1270         compilerToVm.callSystemExit(status);
1271     }
1272 
1273     /**
1274      * Writes a message to HotSpot's log stream and then calls {@link System#exit(int)} in HotSpot's
1275      * runtime.
1276      */
1277     JVMCIError exitHotSpotWithMessage(int status, String format, Object... args) {
1278         byte[] messageBytes = String.format(format, args).getBytes();
1279         writeDebugOutput(messageBytes, 0, messageBytes.length, true, true);
1280         exitHotSpot(status);
1281         throw JVMCIError.shouldNotReachHere();
1282     }
1283 }