8225810: Update JVMCI
authorkvn
Fri, 21 Jun 2019 16:21:13 -0700
changeset 55463 31bf7b93df5d
parent 55462 6dfdcd31463d
child 55472 72bbc930d7b6
8225810: Update JVMCI Reviewed-by: never, dnsimon
src/hotspot/share/jvmci/jvmciCompilerToVM.cpp
src/hotspot/share/jvmci/jvmciEnv.cpp
src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java
src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCompilationRequest.java
src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java
src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotObjectConstantScope.java
src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedJavaType.java
src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedObjectTypeImpl.java
src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedPrimitiveType.java
src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/IndirectHotSpotObjectConstantImpl.java
src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/SharedLibraryJVMCIReflection.java
src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.meta/src/jdk/vm/ci/meta/MetaUtil.java
test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestMetaAccessProvider.java
test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java
test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TypeUniverse.java
--- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp	Fri Jun 21 13:04:14 2019 -0700
+++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp	Fri Jun 21 16:21:13 2019 -0700
@@ -568,6 +568,33 @@
   return JVMCIENV->get_jobject(result);
 C2V_END
 
+C2V_VMENTRY_NULL(jobject, getArrayType, (JNIEnv* env, jobject, jobject jvmci_type))
+  if (jvmci_type == NULL) {
+    JVMCI_THROW_0(NullPointerException);
+  }
+
+  JVMCIObject jvmci_type_object = JVMCIENV->wrap(jvmci_type);
+  JVMCIKlassHandle array_klass(THREAD);
+  if (JVMCIENV->isa_HotSpotResolvedPrimitiveType(jvmci_type_object)) {
+    BasicType type = JVMCIENV->kindToBasicType(JVMCIENV->get_HotSpotResolvedPrimitiveType_kind(jvmci_type_object), JVMCI_CHECK_0);
+    if (type == T_VOID) {
+      return NULL;
+    }
+    array_klass = Universe::typeArrayKlassObj(type);
+    if (array_klass == NULL) {
+      JVMCI_THROW_MSG_NULL(InternalError, err_msg("No array klass for primitive type %s", type2name(type)));
+    }
+  } else {
+    Klass* klass = JVMCIENV->asKlass(jvmci_type);
+    if (klass == NULL) {
+      JVMCI_THROW_0(NullPointerException);
+    }
+    array_klass = klass->array_klass(CHECK_NULL);
+  }
+  JVMCIObject result = JVMCIENV->get_jvmci_type(array_klass, JVMCI_CHECK_NULL);
+  return JVMCIENV->get_jobject(result);
+C2V_END
+
 C2V_VMENTRY_NULL(jobject, lookupClass, (JNIEnv* env, jobject, jclass mirror))
   requireInHotSpot("lookupClass", JVMCI_CHECK_NULL);
   if (mirror == NULL) {
@@ -2578,6 +2605,18 @@
   return FailedSpeculation::add_failed_speculation(NULL, (FailedSpeculation**)(address) failed_speculations_address, (address) speculation, speculation_len);
 }
 
+C2V_VMENTRY(void, callSystemExit, (JNIEnv* env, jobject, jint status))
+  JavaValue result(T_VOID);
+  JavaCallArguments jargs(1);
+  jargs.push_int(status);
+  JavaCalls::call_static(&result,
+                       SystemDictionary::System_klass(),
+                       vmSymbols::exit_method_name(),
+                       vmSymbols::int_void_signature(),
+                       &jargs,
+                       CHECK);
+}
+
 #define CC (char*)  /*cast a literal from (const char*)*/
 #define FN_PTR(f) CAST_FROM_FN_PTR(void*, &(c2v_ ## f))
 
@@ -2624,6 +2663,7 @@
   {CC "hasNeverInlineDirective",                      CC "(" HS_RESOLVED_METHOD ")Z",                                                       FN_PTR(hasNeverInlineDirective)},
   {CC "shouldInlineMethod",                           CC "(" HS_RESOLVED_METHOD ")Z",                                                       FN_PTR(shouldInlineMethod)},
   {CC "lookupType",                                   CC "(" STRING HS_RESOLVED_KLASS "Z)" HS_RESOLVED_TYPE,                                FN_PTR(lookupType)},
+  {CC "getArrayType",                                 CC "(" HS_RESOLVED_TYPE ")" HS_RESOLVED_KLASS,                                        FN_PTR(getArrayType)},
   {CC "lookupClass",                                  CC "(" CLASS ")" HS_RESOLVED_TYPE,                                                    FN_PTR(lookupClass)},
   {CC "lookupNameInPool",                             CC "(" HS_CONSTANT_POOL "I)" STRING,                                                  FN_PTR(lookupNameInPool)},
   {CC "lookupNameAndTypeRefIndexInPool",              CC "(" HS_CONSTANT_POOL "I)I",                                                        FN_PTR(lookupNameAndTypeRefIndexInPool)},
@@ -2723,6 +2763,7 @@
   {CC "getFailedSpeculationsAddress",                 CC "(" HS_RESOLVED_METHOD ")J",                                                       FN_PTR(getFailedSpeculationsAddress)},
   {CC "releaseFailedSpeculations",                    CC "(J)V",                                                                            FN_PTR(releaseFailedSpeculations)},
   {CC "addFailedSpeculation",                         CC "(J[B)Z",                                                                          FN_PTR(addFailedSpeculation)},
+  {CC "callSystemExit",                               CC "(I)V",                                                                            FN_PTR(callSystemExit)},
 };
 
 int CompilerToVM::methods_count() {
--- a/src/hotspot/share/jvmci/jvmciEnv.cpp	Fri Jun 21 13:04:14 2019 -0700
+++ b/src/hotspot/share/jvmci/jvmciEnv.cpp	Fri Jun 21 16:21:13 2019 -0700
@@ -1361,6 +1361,9 @@
     return Handle(THREAD, obj);
   } else if (isa_IndirectHotSpotObjectConstantImpl(constant)) {
     jlong object_handle = get_IndirectHotSpotObjectConstantImpl_objectHandle(constant);
+    if (object_handle == 0L) {
+      JVMCI_THROW_MSG_(NullPointerException, "Foreign object reference has been cleared", Handle());
+    }
     oop result = resolve_handle(object_handle);
     if (result == NULL) {
       JVMCI_THROW_MSG_(InternalError, "Constant was unexpectedly NULL", Handle());
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java	Fri Jun 21 13:04:14 2019 -0700
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java	Fri Jun 21 16:21:13 2019 -0700
@@ -771,6 +771,12 @@
     native HotSpotResolvedJavaType getComponentType(HotSpotResolvedObjectTypeImpl type);
 
     /**
+     * Get the array class for {@code type}. This can't be done symbolically since anonymous types
+     * can't be looked up by name.
+     */
+    native HotSpotResolvedObjectTypeImpl getArrayType(HotSpotResolvedJavaType type);
+
+    /**
      * Forces initialization of {@code type}.
      */
     native void ensureInitialized(HotSpotResolvedObjectTypeImpl type);
@@ -978,4 +984,9 @@
      * @see HotSpotJVMCIRuntime#detachCurrentThread()
      */
     native void detachCurrentThread();
+
+    /**
+     * @see HotSpotJVMCIRuntime#exitHotSpot(int)
+     */
+    native void callSystemExit(int status);
 }
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCompilationRequest.java	Fri Jun 21 13:04:14 2019 -0700
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCompilationRequest.java	Fri Jun 21 16:21:13 2019 -0700
@@ -87,4 +87,9 @@
     public int getId() {
         return id;
     }
+
+    @Override
+    public String toString() {
+        return id + ":" + super.toString();
+    }
 }
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java	Fri Jun 21 13:04:14 2019 -0700
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java	Fri Jun 21 16:21:13 2019 -0700
@@ -223,6 +223,8 @@
         // so that -XX:+JVMCIPrintProperties shows the option.
         InitTimer(Boolean.class, false, "Specifies if initialization timing is enabled."),
         PrintConfig(Boolean.class, false, "Prints VM configuration available via JVMCI."),
+        AuditHandles(Boolean.class, false, "Record stack trace along with scoped foreign object reference wrappers " +
+                "to debug issue with a wrapper being used after its scope has closed."),
         TraceMethodDataFilter(String.class, null,
                 "Enables tracing of profiling info when read by JVMCI.",
                 "Empty value: trace all methods",
@@ -687,9 +689,11 @@
         return Collections.unmodifiableMap(backends);
     }
 
+    @SuppressWarnings("try")
     @VMEntryPoint
     private HotSpotCompilationRequestResult compileMethod(HotSpotResolvedJavaMethod method, int entryBCI, long compileState, int id) {
-        CompilationRequestResult result = getCompiler().compileMethod(new HotSpotCompilationRequest(method, entryBCI, compileState, id));
+        HotSpotCompilationRequest request = new HotSpotCompilationRequest(method, entryBCI, compileState, id);
+        CompilationRequestResult result = getCompiler().compileMethod(request);
         assert result != null : "compileMethod must always return something";
         HotSpotCompilationRequestResult hsResult;
         if (result instanceof HotSpotCompilationRequestResult) {
@@ -704,7 +708,6 @@
                 hsResult = HotSpotCompilationRequestResult.success(inlinedBytecodes);
             }
         }
-
         return hsResult;
     }
 
@@ -1032,4 +1035,14 @@
     public void excludeFromJVMCICompilation(Module...modules) {
         this.excludeFromJVMCICompilation = modules.clone();
     }
+
+    /**
+     * Calls {@link System#exit(int)} in HotSpot's runtime.
+     */
+    public void exitHotSpot(int status) {
+        if (!IS_IN_NATIVE_IMAGE) {
+            System.exit(status);
+        }
+        compilerToVm.callSystemExit(status);
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotObjectConstantScope.java	Fri Jun 21 16:21:13 2019 -0700
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.vm.ci.hotspot;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import jdk.vm.ci.services.Services;
+
+/**
+ * A mechanism for limiting the lifetime of a foreign object reference encapsulated in a
+ * {@link HotSpotObjectConstant}.
+ *
+ * A {@link HotSpotObjectConstant} allocated in a {@linkplain #openLocalScope local} scope will have
+ * its reference to any foreign object cleared when the scope {@linkplain #close() closes}. This
+ * allows the foreign memory manager to reclaim the foreign object (once there are no other strong
+ * references to it).
+ *
+ * {@link HotSpotObjectConstantScope}s have no impact on {@link HotSpotObjectConstant}s that do not
+ * encapsulate a foreign object reference.
+ *
+ * The object returned by {@link #enterGlobalScope()} or {@link #openLocalScope(Object)} should
+ * always be used in a try-with-resources statement. Failure to close a scope will almost certainly
+ * result in foreign objects being leaked.
+ */
+public final class HotSpotObjectConstantScope implements AutoCloseable {
+    static final ThreadLocal<HotSpotObjectConstantScope> CURRENT = new ThreadLocal<>();
+
+    private final HotSpotObjectConstantScope parent;
+    private List<IndirectHotSpotObjectConstantImpl> foreignObjects;
+
+    /**
+     * An object whose {@link Object#toString()} value describes a non-global scope. This is
+     * {@code null} iff this is a global scope.
+     */
+    final Object localScopeDescription;
+
+    /**
+     * Opens a local scope that upon closing, will release foreign object references encapsulated by
+     * {@link HotSpotObjectConstant}s created in the scope.
+     *
+     * @param description an non-null object whose {@link Object#toString()} value describes the
+     *            scope being opened
+     * @return {@code null} if the current runtime does not support remote object references
+     */
+    public static HotSpotObjectConstantScope openLocalScope(Object description) {
+        return Services.IS_IN_NATIVE_IMAGE ? new HotSpotObjectConstantScope(Objects.requireNonNull(description)) : null;
+    }
+
+    /**
+     * Enters the global scope. This is useful to escape a local scope for execution that will
+     * create foreign object references that need to outlive the local scope.
+     *
+     * Foreign object references encapsulated by {@link HotSpotObjectConstant}s created in the
+     * global scope are only subject to reclamation once the {@link HotSpotObjectConstant} wrapper
+     * dies.
+     *
+     * @return {@code null} if the current runtime does not support remote object references or if
+     *         this thread is currently in the global scope
+     */
+    public static HotSpotObjectConstantScope enterGlobalScope() {
+        return Services.IS_IN_NATIVE_IMAGE && CURRENT.get() != null ? new HotSpotObjectConstantScope(null) : null;
+    }
+
+    private HotSpotObjectConstantScope(Object localScopeDescription) {
+        this.parent = CURRENT.get();
+        CURRENT.set(this);
+        this.localScopeDescription = localScopeDescription;
+    }
+
+    /**
+     * Determines if this scope is global.
+     */
+    boolean isGlobal() {
+        return localScopeDescription == null;
+    }
+
+    void add(IndirectHotSpotObjectConstantImpl obj) {
+        assert !isGlobal();
+        if (foreignObjects == null) {
+            foreignObjects = new ArrayList<>();
+        }
+        foreignObjects.add(obj);
+    }
+
+    @VMEntryPoint
+    @Override
+    public void close() {
+        if (CURRENT.get() != this) {
+            throw new IllegalStateException("Cannot close non-active scope");
+        }
+        if (foreignObjects != null) {
+            for (IndirectHotSpotObjectConstantImpl obj : foreignObjects) {
+                obj.clear(localScopeDescription);
+            }
+            foreignObjects = null;
+        }
+        CURRENT.set(parent);
+    }
+}
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedJavaType.java	Fri Jun 21 13:04:14 2019 -0700
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedJavaType.java	Fri Jun 21 16:21:13 2019 -0700
@@ -22,11 +22,15 @@
  */
 package jdk.vm.ci.hotspot;
 
+import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.runtime;
+
 import jdk.vm.ci.meta.JavaConstant;
 import jdk.vm.ci.meta.ResolvedJavaType;
 
 public abstract class HotSpotResolvedJavaType extends HotSpotJavaType implements ResolvedJavaType {
 
+    HotSpotResolvedObjectTypeImpl arrayOfType;
+
     HotSpotResolvedJavaType(String name) {
         super(name);
     }
@@ -40,4 +44,12 @@
     }
 
     abstract JavaConstant getJavaMirror();
+
+    @Override
+    public HotSpotResolvedObjectType getArrayClass() {
+        if (arrayOfType == null) {
+            arrayOfType = runtime().compilerToVm.getArrayType(this);
+        }
+        return arrayOfType;
+    }
 }
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedObjectTypeImpl.java	Fri Jun 21 13:04:14 2019 -0700
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedObjectTypeImpl.java	Fri Jun 21 16:21:13 2019 -0700
@@ -72,7 +72,6 @@
     private volatile HotSpotResolvedJavaField[] instanceFields;
     private volatile HotSpotResolvedObjectTypeImpl[] interfaces;
     private HotSpotConstantPool constantPool;
-    private HotSpotResolvedObjectType arrayOfType;
     private final JavaConstant mirror;
     private HotSpotResolvedObjectTypeImpl superClass;
 
@@ -103,17 +102,24 @@
      * Creates the JVMCI mirror for a {@link Class} object.
      *
      * <b>NOTE</b>: Creating an instance of this class does not install the mirror for the
-     * {@link Class} type. {@link #fromMetaspace} instead.
+     * {@link Class} type.
      * </p>
      *
      * @param metadataPointer the Klass* to create the mirror for
      */
+    @SuppressWarnings("try")
     HotSpotResolvedObjectTypeImpl(long metadataPointer, String name) {
         super(name);
+        assert metadataPointer != 0;
         this.metadataPointer = metadataPointer;
-        this.mirror = runtime().compilerToVm.getJavaMirror(this);
-        assert metadataPointer != 0;
-        assert getName().charAt(0) != '[' || isArray() : getName();
+
+        // The mirror object must be in the global scope since
+        // this object will be cached in HotSpotJVMCIRuntime.resolvedJavaTypes
+        // and live across more than one compilation.
+        try (HotSpotObjectConstantScope global = HotSpotObjectConstantScope.enterGlobalScope()) {
+            this.mirror = runtime().compilerToVm.getJavaMirror(this);
+            assert getName().charAt(0) != '[' || isArray() : getName();
+        }
     }
 
     /**
@@ -147,18 +153,6 @@
     }
 
     @Override
-    public HotSpotResolvedObjectType getArrayClass() {
-        if (arrayOfType == null) {
-            try {
-                arrayOfType = (HotSpotResolvedObjectType) runtime().compilerToVm.lookupType("[" + getName(), this, true);
-            } catch (ClassNotFoundException e) {
-                throw new JVMCIError(e);
-            }
-        }
-        return arrayOfType;
-    }
-
-    @Override
     public ResolvedJavaType getComponentType() {
         return runtime().compilerToVm.getComponentType(this);
     }
@@ -580,6 +574,11 @@
             return null;
         }
 
+        if (resolvedMethod.canBeStaticallyBound()) {
+            // No assumptions are required.
+            return new AssumptionResult<>(resolvedMethod);
+        }
+
         ResolvedJavaMethod result = resolvedMethod.uniqueConcreteMethod(this);
         if (result != null) {
             return new AssumptionResult<>(result, new ConcreteMethod(method, this, result));
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedPrimitiveType.java	Fri Jun 21 13:04:14 2019 -0700
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedPrimitiveType.java	Fri Jun 21 16:21:13 2019 -0700
@@ -46,7 +46,6 @@
     @NativeImageReinitialize static HotSpotResolvedPrimitiveType[] primitives;
 
     private JavaKind kind;
-    private HotSpotResolvedObjectType arrayClass;
     HotSpotObjectConstantImpl mirror;
 
     /**
@@ -87,14 +86,7 @@
         if (kind == JavaKind.Void) {
             return null;
         }
-        if (arrayClass == null) {
-            try {
-                arrayClass = (HotSpotResolvedObjectType) runtime().compilerToVm.lookupType("[" + kind.getTypeChar(), null, true);
-            } catch (ClassNotFoundException e) {
-                throw new JVMCIError(e);
-            }
-        }
-        return arrayClass;
+        return super.getArrayClass();
     }
 
     @Override
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/IndirectHotSpotObjectConstantImpl.java	Fri Jun 21 13:04:14 2019 -0700
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/IndirectHotSpotObjectConstantImpl.java	Fri Jun 21 16:21:13 2019 -0700
@@ -24,17 +24,52 @@
 
 import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.runtime;
 
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.Option;
 import jdk.vm.ci.meta.JavaConstant;
 
+/**
+ * Encapsulates a JNI reference to an object in the HotSpot heap.
+ *
+ * {@link IndirectHotSpotObjectConstantImpl} objects are only allocated in the shared library heap.
+ *
+ * @see HotSpotObjectConstantScope
+ */
 final class IndirectHotSpotObjectConstantImpl extends HotSpotObjectConstantImpl {
     /**
-     * An object handle in {@code JVMCI::_jvmci_handles}.
+     * An object handle in {@code JVMCI::_object_handles}.
      */
-    final long objectHandle;
+    private long objectHandle;
+
+    /**
+     * Lazily computed hash code.
+     */
     private int hashCode;
 
     final IndirectHotSpotObjectConstantImpl base;
 
+    private static class Audit {
+        final Object scope;
+        final long handle;
+        final Throwable origin;
+
+        Audit(Object scope, long handle, Throwable origin) {
+            this.scope = scope;
+            this.handle = handle;
+            this.origin = origin;
+        }
+    }
+
+    /**
+     * Details useful to audit a scoped handle used after its creating scope closes. Set to an
+     * {@link Audit} object if {@link HotSpotJVMCIRuntime.Option#AuditHandles} is true otherwise to
+     * {@link HotSpotObjectConstantScope#localScopeDescription}.
+     */
+    private Object rawAudit;
+
+    @SuppressWarnings("serial")
     @VMEntryPoint
     private IndirectHotSpotObjectConstantImpl(long objectHandle, boolean compressed, boolean skipRegister) {
         super(compressed);
@@ -42,7 +77,20 @@
         this.objectHandle = objectHandle;
         this.base = null;
         if (!skipRegister) {
-            HandleCleaner.create(this, objectHandle);
+            HotSpotObjectConstantScope scope = HotSpotObjectConstantScope.CURRENT.get();
+            if (scope != null && !scope.isGlobal()) {
+                scope.add(this);
+                if (HotSpotJVMCIRuntime.Option.AuditHandles.getBoolean()) {
+                    rawAudit = new Audit(scope.localScopeDescription, objectHandle, new Throwable() {
+                        @Override
+                        public String toString() {
+                            return "Created " + objectHandle;
+                        }
+                    });
+                }
+            } else {
+                HandleCleaner.create(this, objectHandle);
+            }
         }
     }
 
@@ -50,12 +98,59 @@
         super(compressed);
         // This is a variant of an original object that only varies in compress vs uncompressed.
         // Instead of creating a new handle, reference that object and objectHandle.
-        this.objectHandle = base.objectHandle;
-        // There should only be on level of indirection to the base object.
+        this.objectHandle = base.getHandle();
+        // There should only be one level of indirection to the base object.
         assert base.base == null || base.base.base == null;
         this.base = base.base != null ? base.base : base;
     }
 
+    long getHandle() {
+        checkHandle();
+        return objectHandle;
+    }
+
+    private void checkHandle() {
+        if (objectHandle == 0L) {
+            String message;
+            if (rawAudit instanceof Audit) {
+                Audit audit = (Audit) rawAudit;
+                ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                PrintStream ps = new PrintStream(baos);
+                ps.println("Foreign object reference " + audit.handle + " created in scope '" + audit.scope + "' is no longer valid. Origin: {");
+                audit.origin.printStackTrace(ps);
+                ps.print('}');
+                ps.flush();
+                message = baos.toString();
+            } else {
+                message = "Foreign object reference created in scope '" + rawAudit + "' is no longer valid. " +
+                                "Set property " + Option.AuditHandles.getPropertyName() + "=true to show origin of invalid foreign references.";
+            }
+            throw new NullPointerException(message);
+        }
+    }
+
+    boolean isValid() {
+        return objectHandle != 0L;
+    }
+
+    @Override
+    public HotSpotResolvedObjectType getType() {
+        checkHandle();
+        return super.getType();
+    }
+
+    /**
+     * Clears the foreign object reference.
+     */
+    void clear(Object scopeDescription) {
+        checkHandle();
+        CompilerToVM.compilerToVM().deleteGlobalHandle(objectHandle);
+        if (rawAudit == null) {
+            rawAudit = scopeDescription;
+        }
+        objectHandle = 0L;
+    }
+
     @Override
     public JavaConstant compress() {
         assert !compressed;
@@ -70,6 +165,7 @@
 
     @Override
     public int getIdentityHashCode() {
+        checkHandle();
         int hash = hashCode;
         if (hash == 0) {
             hash = runtime().compilerToVm.getIdentityHashCode(this);
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/SharedLibraryJVMCIReflection.java	Fri Jun 21 13:04:14 2019 -0700
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/SharedLibraryJVMCIReflection.java	Fri Jun 21 16:21:13 2019 -0700
@@ -114,7 +114,7 @@
         }
         IndirectHotSpotObjectConstantImpl indirectX = (IndirectHotSpotObjectConstantImpl) x;
         IndirectHotSpotObjectConstantImpl indirectY = (IndirectHotSpotObjectConstantImpl) y;
-        return runtime().compilerToVm.equals(x, indirectX.objectHandle, y, indirectY.objectHandle);
+        return runtime().compilerToVm.equals(x, indirectX.getHandle(), y, indirectY.getHandle());
     }
 
     @Override
@@ -288,6 +288,10 @@
             DirectHotSpotObjectConstantImpl direct = (DirectHotSpotObjectConstantImpl) object;
             return "CompilerObject<" + direct.object.getClass().getName() + ">";
         }
+        IndirectHotSpotObjectConstantImpl indirect = (IndirectHotSpotObjectConstantImpl) object;
+        if (!indirect.isValid()) {
+            return "Instance<null>";
+        }
         return "Instance<" + object.getType().toJavaName() + ">";
     }
 
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.meta/src/jdk/vm/ci/meta/MetaUtil.java	Fri Jun 21 13:04:14 2019 -0700
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.meta/src/jdk/vm/ci/meta/MetaUtil.java	Fri Jun 21 16:21:13 2019 -0700
@@ -87,6 +87,29 @@
     }
 
     /**
+     * Classes for lambdas can have {@code /} characters that are not package separators. These are
+     * distinguished by being followed by a character that is not a
+     * {@link Character#isJavaIdentifierStart(char)} (e.g.,
+     * "jdk.vm.ci.runtime.test.TypeUniverse$$Lambda$1/869601985").
+     */
+    private static String replacePackageSeparatorsWithDot(String name) {
+        int length = name.length();
+        int i = 0;
+        StringBuilder buf = new StringBuilder(length);
+        while (i < length - 1) {
+            char ch = name.charAt(i);
+            if (ch == '/' && Character.isJavaIdentifierStart(name.charAt(i + 1))) {
+                buf.append('.');
+            } else {
+                buf.append(ch);
+            }
+            i++;
+        }
+        buf.append(name.charAt(length - 1));
+        return buf.toString();
+    }
+
+    /**
      * Converts a type name in internal form to an external form.
      *
      * @param name the internal name to convert
@@ -99,7 +122,7 @@
     public static String internalNameToJava(String name, boolean qualified, boolean classForNameCompatible) {
         switch (name.charAt(0)) {
             case 'L': {
-                String result = name.substring(1, name.length() - 1).replace('/', '.');
+                String result = replacePackageSeparatorsWithDot(name.substring(1, name.length() - 1));
                 if (!qualified) {
                     final int lastDot = result.lastIndexOf('.');
                     if (lastDot != -1) {
@@ -109,7 +132,7 @@
                 return result;
             }
             case '[':
-                return classForNameCompatible ? name.replace('/', '.') : internalNameToJava(name.substring(1), qualified, classForNameCompatible) + "[]";
+                return classForNameCompatible ? replacePackageSeparatorsWithDot(name) : internalNameToJava(name.substring(1), qualified, classForNameCompatible) + "[]";
             default:
                 if (name.length() != 1) {
                     throw new IllegalArgumentException("Illegal internal name: " + name);
--- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestMetaAccessProvider.java	Fri Jun 21 13:04:14 2019 -0700
+++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestMetaAccessProvider.java	Fri Jun 21 16:21:13 2019 -0700
@@ -68,16 +68,22 @@
                     metaAccess.encodeDeoptActionAndReason(DEOPT_ACTION, DEOPT_REASON, DEBUG_IDS[3]).asInt()
     };
 
+    private static boolean isUnsafeAnoymous(ResolvedJavaType type) {
+        return type.getHostClass() != null;
+    }
+
     @Test
     public void lookupJavaTypeTest() {
         for (Class<?> c : classes) {
             ResolvedJavaType type = metaAccess.lookupJavaType(c);
             assertNotNull(c.toString(), type);
-            assertEquals(c.toString(), type.getName(), toInternalName(c.getName()));
-            assertEquals(c.toString(), type.getName(), toInternalName(type.toJavaName()));
-            assertEquals(c.toString(), c.getName(), type.toClassName());
-            if (!type.isArray()) {
-                assertEquals(c.toString(), c.getName(), type.toJavaName());
+            if (!isUnsafeAnoymous(type)) {
+                assertEquals(c.toString(), type.getName(), toInternalName(c.getName()));
+                assertEquals(c.toString(), type.getName(), toInternalName(type.toJavaName()));
+                assertEquals(c.toString(), c.getName(), type.toClassName());
+                if (!type.isArray()) {
+                    assertEquals(c.toString(), c.getName(), type.toJavaName());
+                }
             }
         }
     }
@@ -92,7 +98,9 @@
         ResolvedJavaType[] result = metaAccess.lookupJavaTypes(classes.toArray(new Class<?>[classes.size()]));
         int counter = 0;
         for (Class<?> aClass : classes) {
-            assertEquals("Unexpected javaType: " + result[counter] + " while expecting of class: " + aClass, result[counter].toClassName(), aClass.getName());
+            if (!isUnsafeAnoymous(result[counter])) {
+                assertEquals("Unexpected javaType: " + result[counter] + " while expecting of class: " + aClass, result[counter].toClassName(), aClass.getName());
+            }
             counter++;
         }
     }
--- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java	Fri Jun 21 13:04:14 2019 -0700
+++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java	Fri Jun 21 16:21:13 2019 -0700
@@ -161,7 +161,11 @@
         for (Class<?> c : classes) {
             ResolvedJavaType type = metaAccess.lookupJavaType(c);
             ResolvedJavaType host = type.getHostClass();
-            assertNull(host);
+            if (!type.equals(predicateType)) {
+                assertNull(host);
+            } else {
+                assertNotNull(host);
+            }
         }
 
         class LocalClass {}
--- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TypeUniverse.java	Fri Jun 21 13:04:14 2019 -0700
+++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TypeUniverse.java	Fri Jun 21 16:21:13 2019 -0700
@@ -51,6 +51,7 @@
 import java.util.Queue;
 import java.util.Set;
 import java.util.TreeMap;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 import static java.lang.reflect.Modifier.isFinal;
@@ -68,6 +69,7 @@
     public static final ConstantReflectionProvider constantReflection = JVMCI.getRuntime().getHostJVMCIBackend().getConstantReflection();
     public static final Collection<Class<?>> classes = new HashSet<>();
     public static final Set<ResolvedJavaType> javaTypes;
+    public static final ResolvedJavaType predicateType;
     public static final Map<Class<?>, Class<?>> arrayClasses = new HashMap<>();
 
     private static List<ConstantValue> constants;
@@ -116,6 +118,9 @@
         for (Class<?> c : initialClasses) {
             addClass(c);
         }
+        Predicate<String> predicate = s -> s.length() == 1;
+        addClass(predicate.getClass());
+        predicateType = metaAccess.lookupJavaType(predicate.getClass());
 
         javaTypes = Collections.unmodifiableSet(classes.stream().map(c -> metaAccess.lookupJavaType(c)).collect(Collectors.toSet()));
     }