Merge
authorvlivanov
Fri, 17 Apr 2015 16:45:55 +0000
changeset 30297 fdef6544a94f
parent 30295 d91d9c285139 (current diff)
parent 30296 95baefac8485 (diff)
child 30298 00461a39b519
Merge
--- a/hotspot/src/share/vm/ci/ciCallSite.cpp	Wed Apr 15 11:36:42 2015 +0200
+++ b/hotspot/src/share/vm/ci/ciCallSite.cpp	Fri Apr 17 16:45:55 2015 +0000
@@ -50,6 +50,25 @@
 }
 
 // ------------------------------------------------------------------
+// ciCallSite::get_context
+//
+// Return the target MethodHandle of this CallSite.
+ciKlass* ciCallSite::get_context() {
+  assert(!is_constant_call_site(), "");
+
+  VM_ENTRY_MARK;
+  oop call_site_oop = get_oop();
+  InstanceKlass* ctxk = MethodHandles::get_call_site_context(call_site_oop);
+  if (ctxk == NULL) {
+    // The call site doesn't have a context associated. Set it to the default context.
+    oop def_context_oop = java_lang_invoke_CallSite::default_context();
+    java_lang_invoke_CallSite::set_context_cas(call_site_oop, def_context_oop, /*expected=*/NULL);
+    ctxk = MethodHandles::get_call_site_context(call_site_oop);
+  }
+  return (CURRENT_ENV->get_metadata(ctxk))->as_klass();
+}
+
+// ------------------------------------------------------------------
 // ciCallSite::print
 //
 // Print debugging information about the CallSite.
--- a/hotspot/src/share/vm/ci/ciCallSite.hpp	Wed Apr 15 11:36:42 2015 +0200
+++ b/hotspot/src/share/vm/ci/ciCallSite.hpp	Fri Apr 17 16:45:55 2015 +0000
@@ -43,6 +43,7 @@
 
   // Return the target MethodHandle of this CallSite.
   ciMethodHandle* get_target() const;
+  ciKlass* get_context();
 
   void print();
 };
--- a/hotspot/src/share/vm/classfile/javaClasses.cpp	Wed Apr 15 11:36:42 2015 +0200
+++ b/hotspot/src/share/vm/classfile/javaClasses.cpp	Fri Apr 17 16:45:55 2015 +0000
@@ -102,21 +102,22 @@
 static bool find_field(InstanceKlass* ik,
                        Symbol* name_symbol, Symbol* signature_symbol,
                        fieldDescriptor* fd,
-                       bool allow_super = false) {
-  if (allow_super)
-    return ik->find_field(name_symbol, signature_symbol, fd) != NULL;
-  else
+                       bool is_static = false, bool allow_super = false) {
+  if (allow_super || is_static) {
+    return ik->find_field(name_symbol, signature_symbol, is_static, fd) != NULL;
+  } else {
     return ik->find_local_field(name_symbol, signature_symbol, fd);
+  }
 }
 
 // Helpful routine for computing field offsets at run time rather than hardcoding them
 static void
 compute_offset(int &dest_offset,
                Klass* klass_oop, Symbol* name_symbol, Symbol* signature_symbol,
-               bool allow_super = false) {
+               bool is_static = false, bool allow_super = false) {
   fieldDescriptor fd;
   InstanceKlass* ik = InstanceKlass::cast(klass_oop);
-  if (!find_field(ik, name_symbol, signature_symbol, &fd, allow_super)) {
+  if (!find_field(ik, name_symbol, signature_symbol, &fd, is_static, allow_super)) {
     ResourceMark rm;
     tty->print_cr("Invalid layout of %s at %s", ik->external_name(), name_symbol->as_C_string());
 #ifndef PRODUCT
@@ -2972,14 +2973,49 @@
 // Support for java_lang_invoke_CallSite
 
 int java_lang_invoke_CallSite::_target_offset;
+int java_lang_invoke_CallSite::_context_offset;
+int java_lang_invoke_CallSite::_default_context_offset;
 
 void java_lang_invoke_CallSite::compute_offsets() {
   Klass* k = SystemDictionary::CallSite_klass();
   if (k != NULL) {
     compute_offset(_target_offset, k, vmSymbols::target_name(), vmSymbols::java_lang_invoke_MethodHandle_signature());
+    compute_offset(_context_offset, k, vmSymbols::context_name(), vmSymbols::sun_misc_Cleaner_signature());
+    compute_offset(_default_context_offset, k,
+                   vmSymbols::DEFAULT_CONTEXT_name(), vmSymbols::sun_misc_Cleaner_signature(),
+                   /*is_static=*/true, /*allow_super=*/false);
   }
 }
 
+oop java_lang_invoke_CallSite::context_volatile(oop call_site) {
+  assert(java_lang_invoke_CallSite::is_instance(call_site), "");
+
+  oop dep_oop = call_site->obj_field_volatile(_context_offset);
+  return dep_oop;
+}
+
+void java_lang_invoke_CallSite::set_context_volatile(oop call_site, oop context) {
+  assert(java_lang_invoke_CallSite::is_instance(call_site), "");
+  call_site->obj_field_put_volatile(_context_offset, context);
+}
+
+bool java_lang_invoke_CallSite::set_context_cas(oop call_site, oop context, oop expected) {
+  assert(java_lang_invoke_CallSite::is_instance(call_site), "");
+  HeapWord* context_addr = call_site->obj_field_addr<HeapWord>(_context_offset);
+  oop res = oopDesc::atomic_compare_exchange_oop(context, context_addr, expected, true);
+  bool success = (res == expected);
+  if (success) {
+    update_barrier_set((void*)context_addr, context);
+  }
+  return success;
+}
+
+oop java_lang_invoke_CallSite::default_context() {
+  InstanceKlass* ik = InstanceKlass::cast(SystemDictionary::CallSite_klass());
+  oop def_context_oop = ik->java_mirror()->obj_field(_default_context_offset);
+  assert(!oopDesc::is_null(def_context_oop), "");
+  return def_context_oop;
+}
 
 // Support for java_security_AccessControlContext
 
--- a/hotspot/src/share/vm/classfile/javaClasses.hpp	Wed Apr 15 11:36:42 2015 +0200
+++ b/hotspot/src/share/vm/classfile/javaClasses.hpp	Fri Apr 17 16:45:55 2015 +0000
@@ -961,7 +961,6 @@
   static void set_clock(jlong value);
 };
 
-
 // Interface to java.lang.invoke.MethodHandle objects
 
 class MethodHandleEntry;
@@ -1173,16 +1172,25 @@
 
 private:
   static int _target_offset;
+  static int _context_offset;
+  static int _default_context_offset;
+
 
   static void compute_offsets();
 
 public:
   // Accessors
-  static oop              target(         oop site);
-  static void         set_target(         oop site, oop target);
+  static oop              target(          oop site);
+  static void         set_target(          oop site, oop target);
+
+  static volatile oop     target_volatile( oop site);
+  static void         set_target_volatile( oop site, oop target);
 
-  static volatile oop     target_volatile(oop site);
-  static void         set_target_volatile(oop site, oop target);
+  static oop              context_volatile(oop site);
+  static void         set_context_volatile(oop site, oop context);
+  static bool         set_context_cas     (oop site, oop context, oop expected);
+
+  static oop default_context();
 
   // Testers
   static bool is_subclass(Klass* klass) {
@@ -1194,7 +1202,6 @@
   static int target_offset_in_bytes()           { return _target_offset; }
 };
 
-
 // Interface to java.security.AccessControlContext objects
 
 class java_security_AccessControlContext: AllStatic {
--- a/hotspot/src/share/vm/classfile/vmSymbols.hpp	Wed Apr 15 11:36:42 2015 +0200
+++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp	Fri Apr 17 16:45:55 2015 +0000
@@ -292,6 +292,7 @@
   template(setTargetNormal_name,                      "setTargetNormal")                          \
   template(setTargetVolatile_name,                    "setTargetVolatile")                        \
   template(setTarget_signature,                       "(Ljava/lang/invoke/MethodHandle;)V")       \
+  template(DEFAULT_CONTEXT_name,                      "DEFAULT_CONTEXT")                          \
   NOT_LP64(  do_alias(intptr_signature,               int_signature)  )                           \
   LP64_ONLY( do_alias(intptr_signature,               long_signature) )                           \
                                                                                                   \
@@ -501,6 +502,7 @@
   template(class_signature,                           "Ljava/lang/Class;")                                        \
   template(string_signature,                          "Ljava/lang/String;")                                       \
   template(reference_signature,                       "Ljava/lang/ref/Reference;")                                \
+  template(sun_misc_Cleaner_signature,                "Lsun/misc/Cleaner;")                                       \
   template(executable_signature,                      "Ljava/lang/reflect/Executable;")                           \
   template(concurrenthashmap_signature,               "Ljava/util/concurrent/ConcurrentHashMap;")                 \
   template(String_StringBuilder_signature,            "(Ljava/lang/String;)Ljava/lang/StringBuilder;")            \
@@ -554,7 +556,7 @@
   template(createGarbageCollectorMBean_signature,      "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/management/GarbageCollectorMBean;") \
   template(trigger_name,                               "trigger")                                                 \
   template(clear_name,                                 "clear")                                                   \
-  template(trigger_method_signature,                   "(ILjava/lang/management/MemoryUsage;)V")                                                 \
+  template(trigger_method_signature,                   "(ILjava/lang/management/MemoryUsage;)V")                  \
   template(startAgent_name,                            "startAgent")                                              \
   template(startRemoteAgent_name,                      "startRemoteManagementAgent")                              \
   template(startLocalAgent_name,                       "startLocalManagementAgent")                               \
--- a/hotspot/src/share/vm/code/codeCache.cpp	Wed Apr 15 11:36:42 2015 +0200
+++ b/hotspot/src/share/vm/code/codeCache.cpp	Fri Apr 17 16:45:55 2015 +0000
@@ -1067,8 +1067,11 @@
   int marked = 0;
   {
     MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
-    InstanceKlass* call_site_klass = InstanceKlass::cast(call_site->klass());
-    marked = call_site_klass->mark_dependent_nmethods(changes);
+    InstanceKlass* ctxk = MethodHandles::get_call_site_context(call_site());
+    if (ctxk == NULL) {
+      return; // No dependencies to invalidate yet.
+    }
+    marked = ctxk->mark_dependent_nmethods(changes);
   }
   if (marked > 0) {
     // At least one nmethod has been marked for deoptimization
--- a/hotspot/src/share/vm/code/dependencies.cpp	Wed Apr 15 11:36:42 2015 +0200
+++ b/hotspot/src/share/vm/code/dependencies.cpp	Fri Apr 17 16:45:55 2015 +0000
@@ -117,8 +117,9 @@
 }
 
 void Dependencies::assert_call_site_target_value(ciCallSite* call_site, ciMethodHandle* method_handle) {
-  check_ctxk(call_site->klass());
-  assert_common_2(call_site_target_value, call_site, method_handle);
+  ciKlass* ctxk = call_site->get_context();
+  check_ctxk(ctxk);
+  assert_common_3(call_site_target_value, ctxk, call_site, method_handle);
 }
 
 // Helper function.  If we are adding a new dep. under ctxk2,
@@ -388,7 +389,7 @@
   3, // unique_concrete_subtypes_2 ctxk, k1, k2
   3, // unique_concrete_methods_2 ctxk, m1, m2
   1, // no_finalizable_subclasses ctxk
-  2  // call_site_target_value call_site, method_handle
+  3  // call_site_target_value ctxk, call_site, method_handle
 };
 
 const char* Dependencies::dep_name(Dependencies::DepType dept) {
@@ -594,7 +595,7 @@
   const int nargs = argument_count();
   GrowableArray<DepArgument>* args = new GrowableArray<DepArgument>(nargs);
   for (int j = 0; j < nargs; j++) {
-    if (type() == call_site_target_value) {
+    if (is_oop_argument(j)) {
       args->push(argument_oop(j));
     } else {
       args->push(argument(j));
@@ -614,7 +615,7 @@
   int nargs = argument_count();
   GrowableArray<DepArgument>* args = new GrowableArray<DepArgument>(nargs);
   for (int j = 0; j < nargs; j++) {
-    if (type() == call_site_target_value) {
+    if (is_oop_argument(j)) {
       args->push(argument_oop(j));
     } else {
       args->push(argument(j));
@@ -710,7 +711,7 @@
  * Returns a unique identifier for each dependency argument.
  */
 uintptr_t Dependencies::DepStream::get_identifier(int i) {
-  if (has_oop_argument()) {
+  if (is_oop_argument(i)) {
     return (uintptr_t)(oopDesc*)argument_oop(i);
   } else {
     return (uintptr_t)argument(i);
@@ -737,7 +738,7 @@
   }
 
   // Some dependencies are using the klass of the first object
-  // argument as implicit context type (e.g. call_site_target_value).
+  // argument as implicit context type.
   {
     int ctxkj = dep_implicit_context_arg(type());
     if (ctxkj >= 0) {
@@ -1514,9 +1515,16 @@
   return find_finalizable_subclass(search_at);
 }
 
-Klass* Dependencies::check_call_site_target_value(oop call_site, oop method_handle, CallSiteDepChange* changes) {
-  assert(call_site    ->is_a(SystemDictionary::CallSite_klass()),     "sanity");
-  assert(method_handle->is_a(SystemDictionary::MethodHandle_klass()), "sanity");
+Klass* Dependencies::check_call_site_target_value(Klass* recorded_ctxk, oop call_site, oop method_handle, CallSiteDepChange* changes) {
+  assert(call_site->is_a(SystemDictionary::CallSite_klass()),     "sanity");
+  assert(!oopDesc::is_null(method_handle), "sanity");
+
+  Klass* call_site_ctxk = MethodHandles::get_call_site_context(call_site);
+  assert(!Klass::is_null(call_site_ctxk), "call site context should be initialized already");
+  if (recorded_ctxk != call_site_ctxk) {
+    // Stale context
+    return recorded_ctxk;
+  }
   if (changes == NULL) {
     // Validate all CallSites
     if (java_lang_invoke_CallSite::target(call_site) != method_handle)
@@ -1531,7 +1539,6 @@
   return NULL;  // assertion still valid
 }
 
-
 void Dependencies::DepStream::trace_and_log_witness(Klass* witness) {
   if (witness != NULL) {
     if (TraceDependencies) {
@@ -1592,7 +1599,7 @@
   Klass* witness = NULL;
   switch (type()) {
   case call_site_target_value:
-    witness = check_call_site_target_value(argument_oop(0), argument_oop(1), changes);
+    witness = check_call_site_target_value(context_type(), argument_oop(1), argument_oop(2), changes);
     break;
   default:
     witness = NULL;
--- a/hotspot/src/share/vm/code/dependencies.hpp	Wed Apr 15 11:36:42 2015 +0200
+++ b/hotspot/src/share/vm/code/dependencies.hpp	Fri Apr 17 16:45:55 2015 +0000
@@ -174,7 +174,7 @@
     klass_types         = all_types & ~non_klass_types,
 
     non_ctxk_types      = (1 << evol_method),
-    implicit_ctxk_types = (1 << call_site_target_value),
+    implicit_ctxk_types = 0,
     explicit_ctxk_types = all_types & ~(non_ctxk_types | implicit_ctxk_types),
 
     max_arg_count = 3,   // current maximum number of arguments (incl. ctxk)
@@ -330,7 +330,7 @@
   static Klass* check_exclusive_concrete_methods(Klass* ctxk, Method* m1, Method* m2,
                                                    KlassDepChange* changes = NULL);
   static Klass* check_has_no_finalizable_subclasses(Klass* ctxk, KlassDepChange* changes = NULL);
-  static Klass* check_call_site_target_value(oop call_site, oop method_handle, CallSiteDepChange* changes = NULL);
+  static Klass* check_call_site_target_value(Klass* recorded_ctxk, oop call_site, oop method_handle, CallSiteDepChange* changes = NULL);
   // A returned Klass* is NULL if the dependency assertion is still
   // valid.  A non-NULL Klass* is a 'witness' to the assertion
   // failure, a point in the class hierarchy where the assertion has
@@ -496,7 +496,7 @@
     bool next();
 
     DepType type()               { return _type; }
-    bool has_oop_argument()      { return type() == call_site_target_value; }
+    bool is_oop_argument(int i)  { return type() == call_site_target_value && i > 0; }
     uintptr_t get_identifier(int i);
 
     int argument_count()         { return dep_args(type()); }
@@ -682,7 +682,7 @@
       _method_handle(method_handle)
   {
     assert(_call_site()    ->is_a(SystemDictionary::CallSite_klass()),     "must be");
-    assert(_method_handle()->is_a(SystemDictionary::MethodHandle_klass()), "must be");
+    assert(_method_handle.is_null() || _method_handle()->is_a(SystemDictionary::MethodHandle_klass()), "must be");
   }
 
   // What kind of DepChange is this?
--- a/hotspot/src/share/vm/code/nmethod.cpp	Wed Apr 15 11:36:42 2015 +0200
+++ b/hotspot/src/share/vm/code/nmethod.cpp	Fri Apr 17 16:45:55 2015 +0000
@@ -2325,6 +2325,7 @@
             // Dependency checking failed. Print out information about the failed
             // dependency and finally fail with an assert. We can fail here, since
             // dependency checking is never done in a product build.
+            tty->print_cr("Failed dependency:");
             changes.print();
             nm->print();
             nm->print_dependencies();
--- a/hotspot/src/share/vm/prims/methodHandles.cpp	Wed Apr 15 11:36:42 2015 +0200
+++ b/hotspot/src/share/vm/prims/methodHandles.cpp	Fri Apr 17 16:45:55 2015 +0000
@@ -939,6 +939,24 @@
   return rfill + overflow;
 }
 
+// Get context class for a CallSite instance: either extract existing context or use default one.
+InstanceKlass* MethodHandles::get_call_site_context(oop call_site) {
+  // In order to extract a context the following traversal is performed:
+  //   CallSite.context => Cleaner.referent => Class._klass => Klass
+  assert(java_lang_invoke_CallSite::is_instance(call_site), "");
+  oop context_oop = java_lang_invoke_CallSite::context_volatile(call_site);
+  if (oopDesc::is_null(context_oop)) {
+    return NULL; // The context hasn't been initialized yet.
+  }
+  oop context_class_oop = java_lang_ref_Reference::referent(context_oop);
+  if (oopDesc::is_null(context_class_oop)) {
+    // The context reference was cleared by GC, so current dependency context
+    // isn't usable anymore. Context should be fetched from CallSite again.
+    return NULL;
+  }
+  return InstanceKlass::cast(java_lang_Class::as_Klass(context_class_oop));
+}
+
 //------------------------------------------------------------------------------
 // MemberNameTable
 //
@@ -1231,7 +1249,7 @@
 
 JVM_ENTRY(void, MHN_setCallSiteTargetNormal(JNIEnv* env, jobject igcls, jobject call_site_jh, jobject target_jh)) {
   Handle call_site(THREAD, JNIHandles::resolve_non_null(call_site_jh));
-  Handle target   (THREAD, JNIHandles::resolve(target_jh));
+  Handle target   (THREAD, JNIHandles::resolve_non_null(target_jh));
   {
     // Walk all nmethods depending on this call site.
     MutexLocker mu(Compile_lock, thread);
@@ -1243,7 +1261,7 @@
 
 JVM_ENTRY(void, MHN_setCallSiteTargetVolatile(JNIEnv* env, jobject igcls, jobject call_site_jh, jobject target_jh)) {
   Handle call_site(THREAD, JNIHandles::resolve_non_null(call_site_jh));
-  Handle target   (THREAD, JNIHandles::resolve(target_jh));
+  Handle target   (THREAD, JNIHandles::resolve_non_null(target_jh));
   {
     // Walk all nmethods depending on this call site.
     MutexLocker mu(Compile_lock, thread);
@@ -1253,6 +1271,33 @@
 }
 JVM_END
 
+JVM_ENTRY(void, MHN_invalidateDependentNMethods(JNIEnv* env, jobject igcls, jobject call_site_jh)) {
+  Handle call_site(THREAD, JNIHandles::resolve_non_null(call_site_jh));
+  {
+    // Walk all nmethods depending on this call site.
+    MutexLocker mu1(Compile_lock, thread);
+
+    CallSiteDepChange changes(call_site(), Handle());
+
+    InstanceKlass* ctxk = MethodHandles::get_call_site_context(call_site());
+    if (ctxk == NULL) {
+      return; // No dependencies to invalidate yet.
+    }
+    int marked = 0;
+    {
+      MutexLockerEx mu2(CodeCache_lock, Mutex::_no_safepoint_check_flag);
+      marked = ctxk->mark_dependent_nmethods(changes);
+    }
+    java_lang_invoke_CallSite::set_context_volatile(call_site(), NULL); // Reset call site to initial state
+    if (marked > 0) {
+      // At least one nmethod has been marked for deoptimization
+      VM_Deoptimize op;
+      VMThread::execute(&op);
+    }
+  }
+}
+JVM_END
+
 /**
  * Throws a java/lang/UnsupportedOperationException unconditionally.
  * This is required by the specification of MethodHandle.invoke if
@@ -1306,6 +1351,7 @@
   {CC"objectFieldOffset",         CC"("MEM")J",                          FN_PTR(MHN_objectFieldOffset)},
   {CC"setCallSiteTargetNormal",   CC"("CS""MH")V",                       FN_PTR(MHN_setCallSiteTargetNormal)},
   {CC"setCallSiteTargetVolatile", CC"("CS""MH")V",                       FN_PTR(MHN_setCallSiteTargetVolatile)},
+  {CC"invalidateDependentNMethods", CC"("CS")V",                         FN_PTR(MHN_invalidateDependentNMethods)},
   {CC"staticFieldOffset",         CC"("MEM")J",                          FN_PTR(MHN_staticFieldOffset)},
   {CC"staticFieldBase",           CC"("MEM")"OBJ,                        FN_PTR(MHN_staticFieldBase)},
   {CC"getMemberVMInfo",           CC"("MEM")"OBJ,                        FN_PTR(MHN_getMemberVMInfo)}
--- a/hotspot/src/share/vm/prims/methodHandles.hpp	Wed Apr 15 11:36:42 2015 +0200
+++ b/hotspot/src/share/vm/prims/methodHandles.hpp	Fri Apr 17 16:45:55 2015 +0000
@@ -68,6 +68,9 @@
   // bit values for suppress argument to expand_MemberName:
   enum { _suppress_defc = 1, _suppress_name = 2, _suppress_type = 4 };
 
+  // CallSite support
+  static InstanceKlass* get_call_site_context(oop call_site);
+
   // Generate MethodHandles adapters.
   static bool generate_adapters();
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/jsr292/CallSiteDepContextTest.java	Fri Apr 17 16:45:55 2015 +0000
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2015, 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.
+ */
+
+/**
+ * @test
+ * @bug 8057967
+ * @run main/bootclasspath -Xbatch java.lang.invoke.CallSiteDepContextTest
+ */
+package java.lang.invoke;
+
+import java.lang.ref.*;
+import jdk.internal.org.objectweb.asm.*;
+import sun.misc.Unsafe;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+
+public class CallSiteDepContextTest {
+    static final Unsafe               UNSAFE = Unsafe.getUnsafe();
+    static final MethodHandles.Lookup LOOKUP = MethodHandles.Lookup.IMPL_LOOKUP;
+    static final String           CLASS_NAME = "java/lang/invoke/Test";
+    static final String          METHOD_NAME = "m";
+    static final MethodType             TYPE = MethodType.methodType(int.class);
+
+    static MutableCallSite mcs;
+    static MethodHandle bsmMH;
+
+    static {
+        try {
+            bsmMH = LOOKUP.findStatic(
+                    CallSiteDepContextTest.class, "bootstrap",
+                    MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class));
+        } catch(Throwable e) {
+            throw new InternalError(e);
+        }
+    }
+
+    public static CallSite bootstrap(MethodHandles.Lookup caller,
+                                     String invokedName,
+                                     MethodType invokedType) {
+        return mcs;
+    }
+
+    static class T {
+        static int f1() { return 1; }
+        static int f2() { return 2; }
+    }
+
+    static byte[] getClassFile(String suffix) {
+        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+        MethodVisitor mv;
+        cw.visit(52, ACC_PUBLIC | ACC_SUPER, CLASS_NAME + suffix, null, "java/lang/Object", null);
+        {
+            mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, METHOD_NAME, TYPE.toMethodDescriptorString(), null, null);
+            mv.visitCode();
+            Handle bsm = new Handle(H_INVOKESTATIC,
+                    "java/lang/invoke/CallSiteDepContextTest", "bootstrap",
+                    bsmMH.type().toMethodDescriptorString());
+            mv.visitInvokeDynamicInsn("methodName", TYPE.toMethodDescriptorString(), bsm);
+            mv.visitInsn(IRETURN);
+            mv.visitMaxs(0, 0);
+            mv.visitEnd();
+        }
+        cw.visitEnd();
+        return cw.toByteArray();
+    }
+
+    private static void execute(int expected, MethodHandle... mhs) throws Throwable {
+        for (int i = 0; i < 20_000; i++) {
+            for (MethodHandle mh : mhs) {
+                int r = (int) mh.invokeExact();
+                if (r != expected) {
+                    throw new Error(r + " != " + expected);
+                }
+            }
+        }
+    }
+
+    public static void testSharedCallSite() throws Throwable {
+        Class<?> cls1 = UNSAFE.defineAnonymousClass(Object.class, getClassFile("CS_1"), null);
+        Class<?> cls2 = UNSAFE.defineAnonymousClass(Object.class, getClassFile("CS_2"), null);
+
+        MethodHandle[] mhs = new MethodHandle[] {
+            LOOKUP.findStatic(cls1, METHOD_NAME, TYPE),
+            LOOKUP.findStatic(cls2, METHOD_NAME, TYPE)
+        };
+
+        mcs = new MutableCallSite(LOOKUP.findStatic(T.class, "f1", TYPE));
+        execute(1, mhs);
+        mcs.setTarget(LOOKUP.findStatic(T.class, "f2", TYPE));
+        execute(2, mhs);
+    }
+
+    public static void testNonBoundCallSite() throws Throwable {
+        mcs = new MutableCallSite(LOOKUP.findStatic(T.class, "f1", TYPE));
+
+        // mcs.context == null
+        MethodHandle mh = mcs.dynamicInvoker();
+        execute(1, mh);
+
+        // mcs.context == cls1
+        Class<?> cls1 = UNSAFE.defineAnonymousClass(Object.class, getClassFile("NonBound_1"), null);
+        MethodHandle mh1 = LOOKUP.findStatic(cls1, METHOD_NAME, TYPE);
+
+        execute(1, mh1);
+
+        mcs.setTarget(LOOKUP.findStatic(T.class, "f2", TYPE));
+
+        execute(2, mh, mh1);
+    }
+
+    static ReferenceQueue rq = new ReferenceQueue();
+    static PhantomReference ref;
+
+    public static void testGC() throws Throwable {
+        mcs = new MutableCallSite(LOOKUP.findStatic(T.class, "f1", TYPE));
+
+        Class<?>[] cls = new Class[] {
+                UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_1"), null),
+                UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_2"), null),
+        };
+
+        MethodHandle[] mhs = new MethodHandle[] {
+                LOOKUP.findStatic(cls[0], METHOD_NAME, TYPE),
+                LOOKUP.findStatic(cls[1], METHOD_NAME, TYPE),
+        };
+
+        // mcs.context == cls[0]
+        int r = (int) mhs[0].invokeExact();
+
+        execute(1, mhs);
+
+        ref = new PhantomReference<>(cls[0], rq);
+        cls[0] = UNSAFE.defineAnonymousClass(Object.class, getClassFile("GC_3"), null);
+        mhs[0] = LOOKUP.findStatic(cls[0], METHOD_NAME, TYPE);
+
+        do {
+            System.gc();
+            try {
+                Reference ref1 = rq.remove(1000);
+                if (ref1 == ref) {
+                    ref1.clear();
+                    System.gc(); // Ensure that the stale context is cleared
+                    break;
+                }
+            } catch(InterruptedException e) { /* ignore */ }
+        } while (true);
+
+        execute(1, mhs);
+        mcs.setTarget(LOOKUP.findStatic(T.class, "f2", TYPE));
+        execute(2, mhs);
+    }
+
+    public static void main(String[] args) throws Throwable {
+        testSharedCallSite();
+        testNonBoundCallSite();
+        testGC();
+        System.out.println("TEST PASSED");
+    }
+}