src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/IndirectHotSpotObjectConstantImpl.java
changeset 55463 31bf7b93df5d
parent 54669 ad45b3802d4e
--- 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);