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