8234923: Missed call_site_target nmethod dependency for non-fully initialized ConstantCallSite instance

Reviewed-by: jrose
diff --git a/src/hotspot/share/c1/c1_GraphBuilder.cpp b/src/hotspot/share/c1/c1_GraphBuilder.cpp
index 68ab481..2ddf577 100644
--- a/src/hotspot/share/c1/c1_GraphBuilder.cpp
+++ b/src/hotspot/share/c1/c1_GraphBuilder.cpp
@@ -1707,7 +1707,7 @@
             // For CallSite objects add a dependency for invalidation of the optimization.
             if (field->is_call_site_target()) {
               ciCallSite* call_site = const_oop->as_call_site();
-              if (!call_site->is_constant_call_site()) {
+              if (!call_site->is_fully_initialized_constant_call_site()) {
                 ciMethodHandle* target = field_value.as_object()->as_method_handle();
                 dependency_recorder()->assert_call_site_target_value(call_site, target);
               }
diff --git a/src/hotspot/share/ci/ciCallSite.cpp b/src/hotspot/share/ci/ciCallSite.cpp
index f3d2ca1..8be8c4a 100644
--- a/src/hotspot/share/ci/ciCallSite.cpp
+++ b/src/hotspot/share/ci/ciCallSite.cpp
@@ -29,8 +29,18 @@
 
 // ciCallSite
 
-bool ciCallSite::is_constant_call_site() {
-  return klass()->is_subclass_of(CURRENT_ENV->ConstantCallSite_klass());
+bool ciCallSite::is_fully_initialized_constant_call_site() {
+  if (klass()->is_subclass_of(CURRENT_ENV->ConstantCallSite_klass())) {
+    bool is_fully_initialized = _is_fully_initialized_cache;
+    if (!is_fully_initialized) { // changes monotonically: false => true
+      VM_ENTRY_MARK;
+      is_fully_initialized = (java_lang_invoke_ConstantCallSite::is_frozen(get_oop()) != JNI_FALSE);
+      _is_fully_initialized_cache = is_fully_initialized; // cache updated value
+    }
+    return is_fully_initialized;
+  } else {
+    return false;
+  }
 }
 
 // ------------------------------------------------------------------
diff --git a/src/hotspot/share/ci/ciCallSite.hpp b/src/hotspot/share/ci/ciCallSite.hpp
index b7f6a27..2204636 100644
--- a/src/hotspot/share/ci/ciCallSite.hpp
+++ b/src/hotspot/share/ci/ciCallSite.hpp
@@ -31,13 +31,16 @@
 //
 // The class represents a java.lang.invoke.CallSite object.
 class ciCallSite : public ciInstance {
-public:
-  ciCallSite(instanceHandle h_i) : ciInstance(h_i) {}
+ private:
+  bool _is_fully_initialized_cache;
+
+ public:
+  ciCallSite(instanceHandle h_i) : ciInstance(h_i), _is_fully_initialized_cache(false) {}
 
   // What kind of ciObject is this?
   bool is_call_site() const { return true; }
 
-  bool is_constant_call_site();
+  bool is_fully_initialized_constant_call_site();
 
   // Return the target MethodHandle of this CallSite.
   ciMethodHandle* get_target() const;
diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp
index c0e3a57..77a931a 100644
--- a/src/hotspot/share/classfile/javaClasses.cpp
+++ b/src/hotspot/share/classfile/javaClasses.cpp
@@ -3918,6 +3918,24 @@
   return dep_oop;
 }
 
+// Support for java_lang_invoke_ConstantCallSite
+
+int java_lang_invoke_ConstantCallSite::_is_frozen_offset;
+
+#define CONSTANTCALLSITE_FIELDS_DO(macro) \
+  macro(_is_frozen_offset, k, "isFrozen", bool_signature, false)
+
+void java_lang_invoke_ConstantCallSite::compute_offsets() {
+  InstanceKlass* k = SystemDictionary::ConstantCallSite_klass();
+  CONSTANTCALLSITE_FIELDS_DO(FIELD_COMPUTE_OFFSET);
+}
+
+#if INCLUDE_CDS
+void java_lang_invoke_ConstantCallSite::serialize_offsets(SerializeClosure* f) {
+  CONSTANTCALLSITE_FIELDS_DO(FIELD_SERIALIZE_OFFSET);
+}
+#endif
+
 // Support for java_lang_invoke_MethodHandleNatives_CallSiteContext
 
 int java_lang_invoke_MethodHandleNatives_CallSiteContext::_vmdependencies_offset;
diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp
index 185e3fd..b1fea1f 100644
--- a/src/hotspot/share/classfile/javaClasses.hpp
+++ b/src/hotspot/share/classfile/javaClasses.hpp
@@ -66,6 +66,7 @@
   f(java_lang_invoke_LambdaForm) \
   f(java_lang_invoke_MethodType) \
   f(java_lang_invoke_CallSite) \
+  f(java_lang_invoke_ConstantCallSite) \
   f(java_lang_invoke_MethodHandleNatives_CallSiteContext) \
   f(java_security_AccessControlContext) \
   f(java_lang_reflect_AccessibleObject) \
@@ -1226,6 +1227,28 @@
   static int target_offset_in_bytes()           { return _target_offset; }
 };
 
+// Interface to java.lang.invoke.ConstantCallSite objects
+
+class java_lang_invoke_ConstantCallSite: AllStatic {
+  friend class JavaClasses;
+
+private:
+  static int _is_frozen_offset;
+
+  static void compute_offsets();
+
+public:
+  static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN;
+  // Accessors
+  static jboolean is_frozen(oop site);
+
+  // Testers
+  static bool is_subclass(Klass* klass) {
+    return klass->is_subclass_of(SystemDictionary::ConstantCallSite_klass());
+  }
+  static bool is_instance(oop obj);
+};
+
 // Interface to java.lang.invoke.MethodHandleNatives$CallSiteContext objects
 
 #define CALLSITECONTEXT_INJECTED_FIELDS(macro) \
diff --git a/src/hotspot/share/classfile/javaClasses.inline.hpp b/src/hotspot/share/classfile/javaClasses.inline.hpp
index 18176fc..a003943 100644
--- a/src/hotspot/share/classfile/javaClasses.inline.hpp
+++ b/src/hotspot/share/classfile/javaClasses.inline.hpp
@@ -179,6 +179,14 @@
   return obj != NULL && is_subclass(obj->klass());
 }
 
+inline jboolean java_lang_invoke_ConstantCallSite::is_frozen(oop site) {
+  return site->bool_field(_is_frozen_offset);
+}
+
+inline bool java_lang_invoke_ConstantCallSite::is_instance(oop obj) {
+  return obj != NULL && is_subclass(obj->klass());
+}
+
 inline bool java_lang_invoke_MethodHandleNatives_CallSiteContext::is_instance(oop obj) {
   return obj != NULL && is_subclass(obj->klass());
 }
diff --git a/src/hotspot/share/opto/type.cpp b/src/hotspot/share/opto/type.cpp
index 50b9aca..6098be8 100644
--- a/src/hotspot/share/opto/type.cpp
+++ b/src/hotspot/share/opto/type.cpp
@@ -375,7 +375,7 @@
                                             field->is_autobox_cache());
   if (con_type != NULL && field->is_call_site_target()) {
     ciCallSite* call_site = holder->as_call_site();
-    if (!call_site->is_constant_call_site()) {
+    if (!call_site->is_fully_initialized_constant_call_site()) {
       ciMethodHandle* target = con.as_object()->as_method_handle();
       Compile::current()->dependencies()->assert_call_site_target_value(call_site, target);
     }
diff --git a/src/java.base/share/classes/java/lang/invoke/ConstantCallSite.java b/src/java.base/share/classes/java/lang/invoke/ConstantCallSite.java
index c8a0278..e2e3f47 100644
--- a/src/java.base/share/classes/java/lang/invoke/ConstantCallSite.java
+++ b/src/java.base/share/classes/java/lang/invoke/ConstantCallSite.java
@@ -39,7 +39,7 @@
     private static final Unsafe UNSAFE = Unsafe.getUnsafe();
 
     @Stable // should NOT be constant folded during instance initialization (isFrozen == false)
-    /*final*/ private boolean isFrozen;
+    /*final*/ private boolean isFrozen; // Note: This field is known to the JVM.
 
     /**
      * Creates a call site with a permanent target.
diff --git a/test/jdk/java/lang/invoke/CallSiteTest.java b/test/jdk/java/lang/invoke/CallSiteTest.java
index 408c497..7caf8ac 100644
--- a/test/jdk/java/lang/invoke/CallSiteTest.java
+++ b/test/jdk/java/lang/invoke/CallSiteTest.java
@@ -128,7 +128,9 @@
         if (ccs != holder[0]) {
             throw new AssertionError("different call site instances");
         }
-        test(false); // should not throw
+        for (int i = 0; i < 20_000; i++) {
+            test(false); // should not throw
+        }
     }
 
     private static void testMutableCallSite() throws Throwable {