Merge "Don't use $PPID"
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 17b94d3..ac5c6fb 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -77,7 +77,9 @@
     std::vector<uint8_t> elf_file = debug::WriteDebugElfFileForClasses(
         kRuntimeISA, jit_compiler->GetCompilerDriver()->GetInstructionSetFeatures(), types_array);
     MutexLock mu(Thread::Current(), *Locks::native_debug_interface_lock_);
-    CreateJITCodeEntry(std::move(elf_file));
+    // We never free debug info for types, so we don't need to provide a handle
+    // (which would have been otherwise used as identifier to remove it later).
+    AddNativeDebugInfoForJit(nullptr /* handle */, elf_file);
   }
 }
 
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index b3f23a0..e42dfc1 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -1411,13 +1411,12 @@
       mini_debug_info,
       ArrayRef<const debug::MethodDebugInfo>(&info, 1));
   MutexLock mu(Thread::Current(), *Locks::native_debug_interface_lock_);
-  JITCodeEntry* entry = CreateJITCodeEntry(elf_file);
-  IncrementJITCodeEntryRefcount(entry, info.code_address);
+  AddNativeDebugInfoForJit(reinterpret_cast<const void*>(info.code_address), elf_file);
 
   VLOG(jit)
       << "JIT mini-debug-info added for " << ArtMethod::PrettyMethod(method)
       << " size=" << PrettySize(elf_file.size())
-      << " total_size=" << PrettySize(GetJITCodeEntryMemUsage());
+      << " total_size=" << PrettySize(GetJitNativeDebugInfoMemUsage());
 }
 
 }  // namespace art
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 27025d1..46e0ee4 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -18,7 +18,10 @@
 // we use gold as the linker (arm, x86, x86_64). The symbol is used by the debuggers to detect when
 // new jit code is generated. We don't want it to be called when a different function with the same
 // (empty) body is called.
-JIT_DEBUG_REGISTER_CODE_LDFLAGS = ["-Wl,--keep-unique,__jit_debug_register_code"]
+JIT_DEBUG_REGISTER_CODE_LDFLAGS = [
+    "-Wl,--keep-unique,__jit_debug_register_code",
+    "-Wl,--keep-unique,__dex_debug_register_code"
+]
 
 cc_defaults {
     name: "libart_defaults",
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index a4c32dd..8a24daa 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -1218,6 +1218,10 @@
     DCHECK(jni_function_table_lock_ == nullptr);
     jni_function_table_lock_ = new Mutex("JNI function table lock", current_lock_level);
 
+    UPDATE_CURRENT_LOCK_LEVEL(kNativeDebugInterfaceLock);
+    DCHECK(native_debug_interface_lock_ == nullptr);
+    native_debug_interface_lock_ = new Mutex("Native debug interface lock", current_lock_level);
+
     UPDATE_CURRENT_LOCK_LEVEL(kAbortLock);
     DCHECK(abort_lock_ == nullptr);
     abort_lock_ = new Mutex("abort lock", current_lock_level, true);
@@ -1230,10 +1234,6 @@
     DCHECK(unexpected_signal_lock_ == nullptr);
     unexpected_signal_lock_ = new Mutex("unexpected signal lock", current_lock_level, true);
 
-    UPDATE_CURRENT_LOCK_LEVEL(kNativeDebugInterfaceLock);
-    DCHECK(native_debug_interface_lock_ == nullptr);
-    native_debug_interface_lock_ = new Mutex("Native debug interface lock", current_lock_level);
-
     UPDATE_CURRENT_LOCK_LEVEL(kLoggingLock);
     DCHECK(logging_lock_ == nullptr);
     logging_lock_ = new Mutex("logging lock", current_lock_level, true);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index bf27b7f..4f7001a 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -58,11 +58,11 @@
 // [1] http://www.drdobbs.com/parallel/use-lock-hierarchies-to-avoid-deadlock/204801163
 enum LockLevel {
   kLoggingLock = 0,
-  kNativeDebugInterfaceLock,
   kSwapMutexesLock,
   kUnexpectedSignalLock,
   kThreadSuspendCountLock,
   kAbortLock,
+  kNativeDebugInterfaceLock,
   kSignalHandlingLock,
   kJdwpAdbStateLock,
   kJdwpSocketLock,
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 9c0ccee..f18e0b4 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -267,7 +267,9 @@
   // cannot in general be guaranteed, but in all likelihood leads to breakage down the line.
   if (klass->GetClassLoader() == nullptr && !Runtime::Current()->IsAotCompiler()) {
     std::string tmp;
-    LOG(kIsDebugBuild ? FATAL : WARNING) << klass->GetDescriptor(&tmp) << " failed initialization";
+    LOG(kIsDebugBuild ? FATAL : WARNING) << klass->GetDescriptor(&tmp)
+                                         << " failed initialization: "
+                                         << self->GetException()->Dump();
   }
 
   env->ExceptionClear();
@@ -3434,7 +3436,8 @@
   data.weak_root = dex_cache_jweak;
   data.dex_file = dex_cache->GetDexFile();
   data.class_table = ClassTableForClassLoader(class_loader);
-  RegisterDexFileForNative(self, data.dex_file->Begin());
+  AddNativeDebugInfoForDex(self, ArrayRef<const uint8_t>(data.dex_file->Begin(),
+                                                         data.dex_file->Size()));
   DCHECK(data.class_table != nullptr);
   // Make sure to hold the dex cache live in the class table. This case happens for the boot class
   // path dex caches without an image.
@@ -7958,17 +7961,18 @@
     // In case of jmvti, the dex file gets verified before being registered, so first
     // check if it's registered before checking class tables.
     const DexFile& dex_file = *dex_cache->GetDexFile();
-    CHECK(!IsDexFileRegistered(Thread::Current(), dex_file) ||
-          FindClassTable(Thread::Current(), dex_cache) == ClassTableForClassLoader(class_loader))
+    DCHECK(!IsDexFileRegistered(Thread::Current(), dex_file) ||
+           FindClassTable(Thread::Current(), dex_cache) == ClassTableForClassLoader(class_loader))
         << "DexFile referrer: " << dex_file.GetLocation()
         << " ClassLoader: " << DescribeLoaders(class_loader, "");
     // Be a good citizen and update the dex cache to speed subsequent calls.
     dex_cache->SetResolvedMethod(method_idx, resolved, image_pointer_size_);
-    const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx);
-    DCHECK(LookupResolvedType(method_id.class_idx_, dex_cache, class_loader) != nullptr)
-        << "Method: " << resolved->PrettyMethod() << ", "
-        << "Class: " << klass->PrettyClass() << " (" << klass->GetStatus() << "), "
-        << "DexFile referrer: " << dex_file.GetLocation();
+    // Disable the following invariant check as the verifier breaks it. b/73760543
+    // const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx);
+    // DCHECK(LookupResolvedType(method_id.class_idx_, dex_cache, class_loader) != nullptr)
+    //    << "Method: " << resolved->PrettyMethod() << ", "
+    //    << "Class: " << klass->PrettyClass() << " (" << klass->GetStatus() << "), "
+    //    << "DexFile referrer: " << dex_file.GetLocation();
   }
   return resolved;
 }
@@ -8000,13 +8004,9 @@
     DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex();
     klass = LookupResolvedType(method_id.class_idx_, dex_cache.Get(), class_loader.Get());
     if (UNLIKELY(klass == nullptr)) {
-      const char* descriptor = dex_file.StringByTypeIdx(method_id.class_idx_);
-      LOG(FATAL) << "Check failed: klass != nullptr Bug: 64759619 Method: "
-          << resolved->PrettyMethod() << ";" << resolved
-          << "/0x" << std::hex << resolved->GetAccessFlags()
-          << " ReferencedClass: " << descriptor
-          << " DexFile referrer: " << dex_file.GetLocation()
-          << " ClassLoader: " << DescribeLoaders(class_loader.Get(), descriptor);
+      // We normaly should not end up here. However the verifier currently doesn't guarantee
+      // the invariant of having the klass in the class table. b/73760543
+      klass = ResolveType(method_id.class_idx_, dex_cache, class_loader);
     }
   } else {
     // The method was not in the DexCache, resolve the declaring class.
diff --git a/runtime/elf_file_impl.h b/runtime/elf_file_impl.h
index 04c2243..3143df5 100644
--- a/runtime/elf_file_impl.h
+++ b/runtime/elf_file_impl.h
@@ -28,10 +28,6 @@
 
 namespace art {
 
-extern "C" {
-  struct JITCodeEntry;
-}
-
 template <typename ElfTypes>
 class ElfFileImpl {
  public:
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index a8ab626..380a981 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -1073,7 +1073,7 @@
   Handle<mirror::Object> object(hs.NewHandle(result.GetL()));
   if (UNLIKELY(object.IsNull())) {
     // This will typically be for LambdaMetafactory which is not supported.
-    ThrowNullPointerException("Bootstrap method returned null");
+    ThrowClassCastException("Bootstrap method returned null");
     return nullptr;
   }
 
@@ -1085,11 +1085,10 @@
 
   Handle<mirror::CallSite> call_site =
       hs.NewHandle(ObjPtr<mirror::CallSite>::DownCast(ObjPtr<mirror::Object>(result.GetL())));
-
   // Check the call site target is not null as we're going to invoke it.
   Handle<mirror::MethodHandle> target = hs.NewHandle(call_site->GetTarget());
   if (UNLIKELY(target.IsNull())) {
-    ThrowNullPointerException("Target for call-site is null");
+    ThrowClassCastException("Bootstrap method did not return a callsite");
     return nullptr;
   }
 
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 6f9cad8..d8f858e 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -83,10 +83,14 @@
 #define BRANCH_INSTRUMENTATION(offset)                                                         \
   do {                                                                                         \
     if (UNLIKELY(instrumentation->HasBranchListeners())) {                                     \
-      instrumentation->Branch(self, method, dex_pc, offset);                                   \
+      instrumentation->Branch(self, shadow_frame.GetMethod(), dex_pc, offset);                 \
     }                                                                                          \
     JValue result;                                                                             \
-    if (jit::Jit::MaybeDoOnStackReplacement(self, method, dex_pc, offset, &result)) {          \
+    if (jit::Jit::MaybeDoOnStackReplacement(self,                                              \
+                                            shadow_frame.GetMethod(),                          \
+                                            dex_pc,                                            \
+                                            offset,                                            \
+                                            &result)) {                                        \
       if (interpret_one_instruction) {                                                         \
         /* OSR has completed execution of the method.  Signal mterp to return to caller */     \
         shadow_frame.SetDexPC(dex::kDexNoIndex);                                               \
@@ -98,7 +102,7 @@
 #define HOTNESS_UPDATE()                                                                       \
   do {                                                                                         \
     if (jit != nullptr) {                                                                      \
-      jit->AddSamples(self, method, 1, /*with_backedges*/ true);                               \
+      jit->AddSamples(self, shadow_frame.GetMethod(), 1, /*with_backedges*/ true);             \
     }                                                                                          \
   } while (false)
 
@@ -200,7 +204,6 @@
 
   uint32_t dex_pc = shadow_frame.GetDexPC();
   const auto* const instrumentation = Runtime::Current()->GetInstrumentation();
-  ArtMethod* method = shadow_frame.GetMethod();
   const uint16_t* const insns = accessor.Insns();
   const Instruction* inst = Instruction::At(insns + dex_pc);
   uint16_t inst_data;
@@ -390,7 +393,7 @@
         const size_t ref_idx = inst->VRegA_11x(inst_data);
         ObjPtr<mirror::Object> obj_result = shadow_frame.GetVRegReference(ref_idx);
         if (do_assignability_check && obj_result != nullptr) {
-          ObjPtr<mirror::Class> return_type = method->ResolveReturnType();
+          ObjPtr<mirror::Class> return_type = shadow_frame.GetMethod()->ResolveReturnType();
           // Re-load since it might have moved.
           obj_result = shadow_frame.GetVRegReference(ref_idx);
           if (return_type == nullptr) {
@@ -535,7 +538,9 @@
       case Instruction::CONST_METHOD_HANDLE: {
         PREAMBLE();
         ClassLinker* cl = Runtime::Current()->GetClassLinker();
-        ObjPtr<mirror::MethodHandle> mh = cl->ResolveMethodHandle(self, inst->VRegB_21c(), method);
+        ObjPtr<mirror::MethodHandle> mh = cl->ResolveMethodHandle(self,
+                                                                  inst->VRegB_21c(),
+                                                                  shadow_frame.GetMethod());
         if (UNLIKELY(mh == nullptr)) {
           HANDLE_PENDING_EXCEPTION();
         } else {
@@ -547,7 +552,9 @@
       case Instruction::CONST_METHOD_TYPE: {
         PREAMBLE();
         ClassLinker* cl = Runtime::Current()->GetClassLinker();
-        ObjPtr<mirror::MethodType> mt = cl->ResolveMethodType(self, inst->VRegB_21c(), method);
+        ObjPtr<mirror::MethodType> mt = cl->ResolveMethodType(self,
+                                                              inst->VRegB_21c(),
+                                                              shadow_frame.GetMethod());
         if (UNLIKELY(mt == nullptr)) {
           HANDLE_PENDING_EXCEPTION();
         } else {
diff --git a/runtime/jit/debugger_interface.cc b/runtime/jit/debugger_interface.cc
index d60f70a..505e626 100644
--- a/runtime/jit/debugger_interface.cc
+++ b/runtime/jit/debugger_interface.cc
@@ -18,18 +18,35 @@
 
 #include <android-base/logging.h>
 
+#include "base/array_ref.h"
 #include "base/mutex.h"
+#include "base/time_utils.h"
 #include "thread-current-inl.h"
 #include "thread.h"
 
 #include <unordered_map>
 
-namespace art {
+//
+// Debug interface for native tools (gdb, lldb, libunwind, simpleperf).
+//
+// See http://sourceware.org/gdb/onlinedocs/gdb/Declarations.html
+//
+// There are two ways for native tools to access the debug data safely:
+//
+// 1) Synchronously, by setting a breakpoint in the __*_debug_register_code
+//    method, which is called after every modification of the linked list.
+//    GDB does this, but it is complex to set up and it stops the process.
+//
+// 2) Asynchronously, by monitoring the action_counter_, which is incremented
+//    on every modification of the linked list and kept at -1 during updates.
+//    Therefore, if the tool process reads the counter both before and after
+//    iterating over the linked list, and the counters match and are not -1,
+//    the tool process can be sure the list was not modified during the read.
+//    Obviously, it can also cache the data and use the counter to determine
+//    if the cache is up to date, or to intelligently update it if needed.
+//
 
-// -------------------------------------------------------------------
-// Binary GDB JIT Interface as described in
-//   http://sourceware.org/gdb/onlinedocs/gdb/Declarations.html
-// -------------------------------------------------------------------
+namespace art {
 extern "C" {
   typedef enum {
     JIT_NOACTION = 0,
@@ -40,168 +57,193 @@
   struct JITCodeEntry {
     JITCodeEntry* next_;
     JITCodeEntry* prev_;
-    const uint8_t *symfile_addr_;
-    uint64_t symfile_size_;
-    uint32_t ref_count;  // ART internal field.
+    const uint8_t* symfile_addr_;
+    uint64_t symfile_size_;  // Beware of the offset (12 on x86; but 16 on ARM32).
+
+    // Android-specific fields:
+    uint64_t register_timestamp_;  // CLOCK_MONOTONIC time of entry registration.
   };
 
   struct JITDescriptor {
-    uint32_t version_;
-    uint32_t action_flag_;
-    JITCodeEntry* relevant_entry_;
-    JITCodeEntry* first_entry_;
+    uint32_t version_ = 1;                    // NB: GDB supports only version 1.
+    uint32_t action_flag_ = JIT_NOACTION;     // One of the JITAction enum values.
+    JITCodeEntry* relevant_entry_ = nullptr;  // The entry affected by the action.
+    JITCodeEntry* first_entry_ = nullptr;     // Head of link list of all entries.
+
+    // Android-specific fields:
+    uint8_t magic_[8] = {'A', 'n', 'd', 'r', 'o', 'i', 'd', '1'};
+    uint32_t flags_ = 0;                   // Reserved for future use. Must be 0.
+    uint32_t sizeof_descriptor = sizeof(JITDescriptor);
+    uint32_t sizeof_entry = sizeof(JITCodeEntry);
+    std::atomic_int32_t action_counter_;   // Number of actions, or -1 if locked.
+                                           // It can overflow from INT32_MAX to 0.
+    uint64_t action_timestamp_ = 1;        // CLOCK_MONOTONIC time of last action.
   };
 
-  // GDB will place breakpoint into this function.
-  // To prevent GCC from inlining or removing it we place noinline attribute
-  // and inline assembler statement inside.
-  void __attribute__((noinline)) __jit_debug_register_code();
+  // Check that std::atomic_int32_t has the same layout as int32_t.
+  static_assert(alignof(std::atomic_int32_t) == alignof(int32_t), "Weird alignment");
+  static_assert(sizeof(std::atomic_int32_t) == sizeof(int32_t), "Weird size");
+
+  // GDB may set breakpoint here. We must ensure it is not removed or deduplicated.
   void __attribute__((noinline)) __jit_debug_register_code() {
     __asm__("");
   }
 
-  // Call __jit_debug_register_code indirectly via global variable.
-  // This gives the debugger an easy way to inject custom code to handle the events.
+  // Alternatively, native tools may overwrite this field to execute custom handler.
   void (*__jit_debug_register_code_ptr)() = __jit_debug_register_code;
 
-  // GDB will inspect contents of this descriptor.
-  // Static initialization is necessary to prevent GDB from seeing
-  // uninitialized descriptor.
-  JITDescriptor __jit_debug_descriptor = { 1, JIT_NOACTION, nullptr, nullptr };
+  // The root data structure describing of all JITed methods.
+  JITDescriptor __jit_debug_descriptor {};
 
-  // Incremented whenever __jit_debug_descriptor is modified.
-  uint32_t __jit_debug_descriptor_timestamp = 0;
-
-  struct DEXFileEntry {
-    DEXFileEntry* next_;
-    DEXFileEntry* prev_;
-    const void* dexfile_;
-  };
-
-  DEXFileEntry* __art_debug_dexfiles = nullptr;
-
-  // Incremented whenever __art_debug_dexfiles is modified.
-  uint32_t __art_debug_dexfiles_timestamp = 0;
-}
-
-static size_t g_jit_debug_mem_usage
-    GUARDED_BY(Locks::native_debug_interface_lock_) = 0;
-
-static std::unordered_map<const void*, DEXFileEntry*> g_dexfile_entries
-    GUARDED_BY(Locks::native_debug_interface_lock_);
-
-void RegisterDexFileForNative(Thread* current_thread, const void* dexfile_header) {
-  MutexLock mu(current_thread, *Locks::native_debug_interface_lock_);
-  if (g_dexfile_entries.count(dexfile_header) == 0) {
-    DEXFileEntry* entry = new DEXFileEntry();
-    CHECK(entry != nullptr);
-    entry->dexfile_ = dexfile_header;
-    entry->prev_ = nullptr;
-    entry->next_ = __art_debug_dexfiles;
-    if (entry->next_ != nullptr) {
-      entry->next_->prev_ = entry;
-    }
-    __art_debug_dexfiles = entry;
-    __art_debug_dexfiles_timestamp++;
-    g_dexfile_entries.emplace(dexfile_header, entry);
+  // The following globals mirror the ones above, but are used to register dex files.
+  void __attribute__((noinline)) __dex_debug_register_code() {
+    __asm__("");
   }
+  void (*__dex_debug_register_code_ptr)() = __dex_debug_register_code;
+  JITDescriptor __dex_debug_descriptor {};
 }
 
-void DeregisterDexFileForNative(Thread* current_thread, const void* dexfile_header) {
-  MutexLock mu(current_thread, *Locks::native_debug_interface_lock_);
-  auto it = g_dexfile_entries.find(dexfile_header);
-  // We register dex files in the class linker and free them in DexFile_closeDexFile,
-  // but might be cases where we load the dex file without using it in the class linker.
-  if (it != g_dexfile_entries.end()) {
-    DEXFileEntry* entry = it->second;
-    if (entry->prev_ != nullptr) {
-      entry->prev_->next_ = entry->next_;
-    } else {
-      __art_debug_dexfiles = entry->next_;
-    }
-    if (entry->next_ != nullptr) {
-      entry->next_->prev_ = entry->prev_;
-    }
-    __art_debug_dexfiles_timestamp++;
-    delete entry;
-    g_dexfile_entries.erase(it);
-  }
+// Mark the descriptor as "locked", so native tools know the data is unstable.
+// Returns the old value of the counter.
+static int32_t LockActionCounter(JITDescriptor& descriptor) {
+  return descriptor.action_counter_.exchange(-1);
 }
 
-JITCodeEntry* CreateJITCodeEntry(const std::vector<uint8_t>& symfile) {
-  DCHECK_NE(symfile.size(), 0u);
+// Mark the descriptor as "unlocked", so native tools know the data is safe to read.
+// It will also increment the value so that the tools know the data has changed.
+static void UnlockActionCounter(JITDescriptor& descriptor, int32_t old_value) {
+  int32_t new_value = (old_value + 1) & 0x7FFFFFFF;  // Handle overflow to avoid -1.
+  descriptor.action_counter_.store(new_value);
+}
 
-  // Make a copy of the buffer. We want to shrink it anyway.
-  uint8_t* symfile_copy = new uint8_t[symfile.size()];
-  CHECK(symfile_copy != nullptr);
-  memcpy(symfile_copy, symfile.data(), symfile.size());
+static JITCodeEntry* CreateJITCodeEntryInternal(
+    JITDescriptor& descriptor,
+    void (*register_code_ptr)(),
+    const ArrayRef<const uint8_t>& symfile)
+    REQUIRES(Locks::native_debug_interface_lock_) {
+  int32_t old_action_counter = LockActionCounter(descriptor);
 
   JITCodeEntry* entry = new JITCodeEntry;
   CHECK(entry != nullptr);
-  entry->symfile_addr_ = symfile_copy;
+  entry->symfile_addr_ = symfile.data();
   entry->symfile_size_ = symfile.size();
   entry->prev_ = nullptr;
-  entry->ref_count = 0;
-  entry->next_ = __jit_debug_descriptor.first_entry_;
+  entry->next_ = descriptor.first_entry_;
+  entry->register_timestamp_ = NanoTime();
   if (entry->next_ != nullptr) {
     entry->next_->prev_ = entry;
   }
-  g_jit_debug_mem_usage += sizeof(JITCodeEntry) + entry->symfile_size_;
-  __jit_debug_descriptor.first_entry_ = entry;
-  __jit_debug_descriptor.relevant_entry_ = entry;
-  __jit_debug_descriptor.action_flag_ = JIT_REGISTER_FN;
-  __jit_debug_descriptor_timestamp++;
-  (*__jit_debug_register_code_ptr)();
+  descriptor.first_entry_ = entry;
+  descriptor.relevant_entry_ = entry;
+  descriptor.action_flag_ = JIT_REGISTER_FN;
+  descriptor.action_timestamp_ = entry->register_timestamp_;
+  UnlockActionCounter(descriptor, old_action_counter);
+
+  (*register_code_ptr)();
   return entry;
 }
 
-void DeleteJITCodeEntry(JITCodeEntry* entry) {
+static void DeleteJITCodeEntryInternal(
+    JITDescriptor& descriptor,
+    void (*register_code_ptr)(),
+    JITCodeEntry* entry)
+    REQUIRES(Locks::native_debug_interface_lock_) {
+  CHECK(entry != nullptr);
+  int32_t old_action_counter = LockActionCounter(descriptor);
+
   if (entry->prev_ != nullptr) {
     entry->prev_->next_ = entry->next_;
   } else {
-    __jit_debug_descriptor.first_entry_ = entry->next_;
+    descriptor.first_entry_ = entry->next_;
   }
-
   if (entry->next_ != nullptr) {
     entry->next_->prev_ = entry->prev_;
   }
 
-  g_jit_debug_mem_usage -= sizeof(JITCodeEntry) + entry->symfile_size_;
-  __jit_debug_descriptor.relevant_entry_ = entry;
-  __jit_debug_descriptor.action_flag_ = JIT_UNREGISTER_FN;
-  __jit_debug_descriptor_timestamp++;
-  (*__jit_debug_register_code_ptr)();
-  delete[] entry->symfile_addr_;
+  descriptor.relevant_entry_ = entry;
+  descriptor.action_flag_ = JIT_UNREGISTER_FN;
+  descriptor.action_timestamp_ = NanoTime();
+  UnlockActionCounter(descriptor, old_action_counter);
+
+  (*register_code_ptr)();
   delete entry;
 }
 
-// Mapping from code address to entry. Used to manage life-time of the entries.
-static std::unordered_map<uintptr_t, JITCodeEntry*> g_jit_code_entries
+static std::unordered_map<const void*, JITCodeEntry*> __dex_debug_entries
     GUARDED_BY(Locks::native_debug_interface_lock_);
 
-void IncrementJITCodeEntryRefcount(JITCodeEntry* entry, uintptr_t code_address) {
-  DCHECK(entry != nullptr);
-  DCHECK_EQ(g_jit_code_entries.count(code_address), 0u);
-  entry->ref_count++;
-  g_jit_code_entries.emplace(code_address, entry);
-}
-
-void DecrementJITCodeEntryRefcount(JITCodeEntry* entry, uintptr_t code_address) {
-  DCHECK(entry != nullptr);
-  DCHECK(g_jit_code_entries[code_address] == entry);
-  if (--entry->ref_count == 0) {
-    DeleteJITCodeEntry(entry);
+void AddNativeDebugInfoForDex(Thread* current_thread, ArrayRef<const uint8_t> dexfile) {
+  MutexLock mu(current_thread, *Locks::native_debug_interface_lock_);
+  DCHECK(dexfile.data() != nullptr);
+  // This is just defensive check. The class linker should not register the dex file twice.
+  if (__dex_debug_entries.count(dexfile.data()) == 0) {
+    JITCodeEntry* entry = CreateJITCodeEntryInternal(__dex_debug_descriptor,
+                                                     __dex_debug_register_code_ptr,
+                                                     dexfile);
+    __dex_debug_entries.emplace(dexfile.data(), entry);
   }
-  g_jit_code_entries.erase(code_address);
 }
 
-JITCodeEntry* GetJITCodeEntry(uintptr_t code_address) {
-  auto it = g_jit_code_entries.find(code_address);
-  return it == g_jit_code_entries.end() ? nullptr : it->second;
+void RemoveNativeDebugInfoForDex(Thread* current_thread, ArrayRef<const uint8_t> dexfile) {
+  MutexLock mu(current_thread, *Locks::native_debug_interface_lock_);
+  auto it = __dex_debug_entries.find(dexfile.data());
+  // We register dex files in the class linker and free them in DexFile_closeDexFile, but
+  // there might be cases where we load the dex file without using it in the class linker.
+  if (it != __dex_debug_entries.end()) {
+    DeleteJITCodeEntryInternal(__dex_debug_descriptor,
+                               __dex_debug_register_code_ptr,
+                               it->second);
+    __dex_debug_entries.erase(it);
+  }
 }
 
-size_t GetJITCodeEntryMemUsage() {
-  return g_jit_debug_mem_usage + g_jit_code_entries.size() * 2 * sizeof(void*);
+static size_t __jit_debug_mem_usage
+    GUARDED_BY(Locks::native_debug_interface_lock_) = 0;
+
+// Mapping from handle to entry. Used to manage life-time of the entries.
+static std::unordered_map<const void*, JITCodeEntry*> __jit_debug_entries
+    GUARDED_BY(Locks::native_debug_interface_lock_);
+
+void AddNativeDebugInfoForJit(const void* handle, const std::vector<uint8_t>& symfile) {
+  DCHECK_NE(symfile.size(), 0u);
+
+  // Make a copy of the buffer to shrink it and to pass ownership to JITCodeEntry.
+  uint8_t* copy = new uint8_t[symfile.size()];
+  CHECK(copy != nullptr);
+  memcpy(copy, symfile.data(), symfile.size());
+
+  JITCodeEntry* entry = CreateJITCodeEntryInternal(
+      __jit_debug_descriptor,
+      __jit_debug_register_code_ptr,
+      ArrayRef<const uint8_t>(copy, symfile.size()));
+  __jit_debug_mem_usage += sizeof(JITCodeEntry) + entry->symfile_size_;
+
+  // We don't provide handle for type debug info, which means we cannot free it later.
+  // (this only happens when --generate-debug-info flag is enabled for the purpose
+  // of being debugged with gdb; it does not happen for debuggable apps by default).
+  bool ok = handle == nullptr || __jit_debug_entries.emplace(handle, entry).second;
+  DCHECK(ok) << "Native debug entry already exists for " << std::hex << handle;
+}
+
+void RemoveNativeDebugInfoForJit(const void* handle) {
+  auto it = __jit_debug_entries.find(handle);
+  // We generate JIT native debug info only if the right runtime flags are enabled,
+  // but we try to remove it unconditionally whenever code is freed from JIT cache.
+  if (it != __jit_debug_entries.end()) {
+    JITCodeEntry* entry = it->second;
+    const uint8_t* symfile_addr = entry->symfile_addr_;
+    uint64_t symfile_size = entry->symfile_size_;
+    DeleteJITCodeEntryInternal(__jit_debug_descriptor,
+                               __jit_debug_register_code_ptr,
+                               entry);
+    __jit_debug_entries.erase(it);
+    __jit_debug_mem_usage -= sizeof(JITCodeEntry) + symfile_size;
+    delete[] symfile_addr;
+  }
+}
+
+size_t GetJitNativeDebugInfoMemUsage() {
+  return __jit_debug_mem_usage + __jit_debug_entries.size() * 2 * sizeof(void*);
 }
 
 }  // namespace art
diff --git a/runtime/jit/debugger_interface.h b/runtime/jit/debugger_interface.h
index 8c4bb3f..3d25910 100644
--- a/runtime/jit/debugger_interface.h
+++ b/runtime/jit/debugger_interface.h
@@ -26,45 +26,26 @@
 
 namespace art {
 
-extern "C" {
-  struct JITCodeEntry;
-}
-
 // Notify native tools (e.g. libunwind) that DEX file has been opened.
-// The pointer needs to point the start of the dex data (not the DexFile* object).
-void RegisterDexFileForNative(Thread* current_thread, const void* dexfile_header);
+// It takes the lock itself. The parameter must point to dex data (not the DexFile* object).
+void AddNativeDebugInfoForDex(Thread* current_thread, ArrayRef<const uint8_t> dexfile);
 
 // Notify native tools (e.g. libunwind) that DEX file has been closed.
-// The pointer needs to point the start of the dex data (not the DexFile* object).
-void DeregisterDexFileForNative(Thread* current_thread, const void* dexfile_header);
+// It takes the lock itself. The parameter must point to dex data (not the DexFile* object).
+void RemoveNativeDebugInfoForDex(Thread* current_thread, ArrayRef<const uint8_t> dexfile);
 
-// Notify native debugger about new JITed code by passing in-memory ELF.
-// It takes ownership of the in-memory ELF file.
-JITCodeEntry* CreateJITCodeEntry(const std::vector<uint8_t>& symfile)
+// Notify native tools about new JITed code by passing in-memory ELF.
+// The handle is the object that is being described (needed to be able to remove the entry).
+// The method will make copy of the passed ELF file (to shrink it to the minimum size).
+void AddNativeDebugInfoForJit(const void* handle, const std::vector<uint8_t>& symfile)
     REQUIRES(Locks::native_debug_interface_lock_);
 
-// Notify native debugger that JITed code has been removed.
-// It also releases the associated in-memory ELF file.
-void DeleteJITCodeEntry(JITCodeEntry* entry)
-    REQUIRES(Locks::native_debug_interface_lock_);
-
-// Helper method to track life-time of JITCodeEntry.
-// It registers given code address as being described by the given entry.
-void IncrementJITCodeEntryRefcount(JITCodeEntry* entry, uintptr_t code_address)
-    REQUIRES(Locks::native_debug_interface_lock_);
-
-// Helper method to track life-time of JITCodeEntry.
-// It de-registers given code address as being described by the given entry.
-void DecrementJITCodeEntryRefcount(JITCodeEntry* entry, uintptr_t code_address)
-    REQUIRES(Locks::native_debug_interface_lock_);
-
-// Find the registered JITCodeEntry for given code address.
-// There can be only one entry per address at any given time.
-JITCodeEntry* GetJITCodeEntry(uintptr_t code_address)
+// Notify native debugger that JITed code has been removed and free the debug info.
+void RemoveNativeDebugInfoForJit(const void* handle)
     REQUIRES(Locks::native_debug_interface_lock_);
 
 // Returns approximate memory used by all JITCodeEntries.
-size_t GetJITCodeEntryMemUsage()
+size_t GetJitNativeDebugInfoMemUsage()
     REQUIRES(Locks::native_debug_interface_lock_);
 
 }  // namespace art
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 1baa613..6d99ad0 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -718,7 +718,9 @@
   Runtime* runtime = Runtime::Current();
   if (UNLIKELY(runtime->UseJitCompilation() && runtime->GetJit()->JitAtFirstUse())) {
     // The compiler requires a ProfilingInfo object.
-    ProfilingInfo::Create(thread, method, /* retry_allocation */ true);
+    ProfilingInfo::Create(thread,
+                          method->GetInterfaceMethodIfProxy(kRuntimePointerSize),
+                          /* retry_allocation */ true);
     JitCompileTask compile_task(method, JitCompileTask::kCompile);
     compile_task.Run(thread);
     return;
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index c8c13cb..68a3647 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -550,10 +550,7 @@
   // Notify native debugger that we are about to remove the code.
   // It does nothing if we are not using native debugger.
   MutexLock mu(Thread::Current(), *Locks::native_debug_interface_lock_);
-  JITCodeEntry* entry = GetJITCodeEntry(reinterpret_cast<uintptr_t>(code_ptr));
-  if (entry != nullptr) {
-    DecrementJITCodeEntryRefcount(entry, reinterpret_cast<uintptr_t>(code_ptr));
-  }
+  RemoveNativeDebugInfoForJit(code_ptr);
   if (OatQuickMethodHeader::FromCodePointer(code_ptr)->IsOptimized()) {
     FreeData(GetRootTable(code_ptr));
   }  // else this is a JNI stub without any data.
@@ -1828,7 +1825,7 @@
   MutexLock mu2(Thread::Current(), *Locks::native_debug_interface_lock_);
   os << "Current JIT code cache size: " << PrettySize(used_memory_for_code_) << "\n"
      << "Current JIT data cache size: " << PrettySize(used_memory_for_data_) << "\n"
-     << "Current JIT mini-debug-info size: " << PrettySize(GetJITCodeEntryMemUsage()) << "\n"
+     << "Current JIT mini-debug-info size: " << PrettySize(GetJitNativeDebugInfoMemUsage()) << "\n"
      << "Current JIT capacity: " << PrettySize(current_capacity_) << "\n"
      << "Current number of JIT JNI stub entries: " << jni_stubs_map_.size() << "\n"
      << "Current number of JIT code cache entries: " << method_code_map_.size() << "\n"
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index dd7b34a..5d18b6e 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -333,7 +333,8 @@
     int32_t i = kDexFileIndexStart;  // Oat file is at index 0.
     for (const DexFile* dex_file : dex_files) {
       if (dex_file != nullptr) {
-        DeregisterDexFileForNative(soa.Self(), dex_file->Begin());
+        RemoveNativeDebugInfoForDex(soa.Self(), ArrayRef<const uint8_t>(dex_file->Begin(),
+                                                                        dex_file->Size()));
         // Only delete the dex file if the dex cache is not found to prevent runtime crashes if there
         // are calls to DexFile.close while the ART DexFile is still in use.
         if (!class_linker->IsDexFileRegistered(soa.Self(), *dex_file)) {
diff --git a/test/004-ThreadStress/run b/test/004-ThreadStress/run
index 3851d73..8004036 100755
--- a/test/004-ThreadStress/run
+++ b/test/004-ThreadStress/run
@@ -16,7 +16,13 @@
 
 # Enable lock contention logging.
 ${RUN} --runtime-option -Xlockprofthreshold:10 "${@}"
+return_status1=$?
 
 # Run locks-only mode with stack-dump lock profiling. Reduce the number of total operations from
 # the default 1000 to 100.
-${RUN} --runtime-option -Xlockprofthreshold:10 --runtime-option -Xstackdumplockprofthreshold:20 "${@}" Main --locks-only -o 100
+${RUN} --runtime-option -Xlockprofthreshold:10 --runtime-option -Xstackdumplockprofthreshold:20 \
+  "${@}" Main --locks-only -o 100
+return_status2=$?
+
+# Make sure we don't silently ignore an early failure.
+(exit $return_status1) && (exit $return_status2)
diff --git a/test/018-stack-overflow/run b/test/018-stack-overflow/run
index 1a71a1a..7443bd7 100755
--- a/test/018-stack-overflow/run
+++ b/test/018-stack-overflow/run
@@ -17,7 +17,12 @@
 # Run normal. This will be the debug build.
 echo "libartd run."
 ${RUN} "${@}"
+return_status1=$?
 
 # Run non-debug.
 echo "libart run."
 ${RUN} "${@/#libartd.so/libart.so}"
+return_status2=$?
+
+# Make sure we don't silently ignore an early failure.
+(exit $return_status1) && (exit $return_status2)
diff --git a/test/116-nodex2oat/run b/test/116-nodex2oat/run
index 2cdb3f7..d7984ce 100755
--- a/test/116-nodex2oat/run
+++ b/test/116-nodex2oat/run
@@ -27,11 +27,17 @@
 # Make sure we can run without an oat file.
 echo "Run -Xnodex2oat"
 ${RUN} ${flags} --runtime-option -Xnodex2oat
+return_status1=$?
 
 # Make sure we can run with the oat file.
 echo "Run -Xdex2oat"
 ${RUN} ${flags} --runtime-option -Xdex2oat
+return_status2=$?
 
 # Make sure we can run with the default settings.
 echo "Run default"
 ${RUN} ${flags}
+return_status3=$?
+
+# Make sure we don't silently ignore an early failure.
+(exit $return_status1) && (exit $return_status2) && (exit $return_status3)
diff --git a/test/117-nopatchoat/run b/test/117-nopatchoat/run
index c634900..0627fe5 100755
--- a/test/117-nopatchoat/run
+++ b/test/117-nopatchoat/run
@@ -37,11 +37,17 @@
 # Make sure we can run without relocation
 echo "Run without dex2oat/patchoat"
 ${RUN} ${flags} --runtime-option -Xnodex2oat
+return_status1=$?
 
 # Make sure we can run with the oat file.
 echo "Run with dexoat/patchoat"
 ${RUN} ${flags} --runtime-option -Xdex2oat
+return_status2=$?
 
 # Make sure we can run with the default settings.
 echo "Run default"
 ${RUN} ${flags}
+return_status3=$?
+
+# Make sure we don't silently ignore an early failure.
+(exit $return_status1) && (exit $return_status2) && (exit $return_status3)
diff --git a/test/118-noimage-dex2oat/run b/test/118-noimage-dex2oat/run
index 07bdb08..e1e2577 100644
--- a/test/118-noimage-dex2oat/run
+++ b/test/118-noimage-dex2oat/run
@@ -48,15 +48,23 @@
 # Make sure we can run without an oat file.
 echo "Run -Xnoimage-dex2oat"
 ${RUN} ${flags} ${bpath_arg} --runtime-option -Xnoimage-dex2oat --runtime-option -Xnodex2oat
+return_status1=$?
 
 # Make sure we cannot run without an oat file without fallback.
 echo "Run -Xnoimage-dex2oat -Xno-dex-file-fallback"
-${RUN} ${flags} ${bpath_arg} --runtime-option -Xnoimage-dex2oat --runtime-option -Xnodex2oat --runtime-option -Xno-dex-file-fallback
+${RUN} ${flags} ${bpath_arg} --runtime-option -Xnoimage-dex2oat --runtime-option -Xnodex2oat \
+  --runtime-option -Xno-dex-file-fallback
+return_status2=$?
 
 # Make sure we can run with the oat file.
 echo "Run -Ximage-dex2oat"
 ${RUN} ${flags} ${bpath_arg} --runtime-option -Ximage-dex2oat
+return_status3=$?
 
 # Make sure we can run with the default settings.
 echo "Run default"
 ${RUN} ${flags} ${bpath_arg}
+return_status4=$?
+
+# Make sure we don't silently ignore an early failure.
+(exit $return_status1) && (exit $return_status2) && (exit $return_status3) && (exit $return_status4)
diff --git a/test/119-noimage-patchoat/run b/test/119-noimage-patchoat/run
index 02a64f8..497dc4a 100644
--- a/test/119-noimage-patchoat/run
+++ b/test/119-noimage-patchoat/run
@@ -31,16 +31,26 @@
 
 # Make sure we can run without an image file.
 echo "Run -Xnoimage-dex2oat -Xpatchoat:/system/bin/false"
-${RUN} ${flags} ${BPATH} --runtime-option -Xnoimage-dex2oat --runtime-option -Xpatchoat:${false_bin}
+${RUN} ${flags} ${BPATH} --runtime-option -Xnoimage-dex2oat \
+  --runtime-option -Xpatchoat:${false_bin}
+return_status1=$?
 
 # Make sure we cannot run without an image file without fallback.
 echo "Run -Xnoimage-dex2oat -Xpatchoat:/system/bin/false -Xno-dex-file-fallback"
-${RUN} ${flags} ${BPATH} --runtime-option -Xnoimage-dex2oat --runtime-option -Xpatchoat:${false_bin} --runtime-option -Xno-dex-file-fallback
+${RUN} ${flags} ${BPATH} --runtime-option -Xnoimage-dex2oat \
+  --runtime-option -Xpatchoat:${false_bin} --runtime-option -Xno-dex-file-fallback
+# This second run is expected to fail: invert the return status of the previous command.
+return_status2=$((! $?))
 
 # Make sure we can run with the image file.
 echo "Run -Ximage-dex2oat"
 ${RUN} ${flags} ${BPATH} --runtime-option -Ximage-dex2oat
+return_status3=$?
 
 # Make sure we can run with the default settings.
 echo "Run default"
 ${RUN} ${flags} ${BPATH}
+return_status4=$?
+
+# Make sure we don't silently ignore an early failure.
+(exit $return_status1) && (exit $return_status2) && (exit $return_status3) && (exit $return_status4)
diff --git a/test/126-miranda-multidex/run b/test/126-miranda-multidex/run
index 23c9935..abd63cb 100755
--- a/test/126-miranda-multidex/run
+++ b/test/126-miranda-multidex/run
@@ -15,7 +15,12 @@
 # limitations under the License.
 
 ${RUN} $@
+return_status1=$?
 
 # The problem was first exposed in a no-verify setting, as that changes the resolution path
 # taken. Make sure we also test in that environment.
 ${RUN} --no-verify ${@}
+return_status2=$?
+
+# Make sure we don't silently ignore an early failure.
+(exit $return_status1) && (exit $return_status2)
diff --git a/test/137-cfi/run b/test/137-cfi/run
index adea71a..9190b1c 100755
--- a/test/137-cfi/run
+++ b/test/137-cfi/run
@@ -21,6 +21,7 @@
 ${RUN} "$@" -Xcompiler-option --generate-debug-info \
   --runtime-option -Xjitthreshold:0 \
   --args --full-signatures --args --test-local --args --test-remote
+return_status1=$?
 
 # Test with minimal compressed debugging information.
 # Check only method names (parameters are omitted to save space).
@@ -28,3 +29,7 @@
 ${RUN} "$@" -Xcompiler-option --generate-mini-debug-info \
   --runtime-option -Xjitthreshold:0 \
   --args --test-remote
+return_status2=$?
+
+# Make sure we don't silently ignore an early failure.
+(exit $return_status1) && (exit $return_status2)
diff --git a/test/952-invoke-custom-lookup/build b/test/1948-obsolete-const-method-handle/build
similarity index 68%
rename from test/952-invoke-custom-lookup/build
rename to test/1948-obsolete-const-method-handle/build
index f3fe95c..ac0dcd9 100644
--- a/test/952-invoke-custom-lookup/build
+++ b/test/1948-obsolete-const-method-handle/build
@@ -14,14 +14,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# make us exit on a failure
+# Make us exit on a failure
 set -e
 
-# This test uses previously prepared dex and jar files. They need to
-# be re-packaged to match the files that the run-test-jar script
-# expects.
-if [[ $@ != *"--jvm"* ]]; then
-  zip ${TEST_NAME}.jar classes.dex
-else
-  unzip -d classes classes.jar
-fi
+mkdir classes
+./util-src/build-classes $PWD/classes
+
+${DX} --dex --min-sdk-version=28 --output=classes.dex classes
+
+zip $TEST_NAME.jar classes.dex
diff --git a/test/1948-obsolete-const-method-handle/expected.txt b/test/1948-obsolete-const-method-handle/expected.txt
new file mode 100644
index 0000000..a0b80a4
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/expected.txt
@@ -0,0 +1,6 @@
+Do nothing
+Hello
+transforming calling function
+Hello
+Do nothing
+Goodbye
diff --git a/test/1948-obsolete-const-method-handle/info.txt b/test/1948-obsolete-const-method-handle/info.txt
new file mode 100644
index 0000000..ef8eb69
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/info.txt
@@ -0,0 +1,6 @@
+Tests that obsolete methods work correctly in the presence of const-method-type.
+
+const-method-type and the invoke-custom/polymorphic opcodes are new in dex 39.
+It is almost impossible to get them emmited from normal java code so we don't
+have any coverage on them. To get this coverage we use ASM to build a class file
+that contains the required opcodes.
diff --git a/test/952-invoke-custom-lookup/build b/test/1948-obsolete-const-method-handle/run
old mode 100644
new mode 100755
similarity index 67%
copy from test/952-invoke-custom-lookup/build
copy to test/1948-obsolete-const-method-handle/run
index f3fe95c..9eacb4c
--- a/test/952-invoke-custom-lookup/build
+++ b/test/1948-obsolete-const-method-handle/run
@@ -14,14 +14,5 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# make us exit on a failure
-set -e
-
-# This test uses previously prepared dex and jar files. They need to
-# be re-packaged to match the files that the run-test-jar script
-# expects.
-if [[ $@ != *"--jvm"* ]]; then
-  zip ${TEST_NAME}.jar classes.dex
-else
-  unzip -d classes classes.jar
-fi
+# Squash the exit status and put it in expected
+./default-run --jvmti "$@"
diff --git a/test/1948-obsolete-const-method-handle/util-src/build-classes b/test/1948-obsolete-const-method-handle/util-src/build-classes
new file mode 100755
index 0000000..1b2d79a
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/util-src/build-classes
@@ -0,0 +1,45 @@
+#!/bin/bash
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function fail() {
+  echo Build failed: $1 1>&2
+  exit 1
+}
+
+D8=${ANDROID_HOST_OUT}/bin/d8
+if [[ ! -x "${D8}" ]]; then
+  fail "Must have a runnable d8 binary!"
+fi
+
+if [[ -z "${ANDROID_BUILD_TOP}" ]]; then
+  fail "ANDROID_BUILD_TOP is not defined. Try running 'lunch' first."
+fi
+
+SCRIPT_PATH=$( cd $(dirname $0) ; pwd -P )
+ASM_CLASSPATH="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-6.0.jar"
+SRC_PATH="${SCRIPT_PATH}/src"
+BUILD_PATH="${1:-${SCRIPT_PATH}/classes}"
+
+if [[ ! -d "${BUILD_PATH}" ]]; then
+    mkdir "$BUILD_PATH" || exit 1
+fi
+
+# Build the initial class files.
+(cd "${SRC_PATH}" && javac -cp "${ASM_CLASSPATH}" -d "${BUILD_PATH}" Main.java art/*.java art/constmethodhandle/*.java) || fail "javac error"
+# Modify the class files using ASM
+(cd "${SCRIPT_PATH}" && java -cp "${ASM_CLASSPATH}:${BUILD_PATH}" art.constmethodhandle.TestGenerator "${BUILD_PATH}" "$D8") || fail "generator failure"
+# Remove the modification classes. We don't need nor want them for the actual test.
+(cd ${BUILD_PATH} && find . -name "TestGenerator*.class" | xargs rm) || fail "Cleanup failure"
diff --git a/test/1948-obsolete-const-method-handle/util-src/info.txt b/test/1948-obsolete-const-method-handle/util-src/info.txt
new file mode 100644
index 0000000..330659a
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/util-src/info.txt
@@ -0,0 +1,7 @@
+This builds the class files for test 1948. It does this by first compiling the
+contents of src/. Then it uses TestGenerator to add additional functions to the
+TestInvoker and Test1948 classes. These additions are a new method that loads a
+constant method-handle and invokes it in TestInvoker and functions that will
+return the dex & class bytes for the redefinition that changes the target of the
+methodHandle TestInvoker loads.
+
diff --git a/test/1948-obsolete-const-method-handle/util-src/src/Main.java b/test/1948-obsolete-const-method-handle/util-src/src/Main.java
new file mode 100644
index 0000000..9a1af2b
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/util-src/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Throwable {
+    art.Test1948.run();
+  }
+}
diff --git a/test/1948-obsolete-const-method-handle/util-src/src/art/Redefinition.java b/test/1948-obsolete-const-method-handle/util-src/src/art/Redefinition.java
new file mode 100644
index 0000000..1eec70b
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/util-src/src/art/Redefinition.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+  public static final class CommonClassDefinition {
+    public final Class<?> target;
+    public final byte[] class_file_bytes;
+    public final byte[] dex_file_bytes;
+
+    public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+      this.target = target;
+      this.class_file_bytes = class_file_bytes;
+      this.dex_file_bytes = dex_file_bytes;
+    }
+  }
+
+  // A set of possible test configurations. Test should set this if they need to.
+  // This must be kept in sync with the defines in ti-agent/common_helper.cc
+  public static enum Config {
+    COMMON_REDEFINE(0),
+    COMMON_RETRANSFORM(1),
+    COMMON_TRANSFORM(2);
+
+    private final int val;
+    private Config(int val) {
+      this.val = val;
+    }
+  }
+
+  public static void setTestConfiguration(Config type) {
+    nativeSetTestConfiguration(type.val);
+  }
+
+  private static native void nativeSetTestConfiguration(int type);
+
+  // Transforms the class
+  public static native void doCommonClassRedefinition(Class<?> target,
+                                                      byte[] classfile,
+                                                      byte[] dexfile);
+
+  public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+    ArrayList<Class<?>> classes = new ArrayList<>();
+    ArrayList<byte[]> class_files = new ArrayList<>();
+    ArrayList<byte[]> dex_files = new ArrayList<>();
+
+    for (CommonClassDefinition d : defs) {
+      classes.add(d.target);
+      class_files.add(d.class_file_bytes);
+      dex_files.add(d.dex_file_bytes);
+    }
+    doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+                                   class_files.toArray(new byte[0][]),
+                                   dex_files.toArray(new byte[0][]));
+  }
+
+  public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+    for (CommonClassDefinition d : defs) {
+      addCommonTransformationResult(d.target.getCanonicalName(),
+                                    d.class_file_bytes,
+                                    d.dex_file_bytes);
+    }
+  }
+
+  public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+                                                           byte[][] classfiles,
+                                                           byte[][] dexfiles);
+  public static native void doCommonClassRetransformation(Class<?>... target);
+  public static native void setPopRetransformations(boolean pop);
+  public static native void popTransformationFor(String name);
+  public static native void enableCommonRetransformation(boolean enable);
+  public static native void addCommonTransformationResult(String target_name,
+                                                          byte[] class_bytes,
+                                                          byte[] dex_bytes);
+}
diff --git a/test/1948-obsolete-const-method-handle/util-src/src/art/Test1948.java b/test/1948-obsolete-const-method-handle/util-src/src/art/Test1948.java
new file mode 100644
index 0000000..f5414fb
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/util-src/src/art/Test1948.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+import art.constmethodhandle.TestInvoke;
+import java.util.*;
+import java.lang.reflect.*;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+public class Test1948 {
+  // These are initialized by a method added by test_generator.
+  // They will contain the dex bytes of TestInvoker but with the method handle changed to pointing
+  // to sayBye.
+  public static final byte[] CLASS_BYTES;
+  public static final byte[] DEX_BYTES;
+  static {
+    try {
+      // TestGenerator will add functions that get the base64 string of these functions. When we
+      // compile this the functions haven't been generated yet though so just do things this way.
+      MethodHandle getClassBase64 = MethodHandles.lookup().findStatic(
+          Test1948.class, "getClassBase64", MethodType.methodType(String.class));
+      MethodHandle getDexBase64 = MethodHandles.lookup().findStatic(
+          Test1948.class, "getDexBase64", MethodType.methodType(String.class));
+      CLASS_BYTES = Base64.getDecoder().decode((String)getClassBase64.invokeExact());
+      DEX_BYTES = Base64.getDecoder().decode((String)getDexBase64.invokeExact());
+    } catch (Throwable e) {
+      throw new Error("Failed to initialize statics: ", e);
+    }
+  }
+
+  public static void run() throws Throwable {
+    // NB Because we aren't using desugar we cannot use capturing-lambda or string concat anywhere
+    // in this test! Version 9+ javac turns these into invokedynamics using bootstrap methods not
+    // currently present in android.
+    new TestInvoke().runTest(
+        new Runnable() { public void run() { System.out.println("Do nothing"); } });
+    new TestInvoke().runTest(
+        new Runnable() {
+          public void run() {
+            System.out.println("transforming calling function");
+            Redefinition.doCommonClassRedefinition(TestInvoke.class, CLASS_BYTES, DEX_BYTES);
+          }
+        });
+    new TestInvoke().runTest(
+        new Runnable() { public void run() { System.out.println("Do nothing"); } });
+  }
+}
diff --git a/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/BaseTestInvoke.java b/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/BaseTestInvoke.java
new file mode 100644
index 0000000..c38f387
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/BaseTestInvoke.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art.constmethodhandle;
+
+public class BaseTestInvoke {
+  // Simply used to make sure that everything links right when building.
+  public void runTest(Runnable preCall) {
+    throw new Error("Should not be called!");
+  }
+}
diff --git a/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/Responses.java b/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/Responses.java
new file mode 100644
index 0000000..2495647
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/Responses.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art.constmethodhandle;
+
+// We call one of these using a constant method-handle. Which one is called is changed by the
+// redefinition.
+public class Responses {
+  public static void sayHi() {
+    System.out.println("Hello");
+  }
+
+  public static void sayBye() {
+    System.out.println("Goodbye");
+  }
+}
diff --git a/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/TestGenerator.java b/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/TestGenerator.java
new file mode 100644
index 0000000..40b5cf9
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/TestGenerator.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art.constmethodhandle;
+
+import java.io.*;
+import java.util.*;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.nio.file.*;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+// This test will modify in place the compiled java files to fill in the transformed version and
+// fill in the TestInvoker.runTest function with a load-constant of a method-handle. It will use d8
+// (passed in as an argument) to create the dex we will transform TestInvoke into.
+public class TestGenerator {
+
+  public static void main(String[] args) throws IOException {
+    if (args.length != 2) {
+      throw new Error("Unable to convert class to dex without d8 binary!");
+    }
+    Path base = Paths.get(args[0]);
+    String d8Bin = args[1];
+
+    Path initTestInvoke = base.resolve(TestGenerator.class.getPackage().getName().replace('.', '/'))
+                              .resolve(TestInvoke.class.getSimpleName() + ".class");
+    byte[] initClass = new FileInputStream(initTestInvoke.toFile()).readAllBytes();
+
+    // Make the initial version of TestInvoker
+    generateInvoker(initClass, "sayHi", new FileOutputStream(initTestInvoke.toFile()));
+
+    // Make the final 'class' version of testInvoker
+    ByteArrayOutputStream finalClass = new ByteArrayOutputStream();
+    generateInvoker(initClass, "sayBye", finalClass);
+
+    Path initTest1948 = base.resolve("art").resolve(art.Test1948.class.getSimpleName() + ".class");
+    byte[] finalClassBytes = finalClass.toByteArray();
+    byte[] finalDexBytes = getFinalDexBytes(d8Bin, finalClassBytes);
+    generateTestCode(
+        new FileInputStream(initTest1948.toFile()).readAllBytes(),
+        finalClassBytes,
+        finalDexBytes,
+        new FileOutputStream(initTest1948.toFile()));
+  }
+
+  // Modify the Test1948 class bytecode so it has the transformed version of TestInvoker as a string
+  // constant.
+  private static void generateTestCode(
+      byte[] initClass, byte[] transClass, byte[] transDex, OutputStream out) throws IOException {
+    ClassReader cr = new ClassReader(initClass);
+    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+    cr.accept(
+        new ClassVisitor(Opcodes.ASM6, cw) {
+          @Override
+          public void visitEnd() {
+            generateStringAccessorMethod(
+                cw, "getDexBase64", Base64.getEncoder().encodeToString(transDex));
+            generateStringAccessorMethod(
+                cw, "getClassBase64", Base64.getEncoder().encodeToString(transClass));
+            super.visitEnd();
+          }
+        }, 0);
+    out.write(cw.toByteArray());
+  }
+
+  // Insert a string accessor method so we can get the transformed versions of TestInvoker.
+  private static void generateStringAccessorMethod(ClassVisitor cv, String name, String ret) {
+    MethodVisitor mv = cv.visitMethod(
+        Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC,
+        name, "()Ljava/lang/String;", null, null);
+    mv.visitLdcInsn(ret);
+    mv.visitInsn(Opcodes.ARETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  // Use d8bin to convert the classBytes into a dex file bytes. We need to do this here because we
+  // need the dex-file bytes to be used by the test class to redefine TestInvoker. We use d8 because
+  // it doesn't require setting up a directory structures or matching file names like dx does.
+  // TODO We should maybe just call d8 functions directly?
+  private static byte[] getFinalDexBytes(String d8Bin, byte[] classBytes) throws IOException {
+    Path tempDir = Files.createTempDirectory("FinalTestInvoker_Gen");
+    File tempInput = Files.createTempFile(tempDir, "temp_input_class", ".class").toFile();
+
+    OutputStream tempClassStream = new FileOutputStream(tempInput);
+    tempClassStream.write(classBytes);
+    tempClassStream.close();
+    tempClassStream = null;
+
+    Process d8Proc = new ProcessBuilder(d8Bin,
+                                        // Put classes.dex in the temp-dir we made.
+                                        "--output", tempDir.toAbsolutePath().toString(),
+                                        "--min-api", "28",  // Allow the new invoke ops.
+                                        "--no-desugaring",  // Don't try to be clever please.
+                                        tempInput.toPath().toAbsolutePath().toString())
+        .inheritIO()  // Just print to stdio.
+        .start();
+    int res;
+    try {
+      res = d8Proc.waitFor();
+    } catch (Exception e) {
+      System.out.println("Failed to dex: ".concat(e.toString()));
+      e.printStackTrace();
+      res = -123;
+    }
+    tempInput.delete();
+    try {
+      if (res == 0) {
+        byte[] out = new FileInputStream(tempDir.resolve("classes.dex").toFile()).readAllBytes();
+        tempDir.resolve("classes.dex").toFile().delete();
+        return out;
+      }
+    } finally {
+      tempDir.toFile().delete();
+    }
+    throw new Error("Failed to get dex file! " + res);
+  }
+
+  private static void generateInvoker(
+      byte[] inputClass,
+      String toCall,
+      OutputStream output) throws IOException {
+    ClassReader cr = new ClassReader(inputClass);
+    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+    cr.accept(
+        new ClassVisitor(Opcodes.ASM6, cw) {
+          @Override
+          public void visitEnd() {
+            generateRunTest(cw, toCall);
+            super.visitEnd();
+          }
+        }, 0);
+    output.write(cw.toByteArray());
+  }
+
+  // Creates the following method:
+  //   public runTest(Runnable preCall) {
+  //     preCall.run();
+  //     MethodHandle mh = <CONSTANT MH>;
+  //     mh.invokeExact();
+  //   }
+  private static void generateRunTest(ClassVisitor cv, String toCall) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC,
+                                      "runTest", "(Ljava/lang/Runnable;)V", null, null);
+    MethodType mt = MethodType.methodType(Void.TYPE);
+    Handle mh = new Handle(
+        Opcodes.H_INVOKESTATIC,
+        Type.getInternalName(Responses.class),
+        toCall,
+        mt.toMethodDescriptorString(),
+        false);
+    String internalName = Type.getInternalName(Runnable.class);
+    mv.visitVarInsn(Opcodes.ALOAD, 1);
+    mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, internalName, "run", "()V", true);
+    mv.visitLdcInsn(mh);
+    mv.visitMethodInsn(
+        Opcodes.INVOKEVIRTUAL,
+        Type.getInternalName(MethodHandle.class),
+        "invokeExact",
+        "()V",
+        false);
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+}
diff --git a/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/TestInvoke.java b/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/TestInvoke.java
new file mode 100644
index 0000000..eaa856e
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/TestInvoke.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art.constmethodhandle;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+
+public class TestInvoke extends BaseTestInvoke {
+  // THIS IS GENERATED BY ASM
+  // @Override
+  // public void runTest(Runnable preCall) {
+  //   preCall.run();
+  //   // MAGIC! Replaced with a static method handle pointing at
+  //   // art.constmethodhandle.Responses.sayHi // (or art.constmethodhandle.Responses.sayBye in the
+  //   // redefined version).
+  //   MethodHandle handle = CONSTANT_MH<Responses.sayHi>;
+  //   handle.invokeExact();
+  // }
+}
diff --git a/test/676-proxy-jit-at-first-use/expected.txt b/test/676-proxy-jit-at-first-use/expected.txt
new file mode 100644
index 0000000..6915b2f
--- /dev/null
+++ b/test/676-proxy-jit-at-first-use/expected.txt
@@ -0,0 +1 @@
+Method: public abstract void Interface.foo()
diff --git a/test/676-proxy-jit-at-first-use/info.txt b/test/676-proxy-jit-at-first-use/info.txt
new file mode 100644
index 0000000..90b683b
--- /dev/null
+++ b/test/676-proxy-jit-at-first-use/info.txt
@@ -0,0 +1 @@
+Regression test for "jit at first use" (-Xjitthreshold:0) crash for proxy methods. b/73718713
diff --git a/test/676-proxy-jit-at-first-use/run b/test/676-proxy-jit-at-first-use/run
new file mode 100644
index 0000000..16c9f76
--- /dev/null
+++ b/test/676-proxy-jit-at-first-use/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Enable "jit at first use" (-Xjitthreshold:0).
+# Ensure this test is not subject to unexpected code collection.
+${RUN} "${@}" --runtime-option -Xjitthreshold:0 --runtime-option -Xjitinitialsize:32M
diff --git a/test/676-proxy-jit-at-first-use/src/Main.java b/test/676-proxy-jit-at-first-use/src/Main.java
new file mode 100644
index 0000000..4ed773f
--- /dev/null
+++ b/test/676-proxy-jit-at-first-use/src/Main.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+public class Main {
+    public static void main(String[] args) throws Exception {
+        Interface i = (Interface) Proxy.newProxyInstance(Main.class.getClassLoader(),
+                                                         new Class<?>[] { Interface.class },
+                                                         new Handler());
+        i.foo();
+    }
+}
+
+interface Interface {
+    void foo();
+}
+
+class Handler implements InvocationHandler {
+    public Object invoke(Object proxy, Method method, Object[] args) {
+        System.out.println("Method: " + method);
+        return null;
+    }
+}
diff --git a/test/676-resolve-field-type/expected.txt b/test/676-resolve-field-type/expected.txt
new file mode 100644
index 0000000..a965a70
--- /dev/null
+++ b/test/676-resolve-field-type/expected.txt
@@ -0,0 +1 @@
+Done
diff --git a/test/676-resolve-field-type/info.txt b/test/676-resolve-field-type/info.txt
new file mode 100644
index 0000000..a53244d
--- /dev/null
+++ b/test/676-resolve-field-type/info.txt
@@ -0,0 +1,2 @@
+Test trying to reproduce class loader issues with the verifier.
+See comments in src-ex/ChildClass.java
diff --git a/test/676-resolve-field-type/src-art/Foo.java b/test/676-resolve-field-type/src-art/Foo.java
new file mode 100644
index 0000000..3df74d3
--- /dev/null
+++ b/test/676-resolve-field-type/src-art/Foo.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Foo {
+  public static Main mainObject;
+}
diff --git a/test/676-resolve-field-type/src-art/Main.java b/test/676-resolve-field-type/src-art/Main.java
new file mode 100644
index 0000000..c915df8
--- /dev/null
+++ b/test/676-resolve-field-type/src-art/Main.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import dalvik.system.PathClassLoader;
+import java.io.File;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    ClassLoader parentLoader = Main.class.getClassLoader();
+    ClassLoader childLoader = new PathClassLoader(DEX_CHILD, parentLoader);
+    Class.forName("ChildClass", true, childLoader).getDeclaredMethod("runTest").invoke(null);
+  }
+
+  private static final String DEX_CHILD =
+      new File(System.getenv("DEX_LOCATION"), "676-resolve-field-type-ex.jar").getAbsolutePath();
+
+  public static void staticMethod() {}
+}
diff --git a/test/676-resolve-field-type/src-ex/ChildClass.java b/test/676-resolve-field-type/src-ex/ChildClass.java
new file mode 100644
index 0000000..167d4a6
--- /dev/null
+++ b/test/676-resolve-field-type/src-ex/ChildClass.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class ChildClass {
+  // This method being synchronized means the SIGQUIT code in ART will call
+  // FindLocksAtDexPc (we check for the presence of try blocks),
+  // which triggered a DCHECK of an invariant.
+  public static synchronized void runTest() throws Exception {
+    Main m = Foo.mainObject;
+
+    SigQuit.doKill();
+
+    // Sleep some time to get the kill while executing this method.
+    Thread.sleep(2);
+
+    // The FindLocksAtDexPc method running with the verifier would fail when
+    // resolving this call, as the verifier didn't register Main from the field
+    // access above with the current class loader.
+    Main.staticMethod();
+    System.out.println("Done");
+  }
+
+  private final static class SigQuit {
+    private final static int sigquit;
+    private final static Method kill;
+    private final static int pid;
+
+    static {
+      int pidTemp = -1;
+      int sigquitTemp = -1;
+      Method killTemp = null;
+
+      try {
+        Class<?> osClass = Class.forName("android.system.Os");
+        Method getpid = osClass.getDeclaredMethod("getpid");
+        pidTemp = (Integer)getpid.invoke(null);
+
+        Class<?> osConstants = Class.forName("android.system.OsConstants");
+        Field sigquitField = osConstants.getDeclaredField("SIGQUIT");
+        sigquitTemp = (Integer)sigquitField.get(null);
+
+        killTemp = osClass.getDeclaredMethod("kill", int.class, int.class);
+      } catch (Exception e) {
+        throw new Error(e);
+      }
+
+      pid = pidTemp;
+      sigquit = sigquitTemp;
+      kill = killTemp;
+    }
+
+    public static void doKill() throws Exception {
+      kill.invoke(null, pid, sigquit);
+    }
+  }
+}
diff --git a/test/714-invoke-custom-lambda-metafactory/expected.txt b/test/714-invoke-custom-lambda-metafactory/expected.txt
index 54b6c24..c98a4c2 100644
--- a/test/714-invoke-custom-lambda-metafactory/expected.txt
+++ b/test/714-invoke-custom-lambda-metafactory/expected.txt
@@ -1,5 +1,5 @@
 Exception in thread "main" java.lang.BootstrapMethodError: Exception from call site #0 bootstrap method
 	at Main.main(Main.java:25)
-Caused by: java.lang.NullPointerException: Bootstrap method returned null
+Caused by: java.lang.ClassCastException: Bootstrap method returned null
 	... 1 more
 exit status: 1
diff --git a/test/909-attach-agent/run b/test/909-attach-agent/run
index 4a2eb34..a556bba 100755
--- a/test/909-attach-agent/run
+++ b/test/909-attach-agent/run
@@ -25,9 +25,15 @@
                    --android-runtime-option -Xcompiler-option \
                    --android-runtime-option --debuggable \
                    --args agent:${agent}=909-attach-agent
+return_status1=$?
 
 ./default-run "$@" --android-runtime-option -Xcompiler-option \
                    --android-runtime-option --debuggable \
                    --args agent:${agent}=909-attach-agent
+return_status2=$?
 
 ./default-run "$@" --args agent:${agent}=909-attach-agent
+return_status3=$?
+
+# Make sure we don't silently ignore an early failure.
+(exit $return_status1) && (exit $return_status2) && (exit $return_status3)
diff --git a/test/952-invoke-custom-lookup/classes.dex b/test/952-invoke-custom-lookup/classes.dex
deleted file mode 100644
index 670d93d..0000000
--- a/test/952-invoke-custom-lookup/classes.dex
+++ /dev/null
Binary files differ
diff --git a/test/952-invoke-custom-lookup/classes.jar b/test/952-invoke-custom-lookup/classes.jar
deleted file mode 100644
index aa6a1f6..0000000
--- a/test/952-invoke-custom-lookup/classes.jar
+++ /dev/null
Binary files differ
diff --git a/test/952-invoke-custom-lookup/expected.txt b/test/952-invoke-custom-lookup/expected.txt
deleted file mode 100644
index 0da2b86..0000000
--- a/test/952-invoke-custom-lookup/expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-NAME: 1
-CALLER: Main
-CALLER CLASS: class Main
-THIS CLASS: class com.android.tools.r8.maindexlist.desugar.BootstrapHolder
-invokedynamic target on Main
-NAME: 2
-CALLER: Main
-CALLER CLASS: class Main
-THIS CLASS: class com.android.tools.r8.maindexlist.desugar.BootstrapHolder
-invokedynamic target on BootstrapHolder
diff --git a/test/952-invoke-custom-lookup/info.txt b/test/952-invoke-custom-lookup/info.txt
deleted file mode 100644
index 3bfe87d..0000000
--- a/test/952-invoke-custom-lookup/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-A temporary test for the lookup class used for invoke-custom (see b/73056094).
diff --git a/test/952-invoke-custom/build b/test/952-invoke-custom/build
old mode 100644
new mode 100755
index 2b0b2c1..2caca94
--- a/test/952-invoke-custom/build
+++ b/test/952-invoke-custom/build
@@ -1,6 +1,6 @@
 #!/bin/bash
 #
-# Copyright 2016 The Android Open Source Project
+# Copyright 2018 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -17,4 +17,36 @@
 # make us exit on a failure
 set -e
 
-./default-build "$@" --experimental method-handles
+ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-6.0.jar"
+INTERMEDIATE_CLASSES=classes-intermediate
+CLASSES=classes
+
+DEXER="${DX:-dx}"
+if [ "${USE_D8=false}" = "true" ]; then
+  DEXER="${ANDROID_HOST_OUT}/bin/d8-compat-dx"
+fi
+
+# Create directory for intermediate classes
+rm -rf "${INTERMEDIATE_CLASSES}"
+mkdir "${INTERMEDIATE_CLASSES}"
+
+# Generate intermediate classes that will allow transform to be applied to test classes
+JAVAC_ARGS="${JAVAC_ARGS} -source 1.8 -target 1.8 -cp ${ASM_JAR}"
+${JAVAC:-javac} ${JAVAC_ARGS} -d ${INTERMEDIATE_CLASSES} $(find src -name '*.java')
+
+# Create directory for transformed classes
+rm -rf "${CLASSES}"
+mkdir "${CLASSES}"
+
+# Run transform
+for class in ${INTERMEDIATE_CLASSES}/*.class ; do
+  transformed_class=${CLASSES}/$(basename ${class})
+  ${JAVA:-java} -cp "${ASM_JAR}:${INTERMEDIATE_CLASSES}" transformer.IndyTransformer ${class} ${transformed_class}
+done
+
+# Create DEX
+DX_FLAGS="${DX_FLAGS} --min-sdk-version=26 --debug --dump-width=1000"
+${DEXER} -JXmx256m --dex ${DX_FLAGS} --dump-to=${CLASSES}.lst --output=classes.dex ${CLASSES}
+
+# Zip DEX to file name expected by test runner
+zip ${TEST_NAME:-classes-dex}.jar classes.dex
diff --git a/test/952-invoke-custom/expected.txt b/test/952-invoke-custom/expected.txt
index bb87296..be01c45 100644
--- a/test/952-invoke-custom/expected.txt
+++ b/test/952-invoke-custom/expected.txt
@@ -1,14 +1,15 @@
 Caught exception from uninitialized call site
 Caught exception from uninitialized call site
 linkerMethod failure type 1
-Returning null instead of CallSite for add (int,int)int
+Returning null instead of CallSite for _add (int,int)int
 linkerMethod failure type 2
 Throwing InstantiationException in linkerMethod()
 linkerMethod failure type 3
 Throwing ArithmeticException in add()
 Failure Type + 0 (1013)
-Linking add (int,int)int
+Linking _add (int,int)int
 100
 -9000
 9000
+TestLinkerUnrelatedBSM
 Winners 1 Votes 16
diff --git a/test/952-invoke-custom/generator/TestInvokeCustomWithConcurrentThreads.java b/test/952-invoke-custom/generator/TestInvokeCustomWithConcurrentThreads.java
deleted file mode 100644
index 9c0645b..0000000
--- a/test/952-invoke-custom/generator/TestInvokeCustomWithConcurrentThreads.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import com.android.jack.annotations.CalledByInvokeCustom;
-import com.android.jack.annotations.Constant;
-import com.android.jack.annotations.LinkerMethodHandle;
-import com.android.jack.annotations.MethodHandleKind;
-
-import java.lang.invoke.CallSite;
-import java.lang.invoke.ConstantCallSite;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
-
-import java.lang.Thread;
-import java.lang.ThreadLocal;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.CyclicBarrier;
-
-public class TestInvokeCustomWithConcurrentThreads extends Thread {
-  private static final int NUMBER_OF_THREADS = 16;
-
-  private static final AtomicInteger nextIndex = new AtomicInteger(0);
-
-  private static final ThreadLocal<Integer> threadIndex =
-      new ThreadLocal<Integer>() {
-        @Override
-        protected Integer initialValue() {
-          return nextIndex.getAndIncrement();
-        }
-      };
-
-  // Array of call sites instantiated, one per thread
-  private static final CallSite[] instantiated = new CallSite[NUMBER_OF_THREADS];
-
-  // Array of counters for how many times each instantiated call site is called
-  private static final AtomicInteger[] called = new AtomicInteger[NUMBER_OF_THREADS];
-
-  // Array of call site indicies of which call site a thread invoked
-  private static final AtomicInteger[] targetted = new AtomicInteger[NUMBER_OF_THREADS];
-
-  // Synchronization barrier all threads will wait on in the bootstrap method.
-  private static final CyclicBarrier barrier = new CyclicBarrier(NUMBER_OF_THREADS);
-
-  private TestInvokeCustomWithConcurrentThreads() {}
-
-  private static int getThreadIndex() {
-    return threadIndex.get().intValue();
-  }
-
-  public static int notUsed(int x) {
-    return x;
-  }
-
-  @Override
-  public void run() {
-    int x = setCalled(-1 /* argument dropped */);
-    notUsed(x);
-  }
-
-  @CalledByInvokeCustom(
-      invokeMethodHandle = @LinkerMethodHandle(kind = MethodHandleKind.INVOKE_STATIC,
-          enclosingType = TestInvokeCustomWithConcurrentThreads.class,
-          name = "linkerMethod",
-          argumentTypes = {MethodHandles.Lookup.class, String.class, MethodType.class}),
-      name = "setCalled",
-      returnType = int.class,
-      argumentTypes = {int.class})
-  private static int setCalled(int index) {
-    called[index].getAndIncrement();
-    targetted[getThreadIndex()].set(index);
-    return 0;
-  }
-
-  @SuppressWarnings("unused")
-  private static CallSite linkerMethod(MethodHandles.Lookup caller,
-                                       String name,
-                                       MethodType methodType) throws Throwable {
-    int threadIndex = getThreadIndex();
-    MethodHandle mh =
-        caller.findStatic(TestInvokeCustomWithConcurrentThreads.class, name, methodType);
-    assertEquals(methodType, mh.type());
-    assertEquals(mh.type().parameterCount(), 1);
-    mh = MethodHandles.insertArguments(mh, 0, threadIndex);
-    mh = MethodHandles.dropArguments(mh, 0, int.class);
-    assertEquals(mh.type().parameterCount(), 1);
-    assertEquals(methodType, mh.type());
-
-    // Wait for all threads to be in this method.
-    // Multiple call sites should be created, but only one
-    // invoked.
-    barrier.await();
-
-    instantiated[getThreadIndex()] = new ConstantCallSite(mh);
-    return instantiated[getThreadIndex()];
-  }
-
-  public static void test() throws Throwable {
-    // Initialize counters for which call site gets invoked
-    for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
-      called[i] = new AtomicInteger(0);
-      targetted[i] = new AtomicInteger(0);
-    }
-
-    // Run threads that each invoke-custom the call site
-    Thread [] threads = new Thread[NUMBER_OF_THREADS];
-    for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
-      threads[i] = new TestInvokeCustomWithConcurrentThreads();
-      threads[i].start();
-    }
-
-    // Wait for all threads to complete
-    for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
-      threads[i].join();
-    }
-
-    // Check one call site instance won
-    int winners = 0;
-    int votes = 0;
-    for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
-      assertNotEquals(instantiated[i], null);
-      if (called[i].get() != 0) {
-        winners++;
-        votes += called[i].get();
-      }
-    }
-
-    System.out.println("Winners " + winners + " Votes " + votes);
-
-    // We assert this below but output details when there's an error as
-    // it's non-deterministic.
-    if (winners != 1) {
-      System.out.println("Threads did not the same call-sites:");
-      for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
-        System.out.format(" Thread % 2d invoked call site instance #%02d\n",
-                          i, targetted[i].get());
-      }
-    }
-
-    // We assert this below but output details when there's an error as
-    // it's non-deterministic.
-    if (votes != NUMBER_OF_THREADS) {
-      System.out.println("Call-sites invocations :");
-      for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
-        System.out.format(" Call site instance #%02d was invoked % 2d times\n",
-                          i, called[i].get());
-      }
-    }
-
-    assertEquals(winners, 1);
-    assertEquals(votes, NUMBER_OF_THREADS);
-  }
-
-  public static void assertTrue(boolean value) {
-    if (!value) {
-      throw new AssertionError("assertTrue value: " + value);
-    }
-  }
-
-  public static void assertEquals(byte b1, byte b2) {
-    if (b1 == b2) { return; }
-    throw new AssertionError("assertEquals b1: " + b1 + ", b2: " + b2);
-  }
-
-  public static void assertEquals(char c1, char c2) {
-    if (c1 == c2) { return; }
-    throw new AssertionError("assertEquals c1: " + c1 + ", c2: " + c2);
-  }
-
-  public static void assertEquals(short s1, short s2) {
-    if (s1 == s2) { return; }
-    throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
-  }
-
-  public static void assertEquals(int i1, int i2) {
-    if (i1 == i2) { return; }
-    throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2);
-  }
-
-  public static void assertEquals(long l1, long l2) {
-    if (l1 == l2) { return; }
-    throw new AssertionError("assertEquals l1: " + l1 + ", l2: " + l2);
-  }
-
-  public static void assertEquals(float f1, float f2) {
-    if (f1 == f2) { return; }
-    throw new AssertionError("assertEquals f1: " + f1 + ", f2: " + f2);
-  }
-
-  public static void assertEquals(double d1, double d2) {
-    if (d1 == d2) { return; }
-    throw new AssertionError("assertEquals d1: " + d1 + ", d2: " + d2);
-  }
-
-  public static void assertEquals(Object o, Object p) {
-    if (o == p) { return; }
-    if (o != null && p != null && o.equals(p)) { return; }
-    throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p);
-  }
-
-  public static void assertNotEquals(Object o, Object p) {
-    if (o != p) { return; }
-    if (o != null && p != null && !o.equals(p)) { return; }
-    throw new AssertionError("assertNotEquals: o1: " + o + ", o2: " + p);
-  }
-
-  public static void assertEquals(String s1, String s2) {
-    if (s1 == s2) {
-      return;
-    }
-
-    if (s1 != null && s2 != null && s1.equals(s2)) {
-      return;
-    }
-
-    throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
-  }
-}
diff --git a/test/952-invoke-custom/generator/TestLinkerMethodMinimalArguments.java b/test/952-invoke-custom/generator/TestLinkerMethodMinimalArguments.java
deleted file mode 100644
index 93d96a9..0000000
--- a/test/952-invoke-custom/generator/TestLinkerMethodMinimalArguments.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import com.android.jack.annotations.CalledByInvokeCustom;
-import com.android.jack.annotations.Constant;
-import com.android.jack.annotations.LinkerMethodHandle;
-import com.android.jack.annotations.MethodHandleKind;
-
-import java.lang.invoke.CallSite;
-import java.lang.invoke.ConstantCallSite;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
-
-public class TestLinkerMethodMinimalArguments {
-
-  private static int forceFailureType = 0;
-
-  private static int FAILURE_TYPE_NONE = 0;
-  private static int FAILURE_TYPE_LINKER_METHOD_RETURNS_NULL = 1;
-  private static int FAILURE_TYPE_LINKER_METHOD_THROWS = 2;
-  private static int FAILURE_TYPE_TARGET_METHOD_THROWS = 3;
-
-  @CalledByInvokeCustom(
-      invokeMethodHandle = @LinkerMethodHandle(
-          kind = MethodHandleKind.INVOKE_STATIC,
-          enclosingType = TestLinkerMethodMinimalArguments.class,
-          argumentTypes = {MethodHandles.Lookup.class, String.class, MethodType.class},
-          name = "linkerMethod"),
-      name = "add",
-      returnType = int.class,
-      argumentTypes = {int.class, int.class})
-  private static int add(int a, int b) {
-    if (forceFailureType == FAILURE_TYPE_TARGET_METHOD_THROWS) {
-      System.out.println("Throwing ArithmeticException in add()");
-      throw new ArithmeticException("add");
-    }
-    return a + b;
-  }
-
-  @SuppressWarnings("unused")
-  private static CallSite linkerMethod(MethodHandles.Lookup caller, String name,
-                                       MethodType methodType) throws Throwable {
-    System.out.println("linkerMethod failure type " + forceFailureType);
-    MethodHandle mh_add =
-        caller.findStatic(TestLinkerMethodMinimalArguments.class, name, methodType);
-    if (forceFailureType == FAILURE_TYPE_LINKER_METHOD_RETURNS_NULL) {
-      System.out.println("Returning null instead of CallSite for " + name + " " + methodType);
-      return null;
-    } else if (forceFailureType == FAILURE_TYPE_LINKER_METHOD_THROWS) {
-      System.out.println("Throwing InstantiationException in linkerMethod()");
-      throw new InstantiationException("linkerMethod");
-    } else {
-      return new ConstantCallSite(mh_add);
-    }
-  }
-
-  public static void test(int failureType, int x, int y) throws Throwable {
-    assertTrue(failureType >= FAILURE_TYPE_NONE);
-    assertTrue(failureType <= FAILURE_TYPE_TARGET_METHOD_THROWS);
-    forceFailureType = failureType;
-    assertEquals(x + y, add(x, y));
-    System.out.println("Failure Type + " + failureType + " (" + x + y+ ")");
-  }
-
-  public static void assertTrue(boolean value) {
-    if (!value) {
-      throw new AssertionError("assertTrue value: " + value);
-    }
-  }
-
-  public static void assertEquals(byte b1, byte b2) {
-    if (b1 == b2) { return; }
-    throw new AssertionError("assertEquals b1: " + b1 + ", b2: " + b2);
-  }
-
-  public static void assertEquals(char c1, char c2) {
-    if (c1 == c2) { return; }
-    throw new AssertionError("assertEquals c1: " + c1 + ", c2: " + c2);
-  }
-
-  public static void assertEquals(short s1, short s2) {
-    if (s1 == s2) { return; }
-    throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
-  }
-
-  public static void assertEquals(int i1, int i2) {
-    if (i1 == i2) { return; }
-    throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2);
-  }
-
-  public static void assertEquals(long l1, long l2) {
-    if (l1 == l2) { return; }
-    throw new AssertionError("assertEquals l1: " + l1 + ", l2: " + l2);
-  }
-
-  public static void assertEquals(float f1, float f2) {
-    if (f1 == f2) { return; }
-    throw new AssertionError("assertEquals f1: " + f1 + ", f2: " + f2);
-  }
-
-  public static void assertEquals(double d1, double d2) {
-    if (d1 == d2) { return; }
-    throw new AssertionError("assertEquals d1: " + d1 + ", d2: " + d2);
-  }
-
-  public static void assertEquals(Object o, Object p) {
-    if (o == p) { return; }
-    if (o != null && p != null && o.equals(p)) { return; }
-    throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p);
-  }
-
-  public static void assertEquals(String s1, String s2) {
-    if (s1 == s2) {
-      return;
-    }
-
-    if (s1 != null && s2 != null && s1.equals(s2)) {
-      return;
-    }
-
-    throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
-  }
-}
diff --git a/test/952-invoke-custom/generator/TestLinkerMethodMultipleArgumentTypes.java b/test/952-invoke-custom/generator/TestLinkerMethodMultipleArgumentTypes.java
deleted file mode 100644
index 4e4d97e..0000000
--- a/test/952-invoke-custom/generator/TestLinkerMethodMultipleArgumentTypes.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import com.android.jack.annotations.CalledByInvokeCustom;
-import com.android.jack.annotations.Constant;
-import com.android.jack.annotations.LinkerMethodHandle;
-import com.android.jack.annotations.MethodHandleKind;
-
-import java.lang.invoke.CallSite;
-import java.lang.invoke.ConstantCallSite;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
-
-public class TestLinkerMethodMultipleArgumentTypes {
-
-  private static int bootstrapRunCount = 0;
-
-  @CalledByInvokeCustom(
-      invokeMethodHandle = @LinkerMethodHandle(kind = MethodHandleKind.INVOKE_STATIC,
-          enclosingType = TestLinkerMethodMultipleArgumentTypes.class,
-          name = "linkerMethod",
-          argumentTypes = {MethodHandles.Lookup.class, String.class, MethodType.class,
-                           boolean.class, byte.class, char.class, short.class, int.class,
-                           float.class, double.class, String.class, Class.class, long.class}),
-      methodHandleExtraArgs = {@Constant(booleanValue = true), @Constant(byteValue = 1),
-                         @Constant(charValue = 'a'), @Constant(shortValue = 1024),
-                         @Constant(intValue = 1), @Constant(floatValue = 11.1f),
-                         @Constant(doubleValue = 2.2), @Constant(stringValue = "Hello"),
-                         @Constant(classValue = TestLinkerMethodMultipleArgumentTypes.class),
-                         @Constant(longValue = 123456789L)},
-      name = "add",
-      returnType = int.class,
-      argumentTypes = {int.class, int.class})
-  private static int add(int a, int b) {
-    return a + b;
-  }
-
-  @SuppressWarnings("unused")
-  private static CallSite linkerMethod(MethodHandles.Lookup caller, String name,
-                                       MethodType methodType, boolean v1, byte v2, char v3,
-                                       short v4, int v5, float v6, double v7,
-                                       String v8, Class<?> v9, long v10) throws Throwable {
-    System.out.println("Linking " + name + " " + methodType);
-    assertTrue(v1);
-    assertEquals(1, v2);
-    assertEquals('a', v3);
-    assertEquals(1024, v4);
-    assertEquals(1, v5);
-    assertEquals(11.1f, v6);
-    assertEquals(2.2, v7);
-    assertEquals("Hello", v8);
-    assertEquals(TestLinkerMethodMultipleArgumentTypes.class, v9);
-    assertEquals(123456789L, v10);
-    MethodHandle mh_add =
-        caller.findStatic(TestLinkerMethodMultipleArgumentTypes.class, name, methodType);
-    return new ConstantCallSite(mh_add);
-  }
-
-  public int GetBootstrapRunCount() {
-    return bootstrapRunCount;
-  }
-
-  public static void test(int x, int y) throws Throwable {
-    assertEquals(x + y, add(x, y));
-    System.out.println(x + y);
-  }
-
-  public static void assertTrue(boolean value) {
-    if (!value) {
-      throw new AssertionError("assertTrue value: " + value);
-    }
-  }
-
-  public static void assertEquals(byte b1, byte b2) {
-    if (b1 == b2) { return; }
-    throw new AssertionError("assertEquals b1: " + b1 + ", b2: " + b2);
-  }
-
-  public static void assertEquals(char c1, char c2) {
-    if (c1 == c2) { return; }
-    throw new AssertionError("assertEquals c1: " + c1 + ", c2: " + c2);
-  }
-
-  public static void assertEquals(short s1, short s2) {
-    if (s1 == s2) { return; }
-    throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
-  }
-
-  public static void assertEquals(int i1, int i2) {
-    if (i1 == i2) { return; }
-    throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2);
-  }
-
-  public static void assertEquals(long l1, long l2) {
-    if (l1 == l2) { return; }
-    throw new AssertionError("assertEquals l1: " + l1 + ", l2: " + l2);
-  }
-
-  public static void assertEquals(float f1, float f2) {
-    if (f1 == f2) { return; }
-    throw new AssertionError("assertEquals f1: " + f1 + ", f2: " + f2);
-  }
-
-  public static void assertEquals(double d1, double d2) {
-    if (d1 == d2) { return; }
-    throw new AssertionError("assertEquals d1: " + d1 + ", d2: " + d2);
-  }
-
-  public static void assertEquals(Object o, Object p) {
-    if (o == p) { return; }
-    if (o != null && p != null && o.equals(p)) { return; }
-    throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p);
-  }
-
-  public static void assertEquals(String s1, String s2) {
-    if (s1 == s2) {
-      return;
-    }
-
-    if (s1 != null && s2 != null && s1.equals(s2)) {
-      return;
-    }
-
-    throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
-  }
-}
diff --git a/test/952-invoke-custom/generator/build-test.sh b/test/952-invoke-custom/generator/build-test.sh
deleted file mode 100755
index d1d8221..0000000
--- a/test/952-invoke-custom/generator/build-test.sh
+++ /dev/null
@@ -1,86 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Set up prog to be the path of this script, including following symlinks,
-# and set up progdir to be the fully-qualified pathname of its directory.
-prog="$0"
-args="$@"
-while [ -h "${prog}" ]; do
-  newProg=`/bin/ls -ld "${prog}"`
-  newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
-  if expr "x${newProg}" : 'x/' >/dev/null; then
-      prog="${newProg}"
-  else
-    progdir=`dirname "${prog}"`
-    prog="${progdir}/${newProg}"
-  fi
-done
-oldwd=`pwd`
-progdir=`dirname "${prog}"`
-cd "${progdir}"
-progdir=`pwd`
-prog="${progdir}"/`basename "${prog}"`
-test_dir="test-$$"
-if [ -z "$TMPDIR" ]; then
-  tmp_dir="/tmp/$USER/${test_dir}"
-else
-  tmp_dir="${TMPDIR}/${test_dir}"
-fi
-
-if [ "x$ANDROID_BUILD_TOP" = "x" ]; then
-  echo Build environment is not set-up.
-  exit -1
-fi
-
-# This only works internally for now (sorry folks!)
-jack_annotations_lib=/google/data/rw/teams/android-runtime/jack/jack-test-annotations-lib.jack
-if [ ! -f $jack_annotations_lib ]; then
-  echo Try 'prodaccess' to access android-runtime directory.
-  exit -1
-fi
-
-# Compile test into a base64 string that can be instantiated via
-# reflection on hosts without the jack-test-annotations-lib.jack file.
-mkdir $tmp_dir
-for input_file in $progdir/*.java; do
-  i=${input_file##*/Test}
-  i=${i%%.java}
-  src_file=$progdir/Test$i.java
-  jack_file=./src.jack
-  dex_file=./classes.dex
-  base_64_file=$tmp_dir/TestData$i.base64
-  output_file=$progdir/../src/TestData$i.java
-  # Compile source file to jack file.
-  jack -g -cp $ANDROID_BUILD_TOP/out/host/linux-x86/../common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/classes.jack:$ANDROID_BUILD_TOP/out/host/linux-x86/../common/obj/JAVA_LIBRARIES/core-oj-hostdex_intermediates/classes.jack:$jack_annotations_lib -D sched.runner=multi-threaded -D sched.runner.thread.kind=fixed -D sched.runner.thread.fixed.count=4 -D jack.java.source.version=1.7 -D jack.android.min-api-level=o-b2 --output-jack $jack_file $src_file
-  # Compile jack file to classes.dex.
-  jack -g -cp $ANDROID_BUILD_TOP/out/host/linux-x86/../common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/classes.jack:$ANDROID_BUILD_TOP/out/host/linux-x86/../common/obj/JAVA_LIBRARIES/core-oj-hostdex_intermediates/classes.jack -D sched.runner=multi-threaded -D sched.runner.thread.kind=fixed -D sched.runner.thread.fixed.count=4 -D jack.java.source.version=1.7 -D jack.android.min-api-level=o-b2 --import $jack_file --output-dex .
-  # Pack the classes.dex file into a base64 string.
-  base64 -w 72 $dex_file > $base_64_file
-  # Emit a managed source file containing the base64 string. The test can be
-  # run by loading this string as a dex file and invoking it via reflection.
-cat > $output_file <<HEADER
-/* Generated by ${prog##*/} from ${src_file##*/} */
-public class TestData$i {
-  public static final String BASE64_DEX_FILE =
-HEADER
-sed -e 's/^\(.*\)$/    "\1" +/' -e '$s/ +/;/' $base_64_file >> $output_file
-cat >> $output_file <<FOOTER
-}
-FOOTER
-  rm $dex_file $jack_file
-done
-
-rm -rf $tmp_dir
diff --git a/test/952-invoke-custom/info.txt b/test/952-invoke-custom/info.txt
index 2954e55..1dce686 100644
--- a/test/952-invoke-custom/info.txt
+++ b/test/952-invoke-custom/info.txt
@@ -1,9 +1 @@
-A test that is only available as a DEX binary.
-
-This tests execution of invoke-custom. There is no bytecode to emit
-invoke-custom directly. This test is generated using an internal only jack file.
-
-Internal developers MUST regenerate the test data files after editing
-the tests under generator/ using:
-
-$ generator/build-tests.sh
+Tests the invoke-custom bytecode.
diff --git a/test/952-invoke-custom/src-art/Main.java b/test/952-invoke-custom/src-art/Main.java
deleted file mode 100644
index 2abc312..0000000
--- a/test/952-invoke-custom/src-art/Main.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import dalvik.system.InMemoryDexClassLoader;
-
-import java.lang.invoke.CallSite;
-import java.lang.invoke.MethodType;
-import java.lang.invoke.MutableCallSite;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.nio.ByteBuffer;
-import java.util.Base64;
-
-// This test is a stop-gap until we have support for generating invoke-custom
-// in the Android tree.
-
-public class Main {
-
-  private static void TestUninitializedCallSite() throws Throwable {
-    CallSite callSite = new MutableCallSite(MethodType.methodType(int.class));
-    try {
-      callSite.getTarget().invoke();
-      fail();
-    } catch (IllegalStateException e) {
-      System.out.println("Caught exception from uninitialized call site");
-    }
-
-    callSite = new MutableCallSite(MethodType.methodType(String.class, int.class, char.class));
-    try {
-      callSite.getTarget().invoke(1535, 'd');
-      fail();
-    } catch (IllegalStateException e) {
-      System.out.println("Caught exception from uninitialized call site");
-    }
-  }
-
-  private static void TestLinkerMethodMultipleArgumentTypes() throws Throwable {
-    // This is a more comprehensive test of invoke-custom, the linker
-    // method takes additional arguments of types boolean, byte, char,
-    // short, int, float, double, String, Class, and long (in this order)
-    // The test asserts the values passed to the linker method match their
-    // expected values.
-    byte[] base64Data = TestDataLinkerMethodMultipleArgumentTypes.BASE64_DEX_FILE.getBytes();
-    Base64.Decoder decoder = Base64.getDecoder();
-    ByteBuffer dexBuffer = ByteBuffer.wrap(decoder.decode(base64Data));
-
-    InMemoryDexClassLoader classLoader =
-        new InMemoryDexClassLoader(dexBuffer,
-                                   ClassLoader.getSystemClassLoader());
-    Class<?> testClass =
-        classLoader.loadClass("TestLinkerMethodMultipleArgumentTypes");
-    Method testMethod = testClass.getDeclaredMethod("test", int.class, int.class);
-    // First invocation should link via the bootstrap method (outputs "Linking add" ...).
-    testMethod.invoke(null, 33, 67);
-    // Subsequent invocations use the cached value of the CallSite and do not require linking.
-    testMethod.invoke(null, -10000, +1000);
-    testMethod.invoke(null, -1000, +10000);
-  }
-
-  private static void TestLinkerMethodMinimalArguments() throws Throwable {
-    // This test checks various failures when running the linker
-    // method and during invocation of the method handle.
-    byte[] base64Data = TestDataLinkerMethodMinimalArguments.BASE64_DEX_FILE.getBytes();
-    Base64.Decoder decoder = Base64.getDecoder();
-    ByteBuffer dexBuffer = ByteBuffer.wrap(decoder.decode(base64Data));
-
-    InMemoryDexClassLoader classLoader =
-        new InMemoryDexClassLoader(dexBuffer,
-                                   ClassLoader.getSystemClassLoader());
-    Class<?> testClass =
-        classLoader.loadClass("TestLinkerMethodMinimalArguments");
-    Method testMethod = testClass.getDeclaredMethod("test", int.class, int.class, int.class);
-
-    try {
-      testMethod.invoke(null, 1 /* linker method return null */, 10, 10);
-    } catch (InvocationTargetException e) {
-      assertEquals(e.getCause().getClass().getName(), "java.lang.BootstrapMethodError");
-      assertEquals(
-          e.getCause().getCause().getClass().getName(), "java.lang.NullPointerException");
-    }
-
-    try {
-      testMethod.invoke(null, 2 /* linker method throw InstantiationException */, 10, 11);
-    } catch (InvocationTargetException e) {
-      assertEquals(e.getCause().getClass().getName(), "java.lang.BootstrapMethodError");
-      assertEquals(
-          e.getCause().getCause().getClass().getName(), "java.lang.InstantiationException");
-    }
-    try {
-      // Creating the CallSite works here, but fail invoking the method.
-      testMethod.invoke(null, 3 /* target throw NPE */, 10, 12);
-    } catch (InvocationTargetException e) {
-      assertEquals(e.getCause().getClass().getName(), "java.lang.ArithmeticException");
-    }
-
-    // This should succeed using already resolved CallSite.
-    testMethod.invoke(null, 0 /* no error */, 10, 13);
-  }
-
-  private static void TestInvokeCustomWithConcurrentThreads() throws Throwable {
-    // This is a concurrency test that attempts to run invoke-custom on the same
-    // call site.
-    byte[] base64Data = TestDataInvokeCustomWithConcurrentThreads.BASE64_DEX_FILE.getBytes();
-    Base64.Decoder decoder = Base64.getDecoder();
-    ByteBuffer dexBuffer = ByteBuffer.wrap(decoder.decode(base64Data));
-
-    InMemoryDexClassLoader classLoader =
-        new InMemoryDexClassLoader(dexBuffer,
-                                   ClassLoader.getSystemClassLoader());
-    Class<?> testClass =
-        classLoader.loadClass("TestInvokeCustomWithConcurrentThreads");
-    Method testMethod = testClass.getDeclaredMethod("test");
-    testMethod.invoke(null);
-  }
-
-  public static void assertEquals(Object o, Object p) {
-    if (o == p) { return; }
-    if (o != null && p != null && o.equals(p)) { return; }
-    throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p);
-  }
-
-  public static void assertEquals(String s1, String s2) {
-    if (s1 == s2) {
-      return;
-    }
-
-    if (s1 != null && s2 != null && s1.equals(s2)) {
-      return;
-    }
-
-    throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
-  }
-
-  private static void fail() {
-    System.out.println("fail");
-    Thread.dumpStack();
-  }
-
-  public static void main(String[] args) throws Throwable {
-    TestUninitializedCallSite();
-    TestLinkerMethodMinimalArguments();
-    TestLinkerMethodMultipleArgumentTypes();
-    TestInvokeCustomWithConcurrentThreads();
-  }
-}
\ No newline at end of file
diff --git a/test/952-invoke-custom/src-art/TestDataInvokeCustomWithConcurrentThreads.java b/test/952-invoke-custom/src-art/TestDataInvokeCustomWithConcurrentThreads.java
deleted file mode 100644
index 076acd7..0000000
--- a/test/952-invoke-custom/src-art/TestDataInvokeCustomWithConcurrentThreads.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/* Generated by build-test.sh from TestInvokeCustomWithConcurrentThreads.java */
-public class TestDataInvokeCustomWithConcurrentThreads {
-  public static final String BASE64_DEX_FILE =
-    "ZGV4CjAzOABWCyocbwmvY62Y5O92LHrb3B/jTa9jHG0IHgAAcAAAAHhWNBIAAAAAAAAAACAd" +
-    "AACrAAAAcAAAACsAAAAcAwAAJQAAAMgDAAAKAAAAhAUAADkAAADUBQAAAgAAAKAHAAAgFgAA" +
-    "6AcAAMARAADzEQAAIxIAACwSAAA0EgAAPBIAAEQSAABMEgAAVBIAAFwSAABkEgAAbBIAAHMS" +
-    "AAB2EgAAgBIAAIgSAACMEgAAjxIAAJISAACsEgAArxIAALISAAC1EgAAuRIAAMgSAADLEgAA" +
-    "zhIAANISAADWEgAA2hIAAN4SAADiEgAA5hIAAOwSAADxEgAA9xIAACITAABLEwAATxMAAIQT" +
-    "AAC3EwAA6BMAAAwUAAAsFAAATxQAAG4UAACKFAAAoRQAAL0UAADQFAAA5RQAAPkUAAANFQAA" +
-    "KBUAADwVAABQFQAAaBUAAIEVAACYFQAAtRUAANoVAAD7FQAAJBYAAEYWAABlFgAAixYAALgW" +
-    "AADLFgAAzhYAANQWAAAAFwAAJhcAACkXAAAuFwAAMxcAADgXAAA9FwAAQRcAAEYXAABLFwAA" +
-    "TxcAAFQXAABZFwAAXRcAAGcXAABqFwAAbhcAAIIXAACXFwAArBcAAMoXAAD4FwAABRgAAA0Y" +
-    "AAAcGAAAKhgAAD0YAABQGAAAYxgAAHYYAACJGAAAnBgAAK8YAADDGAAA1BgAAOsYAAD3GAAA" +
-    "CxkAABIZAAAWGQAAGhkAACMZAAAnGQAAKxkAADMZAAA7GQAAPxkAAEMZAABSGQAAZhkAAHUZ" +
-    "AAB9GQAAgRkAAIUZAACRGQAAmRkAAJ4ZAACvGQAAvxkAAMIZAADGGQAAyhkAANEZAADfGQAA" +
-    "8BkAAP4ZAAAIGgAAHBoAACIaAAAoGgAALBoAADAaAAA+GgAAShoAAE4aAABUGgAAXxoAAGga" +
-    "AABrGgAAcBoAAHMaAACDGgAAjBoAAJgaAACdGgAAoRoAAKUaAACqGgAAtRoAALwaAADHGgAA" +
-    "zRoAANMaAADgGgAA6RoAAPMaAAD5GgAAABsAAAkbAAAQGwAAGRsAABAAAAARAAAAEwAAABQA" +
-    "AAAVAAAAGAAAACMAAAAkAAAAJgAAACcAAAAoAAAAKQAAACoAAAArAAAALAAAAC0AAAAuAAAA" +
-    "LwAAADAAAAAxAAAAMgAAADMAAAA0AAAANQAAADYAAAA4AAAAOQAAADoAAAA7AAAAPAAAAD0A" +
-    "AAA+AAAAPwAAAEAAAABBAAAAQwAAAEcAAABUAAAAVgAAAFcAAABYAAAAWQAAAFoAAAAVAAAA" +
-    "BAAAAAAAAAAWAAAABAAAAPgQAAAhAAAAEAAAAAARAAAZAAAAEwAAAAAAAAAdAAAAEwAAAPgQ" +
-    "AAAZAAAAFAAAAAAAAAAZAAAAFQAAAAAAAAAaAAAAFgAAAAgRAAAbAAAAFgAAABARAAAcAAAA" +
-    "FgAAABgRAAAdAAAAFgAAAPgQAAAeAAAAFgAAACARAAAfAAAAFgAAACgRAAAfAAAAFgAAADAR" +
-    "AAAlAAAAFgAAADgRAAAiAAAAGwAAAEARAAAiAAAAHQAAAEwRAAAgAAAAHQAAAFgRAAAgAAAA" +
-    "HQAAAGQRAAAZAAAAIAAAAAAAAAAZAAAAIgAAAAAAAABHAAAAJAAAAAAAAABIAAAAJAAAAHAR" +
-    "AABJAAAAJAAAAHgRAABKAAAAJAAAAIARAABLAAAAJAAAAIgRAABMAAAAJAAAAPgQAABNAAAA" +
-    "JAAAAJARAABOAAAAJAAAAJgRAABPAAAAJAAAACgRAABQAAAAJAAAAKARAABPAAAAJAAAADAR" +
-    "AABQAAAAJAAAAKgRAABPAAAAJAAAALARAABRAAAAJAAAALgRAABSAAAAJAAAADgRAABVAAAA" +
-    "JQAAACgRAAAHAAQAQgAAAAcAIQBuAAAABwAqAHEAAAAHACkAhgAAAAcAIgCRAAAABwAqAJ8A" +
-    "AAAHABkAogAAAAoACgAXAAAAEwASAEQAAAAXABAAlAAAAAYAFQAOAAAABgADAIQAAAAGAAUA" +
-    "hAAAAAcAFAALAAAABwAVAA0AAAAHABUADgAAAAcAFgBeAAAABwAXAF4AAAAHABgAXgAAAAcA" +
-    "GQBeAAAABwAbAF4AAAAHABwAXgAAAAcAHgBeAAAABwAgAF4AAAAHACIAXgAAAAcAHgBnAAAA" +
-    "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" +
-    "dEludm9rZUN1c3RvbVdpdGhDb25jdXJyZW50VGhyZWFkcyQxOwAnTFRlc3RJbnZva2VDdXN0" +
-    "b21XaXRoQ29uY3VycmVudFRocmVhZHM7AAJMWgAzTGNvbS9hbmRyb2lkL2phY2svYW5ub3Rh" +
-    "dGlvbnMvQ2FsbGVkQnlJbnZva2VDdXN0b207ADFMY29tL2FuZHJvaWQvamFjay9hbm5vdGF0" +
-    "aW9ucy9MaW5rZXJNZXRob2RIYW5kbGU7AC9MY29tL2FuZHJvaWQvamFjay9hbm5vdGF0aW9u" +
-    "cy9NZXRob2RIYW5kbGVLaW5kOwAiTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xvc2luZ0NsYXNz" +
-    "OwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ACFMZGFsdmlrL2Fubm90YXRpb24v" +
-    "TWVtYmVyQ2xhc3NlczsAHUxkYWx2aWsvYW5ub3RhdGlvbi9TaWduYXR1cmU7ABpMZGFsdmlr" +
-    "L2Fubm90YXRpb24vVGhyb3dzOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABpMamF2YS9sYW5n" +
-    "L0Fzc2VydGlvbkVycm9yOwARTGphdmEvbGFuZy9DbGFzczsAE0xqYXZhL2xhbmcvSW50ZWdl" +
-    "cjsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABlMamF2YS9sYW5n" +
-    "L1N0cmluZ0J1aWxkZXI7ABJMamF2YS9sYW5nL1N5c3RlbTsAEkxqYXZhL2xhbmcvVGhyZWFk" +
-    "OwAWTGphdmEvbGFuZy9UaHJlYWRMb2NhbAAXTGphdmEvbGFuZy9UaHJlYWRMb2NhbDsAFUxq" +
-    "YXZhL2xhbmcvVGhyb3dhYmxlOwAbTGphdmEvbGFuZy9pbnZva2UvQ2FsbFNpdGU7ACNMamF2" +
-    "YS9sYW5nL2ludm9rZS9Db25zdGFudENhbGxTaXRlOwAfTGphdmEvbGFuZy9pbnZva2UvTWV0" +
-    "aG9kSGFuZGxlOwAnTGphdmEvbGFuZy9pbnZva2UvTWV0aG9kSGFuZGxlcyRMb29rdXA7ACBM" +
-    "amF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzOwAdTGphdmEvbGFuZy9pbnZva2UvTWV0" +
-    "aG9kVHlwZTsAJExqYXZhL3V0aWwvY29uY3VycmVudC9DeWNsaWNCYXJyaWVyOwArTGphdmEv" +
-    "dXRpbC9jb25jdXJyZW50L2F0b21pYy9BdG9taWNJbnRlZ2VyOwARTlVNQkVSX09GX1RIUkVB" +
-    "RFMAAVMABFRZUEUAKlRlc3RJbnZva2VDdXN0b21XaXRoQ29uY3VycmVudFRocmVhZHMuamF2" +
-    "YQAkVGhyZWFkcyBkaWQgbm90IHRoZSBzYW1lIGNhbGwtc2l0ZXM6AAFWAANWQkIAA1ZDQwAD" +
-    "VkREAANWRkYAAlZJAANWSUkAA1ZKSgACVkwAA1ZMTAADVlNTAAJWWgAIV2lubmVycyAAAVoA" +
-    "AlpMABJbTGphdmEvbGFuZy9DbGFzczsAE1tMamF2YS9sYW5nL09iamVjdDsAE1tMamF2YS9s" +
-    "YW5nL1RocmVhZDsAHFtMamF2YS9sYW5nL2ludm9rZS9DYWxsU2l0ZTsALFtMamF2YS91dGls" +
-    "L2NvbmN1cnJlbnQvYXRvbWljL0F0b21pY0ludGVnZXI7AAthY2Nlc3NGbGFncwAGYXBwZW5k" +
-    "AA1hcmd1bWVudFR5cGVzAAxhc3NlcnRFcXVhbHMAEWFzc2VydEVxdWFscyBiMTogABFhc3Nl" +
-    "cnRFcXVhbHMgYzE6IAARYXNzZXJ0RXF1YWxzIGQxOiAAEWFzc2VydEVxdWFscyBmMTogABFh" +
-    "c3NlcnRFcXVhbHMgaTE6IAARYXNzZXJ0RXF1YWxzIGwxOiAAEWFzc2VydEVxdWFscyBzMTog" +
-    "ABJhc3NlcnRFcXVhbHM6IG8xOiAAD2Fzc2VydE5vdEVxdWFscwAVYXNzZXJ0Tm90RXF1YWxz" +
-    "OiBvMTogAAphc3NlcnRUcnVlABJhc3NlcnRUcnVlIHZhbHVlOiAABWF3YWl0AAJiMQACYjIA" +
-    "B2JhcnJpZXIAAmMxAAJjMgAGY2FsbGVkAAZjYWxsZXIAAmQxAAJkMgANZHJvcEFyZ3VtZW50" +
-    "cwASZW1pdHRlcjogamFjay00LjI1AA1lbmNsb3NpbmdUeXBlAAZlcXVhbHMAAmYxAAJmMgAK" +
-    "ZmluZFN0YXRpYwAGZm9ybWF0AANnZXQAD2dldEFuZEluY3JlbWVudAAOZ2V0VGhyZWFkSW5k" +
-    "ZXgAAWkAAmkxAAJpMgAFaW5kZXgADGluaXRpYWxWYWx1ZQAPaW5zZXJ0QXJndW1lbnRzAAxp" +
-    "bnN0YW50aWF0ZWQACGludFZhbHVlABJpbnZva2VNZXRob2RIYW5kbGUABGpvaW4ABGtpbmQA" +
-    "AmwxAAJsMgAMbGlua2VyTWV0aG9kAAptZXRob2RUeXBlAAJtaAAEbmFtZQAJbmV4dEluZGV4" +
-    "AAdub3RVc2VkAAFvAANvdXQAAXAADnBhcmFtZXRlckNvdW50AAdwcmludGxuAApyZXR1cm5U" +
-    "eXBlAANydW4AAnMxAAJzMgADc2V0AAlzZXRDYWxsZWQABXN0YXJ0AAl0YXJnZXR0ZWQABHRl" +
-    "c3QABHRoaXMAC3RocmVhZEluZGV4AAd0aHJlYWRzAAh0b1N0cmluZwAEdHlwZQAFdmFsdWUA" +
-    "B3ZhbHVlT2YABXZvdGVzAAd3aW5uZXJzAAF4ACcABw4CWjsAKgAHDgAoAAcOACQAByyJWDVN" +
-    "TU0CaXcAOgAHDgCuAQJtbgcOPACzAQJwcQcOPADMAQJ0dQcOWgDHAQJ6ewcOWgC9AQKCAYMB" +
-    "Bw48AMIBAowBjQEHDloA0QEClAGWAQcOPLQA3QECmwGcAQcOLSClIAC4AQKbAZwBBw48ANcB" +
-    "ApQBlgEHDjzSAKgBAacBBw4tARoQAD0ABw4AXANzkQGPAQcsTAMBowEFaQMAkAEeeLTDpbR8" +
-    "W9IAQQGrAQcOAFMBhAEHDni0AHEAB1kBAQMAgQEFLZaTQS0DAaQBKTx4V0E8WEAeAwOqAQUe" +
-    "AwKpAQU8h6UtkUMBJBIthzx4ARQNOkMthzx4ARQNOkE8PABGAAcOWgMAqwEFPAACCwGmARgH" +
-    "AgwCWwQIkAEeAg4BpgEcBBc3FwwXMRcPAg0BpgEcARgGAg8BpgEcARgaAAgEXRwBGASIARwB" +
-    "HQkEXRwDGB4YFRggdxgHigEbB5ABF42QARedmAEYBAEEEAMWABedFQEAAAECAICABJgQAQSw" +
-    "EAHEINwQBwATAQAaARoBGgEaARoBGgEaA4gg+BABiIAEkBEBgoAE7BEBCYQSAQnkEgEJxBMB" +
-    "CagUAQmMFQEJ7BUBCdAWAQnIFwEJwBgBCaAZAQmcGgEK6BoBCpQbAQnoHAIK/BwBCbQdFAGU" +
-    "IQAAABMAAAAAAAAAAQAAAAAAAAABAAAAqwAAAHAAAAACAAAAKwAAABwDAAADAAAAJQAAAMgD" +
-    "AAAEAAAACgAAAIQFAAAFAAAAOQAAANQFAAAHAAAAAQAAAJwHAAAGAAAAAgAAAKAHAAAIAAAA" +
-    "AQAAAOAHAAADEAAABQAAAOgHAAABIAAAFwAAABgIAAAGIAAAAgAAALgQAAABEAAAFwAAAPgQ" +
-    "AAACIAAAqwAAAMARAAADIAAAFgAAABwbAAAEIAAABgAAAD0cAAAFIAAAAgAAAJkcAAAAIAAA" +
-    "AgAAAKMcAAAAEAAAAQAAACAdAAA=";
-}
diff --git a/test/952-invoke-custom/src-art/TestDataLinkerMethodMinimalArguments.java b/test/952-invoke-custom/src-art/TestDataLinkerMethodMinimalArguments.java
deleted file mode 100644
index 443a7af..0000000
--- a/test/952-invoke-custom/src-art/TestDataLinkerMethodMinimalArguments.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/* Generated by build-test.sh from TestLinkerMethodMinimalArguments.java */
-public class TestDataLinkerMethodMinimalArguments {
-  public static final String BASE64_DEX_FILE =
-    "ZGV4CjAzOADnZpVEc25JsNXLCW+vh64OuLf8RymAuINwFQAAcAAAAHhWNBIAAAAAAAAAAIgU" +
-    "AACBAAAAcAAAAB0AAAB0AgAAHAAAAOgCAAAHAAAAOAQAACIAAABwBAAAAQAAAIQFAADEDwAA" +
-    "rAUAAKAMAACjDAAApwwAAKoMAACyDAAAugwAAMIMAADKDAAA0gwAANoMAADiDAAA6gwAAPQM" +
-    "AAD8DAAA/wwAAAINAAAFDQAACA0AADENAABUDQAAZw0AAIoNAACbDQAAng0AAKMNAACyDQAA" +
-    "tQ0AALgNAAC8DQAAwA0AAMQNAADIDQAAzA0AANANAADWDQAA+g0AAP4NAAAzDgAAZg4AAJcO" +
-    "AACzDgAAyg4AAOsOAAAHDwAAGg8AAD4PAABSDwAAZg8AAIEPAACVDwAArA8AAMkPAADuDwAA" +
-    "DxAAADgQAABXEAAAgBAAAIMQAACqEAAA0RAAAAQRAAAHEQAADBEAABERAAAWEQAAGxEAACAR" +
-    "AAAmEQAAKxEAAC8RAAA0EQAAOREAAD0RAABAEQAARBEAAEcRAABMEQAAVBEAAGMRAABxEQAA" +
-    "hBEAAJcRAACqEQAAvREAANARAADjEQAA9hEAAAoSAAAWEgAAKhIAAC0SAAAxEgAANRIAADkS" +
-    "AAA9EgAARRIAAEkSAABNEgAAYRIAAHASAAB4EgAAfBIAAIASAACNEgAAmRIAAKsSAACvEgAA" +
-    "sxIAAMcSAADNEgAA0RIAANUSAADjEgAA/xIAAAsTAAATEwAAGRMAABwTAAAhEwAAJBMAAC0T" +
-    "AAA5EwAAPRMAAEETAABHEwAATRMAAFcTAABeEwAAYRMAAA0AAAAOAAAADwAAABAAAAAWAAAA" +
-    "GQAAACIAAAAkAAAAJQAAACYAAAAnAAAAKAAAACkAAAAqAAAAKwAAACwAAAAtAAAALgAAAC8A" +
-    "AAAwAAAAMQAAADIAAAAzAAAANAAAADUAAAA2AAAAOAAAADwAAABIAAAAFwAAAAQAAADsCwAA" +
-    "GgAAABEAAAAAAAAAGwAAABIAAAD0CwAAHAAAABIAAAD8CwAAHQAAABIAAAAEDAAAHgAAABIA" +
-    "AAAMDAAAHwAAABIAAAAUDAAAIAAAABIAAAAcDAAAIAAAABIAAAAkDAAAIwAAABIAAAAsDAAA" +
-    "IQAAABUAAAA0DAAAIQAAABcAAABADAAAPAAAABsAAAAAAAAAPQAAABsAAABMDAAAPgAAABsA" +
-    "AABUDAAAPwAAABsAAABcDAAAQAAAABsAAABkDAAAQQAAABsAAADsCwAAQgAAABsAAABsDAAA" +
-    "QwAAABsAAAB4DAAARAAAABsAAAAcDAAARQAAABsAAACADAAARAAAABsAAAAkDAAARQAAABsA" +
-    "AACIDAAARAAAABsAAACQDAAARgAAABsAAACYDAAARwAAABsAAAAsDAAASQAAABwAAAAcDAAA" +
-    "BgAEABEAAAAGAAQAEgAAAAYABAATAAAABgAEABQAAAAGAAQAaAAAAAkACQAYAAAAEwALAHUA" +
-    "AAAGAAwACwAAAAYADAAMAAAABgAAAEsAAAAGAA0ATgAAAAYADgBOAAAABgAPAE4AAAAGABAA" +
-    "TgAAAAYAEQBOAAAABgATAE4AAAAGABUATgAAAAYAFwBOAAAABgAZAE4AAAAGABoAVwAAAAYA" +
-    "CgBvAAAABgASAHsAAAALABYAdwAAAAwAFgAMAAAADQAUAAwAAAAPABYADAAAABAADAAMAAAA" +
-    "EAAbAGMAAAARABsAYwAAABIADAAMAAAAEgACAEwAAAASAAMATAAAABIABABMAAAAEgAFAEwA" +
-    "AAASAAYATAAAABIABwBMAAAAEgAIAEwAAAASAAkATAAAABIAAQB9AAAAFgAYAAwAAAAYAAsA" +
-    "ZwAAADIUAAAGAAAAAQAAABAAAAAAAAAAOQAAAMQLAAA5FAAAAAAAAAQAAAANAAAAAQAAAAIU" +
-    "AAABAAAAKhQAAAEAAAAAAAAAZBMAAA8AAAASAGcABABnAAIAEhBnAAAAEiBnAAEAEjBnAAMA" +
-    "DgAAAAEAAQABAAAAcBMAAAQAAABwEBMAAAAOAAQAAgACAAAAdRMAABoAAABgAAQAYAEDADMQ" +
-    "EwBiAAYAGwE6AAAAbiAPABAAIgAMABsBSwAAAHAgEAAQACcAkAACAw8ABQACAAIAAAB/EwAA" +
-    "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" +
-    "KQAGLCBiMjogAAYsIGMyOiAABiwgZDI6IAAGLCBmMjogAAYsIGkyOiAABiwgbDI6IAAGLCBv" +
-    "MjogAAYsIHMyOiAACDxjbGluaXQ+AAY8aW5pdD4AAUIAAUMAAUQAAUYAJ0ZBSUxVUkVfVFlQ" +
-    "RV9MSU5LRVJfTUVUSE9EX1JFVFVSTlNfTlVMTAAhRkFJTFVSRV9UWVBFX0xJTktFUl9NRVRI" +
-    "T0RfVEhST1dTABFGQUlMVVJFX1RZUEVfTk9ORQAhRkFJTFVSRV9UWVBFX1RBUkdFVF9NRVRI" +
-    "T0RfVEhST1dTAA9GYWlsdXJlIFR5cGUgKyAAAUkAA0lJSQANSU5WT0tFX1NUQVRJQwABSgAB" +
-    "TAACTEMAAkxEAAJMRgACTEkAAkxKAAJMTAAETExMTAAiTFRlc3RMaW5rZXJNZXRob2RNaW5p" +
-    "bWFsQXJndW1lbnRzOwACTFoAM0xjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL0NhbGxl" +
-    "ZEJ5SW52b2tlQ3VzdG9tOwAxTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMvTGlua2Vy" +
-    "TWV0aG9kSGFuZGxlOwAvTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMvTWV0aG9kSGFu" +
-    "ZGxlS2luZDsAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ABVMamF2YS9pby9QcmludFN0" +
-    "cmVhbTsAH0xqYXZhL2xhbmcvQXJpdGhtZXRpY0V4Y2VwdGlvbjsAGkxqYXZhL2xhbmcvQXNz" +
-    "ZXJ0aW9uRXJyb3I7ABFMamF2YS9sYW5nL0NsYXNzOwAiTGphdmEvbGFuZy9JbnN0YW50aWF0" +
-    "aW9uRXhjZXB0aW9uOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsA" +
-    "GUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsAEkxqYXZhL2xhbmcvU3lzdGVtOwAVTGphdmEv" +
-    "bGFuZy9UaHJvd2FibGU7ABtMamF2YS9sYW5nL2ludm9rZS9DYWxsU2l0ZTsAI0xqYXZhL2xh" +
-    "bmcvaW52b2tlL0NvbnN0YW50Q2FsbFNpdGU7AB9MamF2YS9sYW5nL2ludm9rZS9NZXRob2RI" +
-    "YW5kbGU7ACdMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzJExvb2t1cDsAHUxqYXZh" +
-    "L2xhbmcvaW52b2tlL01ldGhvZFR5cGU7ACdSZXR1cm5pbmcgbnVsbCBpbnN0ZWFkIG9mIENh" +
-    "bGxTaXRlIGZvciAAAVMAJVRlc3RMaW5rZXJNZXRob2RNaW5pbWFsQXJndW1lbnRzLmphdmEA" +
-    "JVRocm93aW5nIEFyaXRobWV0aWNFeGNlcHRpb24gaW4gYWRkKCkAMVRocm93aW5nIEluc3Rh" +
-    "bnRpYXRpb25FeGNlcHRpb24gaW4gbGlua2VyTWV0aG9kKCkAAVYAA1ZCQgADVkNDAANWREQA" +
-    "A1ZGRgADVklJAARWSUlJAANWSkoAAlZMAANWTEwAA1ZTUwACVloAAVoAAlpMAAFhAANhZGQA" +
-    "BmFwcGVuZAANYXJndW1lbnRUeXBlcwAMYXNzZXJ0RXF1YWxzABFhc3NlcnRFcXVhbHMgYjE6" +
-    "IAARYXNzZXJ0RXF1YWxzIGMxOiAAEWFzc2VydEVxdWFscyBkMTogABFhc3NlcnRFcXVhbHMg" +
-    "ZjE6IAARYXNzZXJ0RXF1YWxzIGkxOiAAEWFzc2VydEVxdWFscyBsMTogABFhc3NlcnRFcXVh" +
-    "bHMgczE6IAASYXNzZXJ0RXF1YWxzOiBvMTogAAphc3NlcnRUcnVlABJhc3NlcnRUcnVlIHZh" +
-    "bHVlOiAAAWIAAmIxAAJiMgACYzEAAmMyAAZjYWxsZXIAAmQxAAJkMgASZW1pdHRlcjogamFj" +
-    "ay00LjI1AA1lbmNsb3NpbmdUeXBlAAZlcXVhbHMAAmYxAAJmMgALZmFpbHVyZVR5cGUACmZp" +
-    "bmRTdGF0aWMAEGZvcmNlRmFpbHVyZVR5cGUAAmkxAAJpMgASaW52b2tlTWV0aG9kSGFuZGxl" +
-    "AARraW5kAAJsMQACbDIADGxpbmtlck1ldGhvZAAabGlua2VyTWV0aG9kIGZhaWx1cmUgdHlw" +
-    "ZSAACm1ldGhvZFR5cGUABm1oX2FkZAAEbmFtZQABbwADb3V0AAFwAAdwcmludGxuAApyZXR1" +
-    "cm5UeXBlAAJzMQACczIABHRlc3QABHRoaXMACHRvU3RyaW5nAAV2YWx1ZQABeAABeQAeAAcd" +
-    "Li08PAJ5OwAcAAcOAC8CS1oHDmmHlwBWAltcBw48AFsCXV4HDjwAdAJgYQcOWgBvAmVmBw5a" +
-    "AGUCamsHDjwAagJubwcOWgB5AnV3Bw48tAB/Anp7Bw4tIKUgAGACensHDjwAUAF/Bw4tARoQ" +
-    "ADkDX3RyBw4BGxBpAwBzGGkBJA8taYeXAEgDZ4ABgQEHLId4LZYBLw8CeywtAAAHBE0cAhgE" +
-    "GARrHAEdCARNHAMYGBgRGBliGAZsGwVzF29zF0t4GAQCCgF+HAEYFAMWABdLFQAFAA8AAAoB" +
-    "CgEKAQoBCgCIgAS8CwGBgATsCwEKhAwBCcgMAQmoDQEJiA4BCewOAQnQDwEJsBABCZQRAQmM" +
-    "EgEJhBMBCeQTAQqwFAEJlBYAEwAAAAAAAAABAAAAAAAAAAEAAACBAAAAcAAAAAIAAAAdAAAA" +
-    "dAIAAAMAAAAcAAAA6AIAAAQAAAAHAAAAOAQAAAUAAAAiAAAAcAQAAAcAAAABAAAAgAUAAAYA" +
-    "AAABAAAAhAUAAAgAAAABAAAApAUAAAMQAAACAAAArAUAAAEgAAAPAAAAvAUAAAYgAAABAAAA" +
-    "xAsAAAEQAAAVAAAA7AsAAAIgAACBAAAAoAwAAAMgAAAPAAAAZBMAAAQgAAACAAAAAhQAAAUg" +
-    "AAABAAAAMhQAAAAgAAABAAAAORQAAAAQAAABAAAAiBQAAA==";
-}
diff --git a/test/952-invoke-custom/src-art/TestDataLinkerMethodMultipleArgumentTypes.java b/test/952-invoke-custom/src-art/TestDataLinkerMethodMultipleArgumentTypes.java
deleted file mode 100644
index b96e184..0000000
--- a/test/952-invoke-custom/src-art/TestDataLinkerMethodMultipleArgumentTypes.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/* Generated by build-test.sh from TestLinkerMethodMultipleArgumentTypes.java */
-public class TestDataLinkerMethodMultipleArgumentTypes {
-  public static final String BASE64_DEX_FILE =
-    "ZGV4CjAzOADmj8ccx56N3pWZ9IunuZvI0eWD+wmFmSnEFQAAcAAAAHhWNBIAAAAAAAAAANwU" +
-    "AACTAAAAcAAAAB0AAAC8AgAAHQAAADADAAADAAAAjAQAACIAAACkBAAAAQAAALgFAADkDwAA" +
-    "4AUAAEQMAABHDAAASgwAAFIMAABaDAAAYgwAAGoMAAByDAAAegwAAIIMAACKDAAAkgwAAJwM" +
-    "AACkDAAApwwAAKoMAACtDAAAsAwAAMYMAADNDAAA0AwAANUMAADkDAAA5wwAAOoMAADuDAAA" +
-    "8gwAAPYMAAD6DAAA/gwAAAINAAAIDQAAGA0AAEENAABFDQAAeg0AAKMNAADWDQAABw4AACYO" +
-    "AABCDgAATA4AAGMOAAB/DgAAkQ4AAKQOAAC6DgAAzg4AAOIOAAD9DgAAEQ8AACgPAABFDwAA" +
-    "ag8AAIsPAAC0DwAA0w8AANYPAAACEAAABRAAAAoQAAAPEAAAFBAAABkQAAAdEAAAIhAAACcQ" +
-    "AAArEAAAMBAAADUQAAA5EAAAPBAAAEUQAABJEAAATBAAAFEQAABZEAAAaBAAAHYQAACJEAAA" +
-    "nBAAAK8QAADCEAAA1RAAAOgQAAD7EAAADxEAABsRAAAvEQAAMhEAADYRAAA6EQAASBEAAFsR" +
-    "AABmEQAAahEAAG4RAAB2EQAAgREAAI0RAACREQAAlREAAKIRAAC2EQAAxREAAM0RAADREQAA" +
-    "1REAAOERAADtEQAA8REAAPURAAD/EQAAExIAABkSAAAdEgAAIRIAAC8SAAA6EgAAURIAAF0S" +
-    "AABlEgAAaxIAAG4SAABzEgAAdhIAAH8SAACLEgAAjxIAAJMSAACfEgAArBIAALISAAC4EgAA" +
-    "whIAAMYSAADLEgAAzxIAANMSAADXEgAA2xIAAN8SAADjEgAA5xIAAOsSAADyEgAA9RIAAA0A" +
-    "AAAOAAAADwAAABAAAAATAAAAFgAAACAAAAAiAAAAIwAAACQAAAAlAAAAJgAAACcAAAApAAAA" +
-    "KgAAACwAAAAuAAAALwAAADAAAAAxAAAAMgAAADMAAAA0AAAANQAAADYAAAA3AAAAOAAAADoA" +
-    "AABGAAAAEwAAAAQAAAAAAAAAFAAAAAQAAACICwAAFwAAABEAAAAAAAAAGAAAABIAAACQCwAA" +
-    "GQAAABIAAACYCwAAGgAAABIAAACgCwAAGwAAABIAAACoCwAAHAAAABIAAACwCwAAHQAAABIA" +
-    "AAC4CwAAHQAAABIAAADACwAAIQAAABIAAADICwAAHwAAABUAAADQCwAAHgAAABcAAADwCwAA" +
-    "OgAAABsAAAAAAAAAOwAAABsAAAD8CwAAPAAAABsAAAAEDAAAPQAAABsAAAAMDAAAPgAAABsA" +
-    "AAAUDAAAPwAAABsAAACoCwAAQAAAABsAAACICwAAQQAAABsAAAAcDAAAQgAAABsAAAC4CwAA" +
-    "QwAAABsAAAAkDAAAQgAAABsAAADACwAAQwAAABsAAAAsDAAAQgAAABsAAAA0DAAARAAAABsA" +
-    "AAA8DAAARQAAABsAAADICwAASAAAABwAAAC4CwAABgAEAFwAAAAKAAoAFQAAABMADQB7AAAA" +
-    "BgANAAsAAAAGAA0ADAAAAAYAAAARAAAABgABAEoAAAAGAA4ATQAAAAYADwBNAAAABgAQAE0A" +
-    "AAAGABEATQAAAAYAEwBNAAAABgAUAE0AAAAGABYATQAAAAYAGABNAAAABgAaAE0AAAAGABsA" +
-    "VgAAAAYACwB0AAAABgATAIMAAAANABIAfQAAAA0AFwB9AAAADgAVAAwAAAAQAA0ADAAAABAA" +
-    "HABoAAAAEQAcAGgAAAASAA0ADAAAABIAAwBLAAAAEgAEAEsAAAASAAUASwAAABIABgBLAAAA" +
-    "EgAHAEsAAAASAAgASwAAABIACQBLAAAAEgAKAEsAAAASAAIAhQAAABYAGQAMAAAAGAAMAGsA" +
-    "AABpFAAABgAAAAEAAAAQAAAAAAAAADkAAABgCwAAkhQAAAAAAAAEAAAADgAAAAEAAACpEwAA" +
-    "AgAAAEcUAABgFAAAAQAAAGAUAAABAAAAAAAAAPgSAAAEAAAAEgBnAAAADgABAAEAAQAAAP4S" +
-    "AAAEAAAAcBATAAAADgADAAIAAAAAAAMTAAADAAAAkAABAg8AAAAFAAIAAgAAAAoTAAAoAAAA" +
-    "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" +
-    "QwABRAABRgAUR2V0Qm9vdHN0cmFwUnVuQ291bnQABUhlbGxvAAFJAANJSUkADUlOVk9LRV9T" +
-    "VEFUSUMAAUoAAUwAAkxDAAJMRAACTEYAAkxJAAJMSgACTEwABExMTEwADkxMTExaQkNTSUZE" +
-    "TExKACdMVGVzdExpbmtlck1ldGhvZE11bHRpcGxlQXJndW1lbnRUeXBlczsAAkxaADNMY29t" +
-    "L2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9DYWxsZWRCeUludm9rZUN1c3RvbTsAJ0xjb20v" +
-    "YW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL0NvbnN0YW50OwAxTGNvbS9hbmRyb2lkL2phY2sv" +
-    "YW5ub3RhdGlvbnMvTGlua2VyTWV0aG9kSGFuZGxlOwAvTGNvbS9hbmRyb2lkL2phY2svYW5u" +
-    "b3RhdGlvbnMvTWV0aG9kSGFuZGxlS2luZDsAHUxkYWx2aWsvYW5ub3RhdGlvbi9TaWduYXR1" +
-    "cmU7ABpMZGFsdmlrL2Fubm90YXRpb24vVGhyb3dzOwAITGlua2luZyAAFUxqYXZhL2lvL1By" +
-    "aW50U3RyZWFtOwAaTGphdmEvbGFuZy9Bc3NlcnRpb25FcnJvcjsAEExqYXZhL2xhbmcvQ2xh" +
-    "c3MAEUxqYXZhL2xhbmcvQ2xhc3M7ABRMamF2YS9sYW5nL0NsYXNzPCo+OwASTGphdmEvbGFu" +
-    "Zy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRl" +
-    "cjsAEkxqYXZhL2xhbmcvU3lzdGVtOwAVTGphdmEvbGFuZy9UaHJvd2FibGU7ABtMamF2YS9s" +
-    "YW5nL2ludm9rZS9DYWxsU2l0ZTsAI0xqYXZhL2xhbmcvaW52b2tlL0NvbnN0YW50Q2FsbFNp" +
-    "dGU7AB9MamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGU7ACdMamF2YS9sYW5nL2ludm9r" +
-    "ZS9NZXRob2RIYW5kbGVzJExvb2t1cDsAHUxqYXZhL2xhbmcvaW52b2tlL01ldGhvZFR5cGU7" +
-    "AAFTACpUZXN0TGlua2VyTWV0aG9kTXVsdGlwbGVBcmd1bWVudFR5cGVzLmphdmEAAVYAA1ZC" +
-    "QgADVkNDAANWREQAA1ZGRgACVkkAA1ZJSQADVkpKAAJWTAADVkxMAANWU1MAAlZaAAFaAAda" +
-    "QkNTSUZEAAJaTAABYQADYWRkAAZhcHBlbmQADWFyZ3VtZW50VHlwZXMADGFzc2VydEVxdWFs" +
-    "cwARYXNzZXJ0RXF1YWxzIGIxOiAAEWFzc2VydEVxdWFscyBjMTogABFhc3NlcnRFcXVhbHMg" +
-    "ZDE6IAARYXNzZXJ0RXF1YWxzIGYxOiAAEWFzc2VydEVxdWFscyBpMTogABFhc3NlcnRFcXVh" +
-    "bHMgbDE6IAARYXNzZXJ0RXF1YWxzIHMxOiAAEmFzc2VydEVxdWFsczogbzE6IAAKYXNzZXJ0" +
-    "VHJ1ZQASYXNzZXJ0VHJ1ZSB2YWx1ZTogAAFiAAJiMQACYjIADGJvb2xlYW5WYWx1ZQARYm9v" +
-    "dHN0cmFwUnVuQ291bnQACWJ5dGVWYWx1ZQACYzEAAmMyAAZjYWxsZXIACWNoYXJWYWx1ZQAK" +
-    "Y2xhc3NWYWx1ZQACZDEAAmQyAAtkb3VibGVWYWx1ZQASZW1pdHRlcjogamFjay00LjI1AA1l" +
-    "bmNsb3NpbmdUeXBlAAZlcXVhbHMAAmYxAAJmMgAKZmluZFN0YXRpYwAKZmxvYXRWYWx1ZQAC" +
-    "aTEAAmkyAAhpbnRWYWx1ZQASaW52b2tlTWV0aG9kSGFuZGxlAARraW5kAAJsMQACbDIADGxp" +
-    "bmtlck1ldGhvZAAJbG9uZ1ZhbHVlABVtZXRob2RIYW5kbGVFeHRyYUFyZ3MACm1ldGhvZFR5" +
-    "cGUABm1oX2FkZAAEbmFtZQABbwADb3V0AAFwAAdwcmludGxuAApyZXR1cm5UeXBlAAJzMQAC" +
-    "czIACnNob3J0VmFsdWUAC3N0cmluZ1ZhbHVlAAR0ZXN0AAR0aGlzAAh0b1N0cmluZwACdjEA" +
-    "A3YxMAACdjIAAnYzAAJ2NAACdjUAAnY2AAJ2NwACdjgAAnY5AAV2YWx1ZQABeAABeQAeAAcO" +
-    "OQAcAAcOADECSlkHDgBZAlpbBw48AF4CX2AHDjwAdwJkZQcOWgByAmprBw5aAGgCbm8HDjwA" +
-    "bQJzdAcOWgB8Ant9Bw48tACCAQKAAYEBBw4tIKUgAGMCgAGBAQcOPABTAZEBBw4tARoQADkN" +
-    "YXp4hwGJAYoBiwGMAY0BjgGPAQCIAQQTkAEQLgcOASQPPEtaWktppYd4iGkDAnkYAE4CkgGT" +
-    "AQcOlngASgAHDgAABwVMHAIYBBgEcBwBHQkETBwNGBgYERgZGBwYABgBGBoYBBgDGAIYERgP" +
-    "GAVnGAZxGwF5F3R2HAodCAFbHAE/HQgBXRwBAAEdCAFhHAEDYR0IAYEBHAEiAAQdCAFvHAEE" +
-    "AR0IAWwcAXCamTFBHQgBZRwB8ZqZmZmZmQFAHQgBggEcARcSHQgBYhwBGAYdCAF1HAFmFc1b" +
-    "B3kXSn4YBAILAZABHAkXARc2Fy8XNxdHFy8XKxcKFzMCDAGQARwBGBQNFgAXShUBBAEEAQRh" +
-    "JAAEBAFwmpkxQfGamZmZmZkBQBcSGAZmFc1bBwEADwEACgCIgAT8CwGBgASUDAIKrAwBCcQM" +
-    "AQmkDQEJhA4BCegOAQnMDwEJrBABCZARAQmIEgEJgBMBCeATAQqsFAEJlBYCAcgWEwAAAAAA" +
-    "AAABAAAAAAAAAAEAAACTAAAAcAAAAAIAAAAdAAAAvAIAAAMAAAAdAAAAMAMAAAQAAAADAAAA" +
-    "jAQAAAUAAAAiAAAApAQAAAcAAAABAAAAtAUAAAYAAAABAAAAuAUAAAgAAAABAAAA2AUAAAMQ" +
-    "AAADAAAA4AUAAAEgAAAQAAAA/AUAAAYgAAABAAAAYAsAAAEQAAAUAAAAiAsAAAIgAACTAAAA" +
-    "RAwAAAMgAAAQAAAA+BIAAAQgAAADAAAAqRMAAAUgAAABAAAAaRQAAAAgAAABAAAAkhQAAAAQ" +
-    "AAABAAAA3BQAAA==";
-}
diff --git a/test/952-invoke-custom/src/Main.java b/test/952-invoke-custom/src/Main.java
new file mode 100644
index 0000000..2e1db82
--- /dev/null
+++ b/test/952-invoke-custom/src/Main.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.MutableCallSite;
+
+public class Main extends TestBase {
+
+    private static void TestUninitializedCallSite() throws Throwable {
+        CallSite callSite = new MutableCallSite(MethodType.methodType(int.class));
+        try {
+            callSite.getTarget().invoke();
+            fail();
+        } catch (IllegalStateException e) {
+            System.out.println("Caught exception from uninitialized call site");
+        }
+
+        callSite = new MutableCallSite(MethodType.methodType(String.class, int.class, char.class));
+        try {
+            callSite.getTarget().invoke(1535, 'd');
+            fail();
+        } catch (IllegalStateException e) {
+            System.out.println("Caught exception from uninitialized call site");
+        }
+    }
+
+    private static void TestLinkerMethodMultipleArgumentTypes() throws Throwable {
+        TestLinkerMethodMultipleArgumentTypes.test(33, 67);
+        TestLinkerMethodMultipleArgumentTypes.test(-10000, 1000);
+        TestLinkerMethodMultipleArgumentTypes.test(-1000, 10000);
+    }
+
+    private static void TestLinkerMethodMinimalArguments() throws Throwable {
+        try {
+            TestLinkerMethodMinimalArguments.test(
+                    TestLinkerMethodMinimalArguments.FAILURE_TYPE_LINKER_METHOD_RETURNS_NULL,
+                    10,
+                    10);
+            assertNotReached();
+        } catch (BootstrapMethodError e) {
+            assertEquals(e.getCause().getClass(), ClassCastException.class);
+        }
+
+        try {
+            TestLinkerMethodMinimalArguments.test(
+                    TestLinkerMethodMinimalArguments.FAILURE_TYPE_LINKER_METHOD_THROWS, 10, 11);
+            assertNotReached();
+        } catch (BootstrapMethodError e) {
+            assertEquals(e.getCause().getClass(), InstantiationException.class);
+        }
+
+        try {
+            TestLinkerMethodMinimalArguments.test(
+                    TestLinkerMethodMinimalArguments.FAILURE_TYPE_TARGET_METHOD_THROWS, 10, 12);
+            assertNotReached();
+        } catch (ArithmeticException e) {
+        }
+
+        TestLinkerMethodMinimalArguments.test(
+                TestLinkerMethodMinimalArguments.FAILURE_TYPE_NONE, 10, 13);
+    }
+
+    private static void TestInvokeCustomWithConcurrentThreads() throws Throwable {
+        // This is a concurrency test that attempts to run invoke-custom on the same
+        // call site.
+        TestInvokeCustomWithConcurrentThreads.test();
+    }
+
+    public static void main(String[] args) throws Throwable {
+        TestUninitializedCallSite();
+        TestLinkerMethodMinimalArguments();
+        TestLinkerMethodMultipleArgumentTypes();
+        TestLinkerUnrelatedBSM.test();
+        TestInvokeCustomWithConcurrentThreads();
+    }
+}
diff --git a/test/952-invoke-custom/src/TestBase.java b/test/952-invoke-custom/src/TestBase.java
new file mode 100644
index 0000000..25ec9b6
--- /dev/null
+++ b/test/952-invoke-custom/src/TestBase.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Objects;
+
+abstract class TestBase {
+    static void assertTrue(boolean value) {
+        if (!value) {
+            throw new AssertionError("assertTrue value: " + value);
+        }
+    }
+
+    static void assertEquals(byte b1, byte b2) {
+        if (b1 == b2) {
+            return;
+        }
+        throw new AssertionError("assertEquals b1: " + b1 + ", b2: " + b2);
+    }
+
+    static void assertEquals(char c1, char c2) {
+        if (c1 == c2) {
+            return;
+        }
+        throw new AssertionError("assertEquals c1: " + c1 + ", c2: " + c2);
+    }
+
+    static void assertEquals(short s1, short s2) {
+        if (s1 == s2) {
+            return;
+        }
+        throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
+    }
+
+    static void assertEquals(int i1, int i2) {
+        if (i1 == i2) {
+            return;
+        }
+        throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2);
+    }
+
+    static void assertEquals(long l1, long l2) {
+        if (l1 == l2) {
+            return;
+        }
+        throw new AssertionError("assertEquals l1: " + l1 + ", l2: " + l2);
+    }
+
+    static void assertEquals(float f1, float f2) {
+        if (f1 == f2) {
+            return;
+        }
+        throw new AssertionError("assertEquals f1: " + f1 + ", f2: " + f2);
+    }
+
+    static void assertEquals(double d1, double d2) {
+        if (d1 == d2) {
+            return;
+        }
+        throw new AssertionError("assertEquals d1: " + d1 + ", d2: " + d2);
+    }
+
+    static void assertEquals(Object o, Object p) {
+        if (!Objects.equals(o, p)) {
+            throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p);
+        }
+    }
+
+    static void assertNotEquals(Object o, Object p) {
+        if (Objects.equals(o, p)) {
+            throw new AssertionError("assertNotEquals: o1: " + o + ", o2: " + p);
+        }
+    }
+
+    static void assertNotReached() {
+        throw new AssertionError("Unreachable");
+    }
+
+    static void fail() {
+        System.out.println("fail");
+        Thread.dumpStack();
+    }
+}
diff --git a/test/952-invoke-custom/src/TestInvokeCustomWithConcurrentThreads.java b/test/952-invoke-custom/src/TestInvokeCustomWithConcurrentThreads.java
new file mode 100644
index 0000000..761d182
--- /dev/null
+++ b/test/952-invoke-custom/src/TestInvokeCustomWithConcurrentThreads.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import annotations.CalledByIndy;
+import annotations.LinkerMethodHandle;
+import annotations.MethodHandleKind;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class TestInvokeCustomWithConcurrentThreads extends TestBase implements Runnable {
+    private static final int NUMBER_OF_THREADS = 16;
+
+    private static final AtomicInteger nextIndex = new AtomicInteger(0);
+
+    private static final ThreadLocal<Integer> threadIndex =
+            new ThreadLocal<Integer>() {
+                @Override
+                protected Integer initialValue() {
+                    return nextIndex.getAndIncrement();
+                }
+            };
+
+    // Array of call sites instantiated, one per thread
+    private static final CallSite[] instantiated = new CallSite[NUMBER_OF_THREADS];
+
+    // Array of counters for how many times each instantiated call site is called
+    private static final AtomicInteger[] called = new AtomicInteger[NUMBER_OF_THREADS];
+
+    // Array of call site indicies of which call site a thread invoked
+    private static final AtomicInteger[] targetted = new AtomicInteger[NUMBER_OF_THREADS];
+
+    // Synchronization barrier all threads will wait on in the bootstrap method.
+    private static final CyclicBarrier barrier = new CyclicBarrier(NUMBER_OF_THREADS);
+
+    private TestInvokeCustomWithConcurrentThreads() {}
+
+    private static int getThreadIndex() {
+        return threadIndex.get().intValue();
+    }
+
+    public static int notUsed(int x) {
+        return x;
+    }
+
+    public void run() {
+        int x = setCalled(-1 /* argument dropped */);
+        notUsed(x);
+    }
+
+    @CalledByIndy(
+        invokeMethodHandle =
+                @LinkerMethodHandle(
+                    kind = MethodHandleKind.INVOKE_STATIC,
+                    enclosingType = TestInvokeCustomWithConcurrentThreads.class,
+                    name = "linkerMethod",
+                    argumentTypes = {MethodHandles.Lookup.class, String.class, MethodType.class}
+                ),
+        name = "setCalled",
+        returnType = int.class,
+        argumentTypes = {int.class}
+    )
+    private static int setCalled(int index) {
+        called[index].getAndIncrement();
+        targetted[getThreadIndex()].set(index);
+        return 0;
+    }
+
+    @SuppressWarnings("unused")
+    private static CallSite linkerMethod(
+            MethodHandles.Lookup caller, String name, MethodType methodType) throws Throwable {
+        MethodHandle mh =
+                caller.findStatic(TestInvokeCustomWithConcurrentThreads.class, name, methodType);
+        assertEquals(methodType, mh.type());
+        assertEquals(mh.type().parameterCount(), 1);
+        mh = MethodHandles.insertArguments(mh, 0, getThreadIndex());
+        mh = MethodHandles.dropArguments(mh, 0, int.class);
+        assertEquals(mh.type().parameterCount(), 1);
+        assertEquals(methodType, mh.type());
+
+        // Wait for all threads to be in this method.
+        // Multiple call sites should be created, but only one
+        // invoked.
+        barrier.await();
+
+        instantiated[getThreadIndex()] = new ConstantCallSite(mh);
+        return instantiated[getThreadIndex()];
+    }
+
+    public static void test() throws Throwable {
+        // Initialize counters for which call site gets invoked
+        for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+            called[i] = new AtomicInteger(0);
+            targetted[i] = new AtomicInteger(0);
+        }
+
+        // Run threads that each invoke-custom the call site
+        Thread[] threads = new Thread[NUMBER_OF_THREADS];
+        for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+            threads[i] = new Thread(new TestInvokeCustomWithConcurrentThreads());
+            threads[i].start();
+        }
+
+        // Wait for all threads to complete
+        for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+            threads[i].join();
+        }
+
+        // Check one call site instance won
+        int winners = 0;
+        int votes = 0;
+        for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+            assertNotEquals(instantiated[i], null);
+            if (called[i].get() != 0) {
+                winners++;
+                votes += called[i].get();
+            }
+        }
+
+        System.out.println("Winners " + winners + " Votes " + votes);
+
+        // We assert this below but output details when there's an error as
+        // it's non-deterministic.
+        if (winners != 1) {
+            System.out.println("Threads did not the same call-sites:");
+            for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+                System.out.format(
+                        " Thread % 2d invoked call site instance #%02d\n", i, targetted[i].get());
+            }
+        }
+
+        // We assert this below but output details when there's an error as
+        // it's non-deterministic.
+        if (votes != NUMBER_OF_THREADS) {
+            System.out.println("Call-sites invocations :");
+            for (int i = 0; i < NUMBER_OF_THREADS; ++i) {
+                System.out.format(
+                        " Call site instance #%02d was invoked % 2d times\n", i, called[i].get());
+            }
+        }
+
+        assertEquals(winners, 1);
+        assertEquals(votes, NUMBER_OF_THREADS);
+    }
+}
diff --git a/test/952-invoke-custom/src/TestLinkerMethodMinimalArguments.java b/test/952-invoke-custom/src/TestLinkerMethodMinimalArguments.java
new file mode 100644
index 0000000..74ac3cd
--- /dev/null
+++ b/test/952-invoke-custom/src/TestLinkerMethodMinimalArguments.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import annotations.CalledByIndy;
+import annotations.LinkerMethodHandle;
+import annotations.MethodHandleKind;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+public class TestLinkerMethodMinimalArguments extends TestBase {
+    private static int forceFailureType = 0;
+
+    static final int FAILURE_TYPE_NONE = 0;
+    static final int FAILURE_TYPE_LINKER_METHOD_RETURNS_NULL = 1;
+    static final int FAILURE_TYPE_LINKER_METHOD_THROWS = 2;
+    static final int FAILURE_TYPE_TARGET_METHOD_THROWS = 3;
+
+    @CalledByIndy(
+        invokeMethodHandle =
+                @LinkerMethodHandle(
+                    kind = MethodHandleKind.INVOKE_STATIC,
+                    enclosingType = TestLinkerMethodMinimalArguments.class,
+                    argumentTypes = {MethodHandles.Lookup.class, String.class, MethodType.class},
+                    name = "linkerMethod"
+                ),
+        name = "_add",
+        returnType = int.class,
+        argumentTypes = {int.class, int.class}
+    )
+    private static int add(int a, int b) {
+        assertNotReached();
+        return -1;
+    }
+
+    @SuppressWarnings("unused")
+    static int _add(int a, int b) {
+        if (forceFailureType == FAILURE_TYPE_TARGET_METHOD_THROWS) {
+            System.out.println("Throwing ArithmeticException in add()");
+            throw new ArithmeticException("add");
+        }
+        return a + b;
+    }
+
+    @SuppressWarnings("unused")
+    private static CallSite linkerMethod(
+            MethodHandles.Lookup caller, String name, MethodType methodType) throws Throwable {
+        System.out.println("linkerMethod failure type " + forceFailureType);
+        MethodHandle mh_add =
+                caller.findStatic(TestLinkerMethodMinimalArguments.class, name, methodType);
+        switch (forceFailureType) {
+            case FAILURE_TYPE_LINKER_METHOD_RETURNS_NULL:
+                System.out.println(
+                        "Returning null instead of CallSite for " + name + " " + methodType);
+                return null;
+            case FAILURE_TYPE_LINKER_METHOD_THROWS:
+                System.out.println("Throwing InstantiationException in linkerMethod()");
+                throw new InstantiationException("linkerMethod");
+            default:
+                return new ConstantCallSite(mh_add);
+        }
+    }
+
+    public static void test(int failureType, int x, int y) throws Throwable {
+        assertTrue(failureType >= FAILURE_TYPE_NONE);
+        assertTrue(failureType <= FAILURE_TYPE_TARGET_METHOD_THROWS);
+        forceFailureType = failureType;
+        assertEquals(x + y, add(x, y));
+        System.out.println("Failure Type + " + failureType + " (" + x + y + ")");
+    }
+}
diff --git a/test/952-invoke-custom/src/TestLinkerMethodMultipleArgumentTypes.java b/test/952-invoke-custom/src/TestLinkerMethodMultipleArgumentTypes.java
new file mode 100644
index 0000000..acb6986
--- /dev/null
+++ b/test/952-invoke-custom/src/TestLinkerMethodMultipleArgumentTypes.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import annotations.CalledByIndy;
+import annotations.Constant;
+import annotations.LinkerMethodHandle;
+import annotations.MethodHandleKind;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+public class TestLinkerMethodMultipleArgumentTypes extends TestBase {
+
+    private static int bootstrapRunCount = 0;
+
+    @CalledByIndy(
+        invokeMethodHandle =
+                @LinkerMethodHandle(
+                    kind = MethodHandleKind.INVOKE_STATIC,
+                    enclosingType = TestLinkerMethodMultipleArgumentTypes.class,
+                    name = "linkerMethod",
+                    argumentTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        int.class,
+                        int.class,
+                        int.class,
+                        int.class,
+                        int.class,
+                        float.class,
+                        double.class,
+                        String.class,
+                        Class.class,
+                        long.class
+                    }
+                ),
+        methodHandleExtraArgs = {
+            @Constant(intValue = -1),
+            @Constant(intValue = 1),
+            @Constant(intValue = (int) 'a'),
+            @Constant(intValue = 1024),
+            @Constant(intValue = 1),
+            @Constant(floatValue = 11.1f),
+            @Constant(doubleValue = 2.2),
+            @Constant(stringValue = "Hello"),
+            @Constant(classValue = TestLinkerMethodMultipleArgumentTypes.class),
+            @Constant(longValue = 123456789L)
+        },
+        name = "_add",
+        returnType = int.class,
+        argumentTypes = {int.class, int.class}
+    )
+    private static int add(int a, int b) {
+        assertNotReached();
+        return -1;
+    }
+
+    @SuppressWarnings("unused")
+    private static int _add(int a, int b) {
+        return a + b;
+    }
+
+    @SuppressWarnings("unused")
+    private static CallSite linkerMethod(
+            MethodHandles.Lookup caller,
+            String name,
+            MethodType methodType,
+            int v1,
+            int v2,
+            int v3,
+            int v4,
+            int v5,
+            float v6,
+            double v7,
+            String v8,
+            Class<?> v9,
+            long v10)
+            throws Throwable {
+        System.out.println("Linking " + name + " " + methodType);
+        assertEquals(-1, v1);
+        assertEquals(1, v2);
+        assertEquals('a', v3);
+        assertEquals(1024, v4);
+        assertEquals(1, v5);
+        assertEquals(11.1f, v6);
+        assertEquals(2.2, v7);
+        assertEquals("Hello", v8);
+        assertEquals(TestLinkerMethodMultipleArgumentTypes.class, v9);
+        assertEquals(123456789L, v10);
+        MethodHandle mh_add =
+                caller.findStatic(TestLinkerMethodMultipleArgumentTypes.class, name, methodType);
+        return new ConstantCallSite(mh_add);
+    }
+
+    public int GetBootstrapRunCount() {
+        return bootstrapRunCount;
+    }
+
+    public static void test(int x, int y) throws Throwable {
+        assertEquals(x + y, add(x, y));
+        System.out.println(x + y);
+    }
+}
diff --git a/test/952-invoke-custom/src/TestLinkerUnrelatedBSM.java b/test/952-invoke-custom/src/TestLinkerUnrelatedBSM.java
new file mode 100644
index 0000000..3a63b33
--- /dev/null
+++ b/test/952-invoke-custom/src/TestLinkerUnrelatedBSM.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import annotations.CalledByIndy;
+import annotations.Constant;
+import annotations.LinkerMethodHandle;
+import annotations.MethodHandleKind;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+class TestLinkerUnrelatedBSM extends TestBase {
+    @CalledByIndy(
+        invokeMethodHandle =
+                @LinkerMethodHandle(
+                    kind = MethodHandleKind.INVOKE_STATIC,
+                    enclosingType = UnrelatedBSM.class,
+                    argumentTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        Class.class
+                    },
+                    name = "bsm"
+                ),
+        methodHandleExtraArgs = {@Constant(classValue = TestLinkerUnrelatedBSM.class)},
+        name = "_addf",
+        returnType = float.class,
+        argumentTypes = {float.class, float.class}
+    )
+    private static float addf(float a, float b) {
+        assertNotReached();
+        return Float.MIN_VALUE;
+    }
+
+    public static float _addf(float a, float b) {
+        return a + b;
+    }
+
+    @CalledByIndy(
+        invokeMethodHandle =
+                @LinkerMethodHandle(
+                    kind = MethodHandleKind.INVOKE_STATIC,
+                    enclosingType = UnrelatedBSM.class,
+                    argumentTypes = {
+                        MethodHandles.Lookup.class,
+                        String.class,
+                        MethodType.class,
+                        Class.class
+                    },
+                    name = "bsm"
+                ),
+        methodHandleExtraArgs = {@Constant(classValue = TestLinkerUnrelatedBSM.class)},
+        name = "_subf",
+        returnType = float.class,
+        argumentTypes = {float.class, float.class}
+    )
+    private static float subf(float a, float b) {
+        assertNotReached();
+        return Float.MIN_VALUE;
+    }
+
+    private static float _subf(float a, float b) {
+        return a - b;
+    }
+
+    public static void test() {
+        System.out.println(TestLinkerUnrelatedBSM.class.getName());
+        assertEquals(2.5f, addf(2.0f, 0.5f));
+        assertEquals(1.5f, subf(2.0f, 0.5f));
+    }
+}
diff --git a/test/952-invoke-custom/src/UnrelatedBSM.java b/test/952-invoke-custom/src/UnrelatedBSM.java
new file mode 100644
index 0000000..1611a81
--- /dev/null
+++ b/test/952-invoke-custom/src/UnrelatedBSM.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+class UnrelatedBSM {
+    static CallSite bsm(
+            MethodHandles.Lookup lookup, String name, MethodType methodType, Class<?> target)
+            throws Throwable {
+        MethodHandle mh = lookup.findStatic(target, name, methodType);
+        return new ConstantCallSite(mh);
+    }
+}
diff --git a/test/952-invoke-custom/src/annotations/CalledByIndy.java b/test/952-invoke-custom/src/annotations/CalledByIndy.java
new file mode 100644
index 0000000..17b8259
--- /dev/null
+++ b/test/952-invoke-custom/src/annotations/CalledByIndy.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation can be set on method to specify that if this method is called then it must be
+ * called by an invokedynamic instruction.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface CalledByIndy {
+    LinkerMethodHandle[] invokeMethodHandle() default {};
+
+    LinkerFieldHandle[] fieldMethodHandle() default {};
+
+    String name();
+
+    Class<?> returnType() default void.class;
+
+    Class<?>[] argumentTypes() default {};
+
+    Constant[] methodHandleExtraArgs() default {};
+}
diff --git a/test/952-invoke-custom/src/annotations/Constant.java b/test/952-invoke-custom/src/annotations/Constant.java
new file mode 100644
index 0000000..7966a52
--- /dev/null
+++ b/test/952-invoke-custom/src/annotations/Constant.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Describes an annotation that allows passing a constant extra argument to a linker method. */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+public @interface Constant {
+    boolean[] booleanValue() default {};
+
+    byte[] byteValue() default {};
+
+    char[] charValue() default {};
+
+    short[] shortValue() default {};
+
+    int[] intValue() default {};
+
+    float[] floatValue() default {};
+
+    double[] doubleValue() default {};
+
+    long[] longValue() default {};
+
+    Class<?>[] classValue() default {};
+
+    String[] stringValue() default {};
+}
diff --git a/test/952-invoke-custom/src/annotations/LinkerFieldHandle.java b/test/952-invoke-custom/src/annotations/LinkerFieldHandle.java
new file mode 100644
index 0000000..a3efe24
--- /dev/null
+++ b/test/952-invoke-custom/src/annotations/LinkerFieldHandle.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.invoke.CallSite;
+
+/** Describe a linker method to a field. */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+public @interface LinkerFieldHandle {
+    MethodHandleKind kind();
+
+    Class<?> enclosingType();
+
+    String name();
+
+    Class<?> type() default CallSite.class;
+}
diff --git a/test/952-invoke-custom/src/annotations/LinkerMethodHandle.java b/test/952-invoke-custom/src/annotations/LinkerMethodHandle.java
new file mode 100644
index 0000000..e0e56c5
--- /dev/null
+++ b/test/952-invoke-custom/src/annotations/LinkerMethodHandle.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.invoke.CallSite;
+
+/** Describe a linker method to a method. */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+public @interface LinkerMethodHandle {
+    MethodHandleKind kind();
+
+    Class<?> enclosingType();
+
+    String name();
+
+    Class<?> returnType() default CallSite.class;
+
+    Class<?>[] argumentTypes() default {};
+}
diff --git a/test/952-invoke-custom/src/annotations/MethodHandleKind.java b/test/952-invoke-custom/src/annotations/MethodHandleKind.java
new file mode 100644
index 0000000..5847e2f
--- /dev/null
+++ b/test/952-invoke-custom/src/annotations/MethodHandleKind.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package annotations;
+
+/** MethodHandle invocations kinds supported by invokedynamic */
+public enum MethodHandleKind {
+    GET_FIELD,
+    GET_STATIC,
+    PUT_FIELD,
+    PUT_STATIC,
+    INVOKE_VIRTUAL,
+    INVOKE_STATIC,
+    INVOKE_SPECIAL,
+    INVOKE_CONSTRUCTOR,
+    INVOKE_INTERFACE
+}
diff --git a/test/952-invoke-custom/src/transformer/IndyTransformer.java b/test/952-invoke-custom/src/transformer/IndyTransformer.java
new file mode 100644
index 0000000..286c098
--- /dev/null
+++ b/test/952-invoke-custom/src/transformer/IndyTransformer.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package transformer;
+
+import annotations.CalledByIndy;
+import annotations.Constant;
+import annotations.LinkerFieldHandle;
+import annotations.LinkerMethodHandle;
+import annotations.MethodHandleKind;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * Class for inserting invoke-dynamic instructions in annotated Java class files.
+ *
+ * This class replaces static method invocations of annotated methods
+ * with invoke-dynamic instructions. Suppose a method is annotated as:
+ *
+ * @CalledByIndy(
+ *     invokeMethodHandle =
+ *         @LinkerMethodHandle(
+ *              kind = MethodHandleKind.INVOKE_STATIC,
+ *                  enclosingType = TestLinkerMethodMinimalArguments.class,
+ *                  argumentTypes = {MethodHandles.Lookup.class, String.class, MethodType.class},
+ *                  name = "linkerMethod"
+ *              ),
+ *     name = "magicAdd",
+ *     returnType = int.class,
+ *     argumentTypes = {int.class, int.class}
+ * )
+ * private int add(int x, int y) {
+ *    throw new UnsupportedOperationException(e);
+ * }
+ *
+ * private int magicAdd(int x, int y) {
+ *    return x + y;
+ * }
+ *
+ * Then invokestatic bytecodes targeting the add() method will be
+ * replaced invokedynamic instructions targetting the CallSite that is
+ * construction by the bootstrap method described by the @CalledByIndy
+ * annotation.
+ *
+ * In the example above, this results in add() being replaced by
+ * invocations of magicAdd().
+ */
+class IndyTransformer {
+
+    static class BootstrapBuilder extends ClassVisitor {
+
+        private final Map<String, CalledByIndy> callsiteMap;
+        private final Map<String, Handle> bsmMap = new HashMap<>();
+
+        public BootstrapBuilder(int api, Map<String, CalledByIndy> callsiteMap) {
+            this(api, null, callsiteMap);
+        }
+
+        public BootstrapBuilder(int api, ClassVisitor cv, Map<String, CalledByIndy> callsiteMap) {
+            super(api, cv);
+            this.callsiteMap = callsiteMap;
+        }
+
+        @Override
+        public MethodVisitor visitMethod(
+                int access, String name, String desc, String signature, String[] exceptions) {
+            MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
+            return new MethodVisitor(this.api, mv) {
+                @Override
+                public void visitMethodInsn(
+                        int opcode, String owner, String name, String desc, boolean itf) {
+                    if (opcode == org.objectweb.asm.Opcodes.INVOKESTATIC) {
+                        CalledByIndy callsite = callsiteMap.get(name);
+                        if (callsite != null) {
+                            insertIndy(callsite.name(), desc, callsite);
+                            return;
+                        }
+                    }
+                    mv.visitMethodInsn(opcode, owner, name, desc, itf);
+                }
+
+                private void insertIndy(String name, String desc, CalledByIndy callsite) {
+                    Handle bsm = buildBootstrapMethodHandle(callsite);
+                    Object[] bsmArgs = buildBootstrapArguments(callsite);
+                    mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
+                }
+
+                private Handle buildBootstrapMethodHandle(CalledByIndy callsite) {
+                    MethodHandleKind kind;
+                    if (callsite.fieldMethodHandle().length != 0) {
+                        return buildBootstrapMethodHandleForField(callsite.fieldMethodHandle()[0]);
+                    } else if (callsite.invokeMethodHandle().length != 0) {
+                        return buildBootstrapMethodHandleForMethod(
+                                callsite.invokeMethodHandle()[0]);
+                    } else {
+                        throw new Error("Missing linker method handle in CalledByIndy annotation");
+                    }
+                }
+
+                private Handle buildBootstrapMethodHandleForField(LinkerFieldHandle fieldHandle) {
+                    int handleKind;
+                    switch (fieldHandle.kind()) {
+                        case GET_FIELD:
+                            handleKind = Opcodes.H_GETFIELD;
+                            break;
+                        case GET_STATIC:
+                            handleKind = Opcodes.H_GETSTATIC;
+                            break;
+                        case PUT_FIELD:
+                            handleKind = Opcodes.H_PUTFIELD;
+                            break;
+                        case PUT_STATIC:
+                            handleKind = Opcodes.H_PUTSTATIC;
+                            break;
+                        default:
+                            throw new Error("Unknown field invocation kind: " + fieldHandle.kind());
+                    }
+                    Class<?> resolverClass = fieldHandle.enclosingType();
+                    String resolverMethod = fieldHandle.name();
+                    Class<?> resolverReturnType = fieldHandle.type();
+
+                    // TODO: arguments types to invoke resolver with (default + extra args).
+                    throw new Error("WIP");
+                }
+
+                private Handle buildBootstrapMethodHandleForMethod(
+                        LinkerMethodHandle methodHandle) {
+                    int handleKind;
+                    switch (methodHandle.kind()) {
+                        case INVOKE_CONSTRUCTOR:
+                            handleKind = Opcodes.H_NEWINVOKESPECIAL;
+                            break;
+                        case INVOKE_INTERFACE:
+                            handleKind = Opcodes.H_INVOKEINTERFACE;
+                            break;
+                        case INVOKE_SPECIAL:
+                            handleKind = Opcodes.H_INVOKESPECIAL;
+                            break;
+                        case INVOKE_STATIC:
+                            handleKind = Opcodes.H_INVOKESTATIC;
+                            break;
+                        case INVOKE_VIRTUAL:
+                            handleKind = Opcodes.H_INVOKEVIRTUAL;
+                            break;
+                        default:
+                            throw new Error(
+                                    "Unknown method invocation kind: " + methodHandle.kind());
+                    }
+                    String className = Type.getInternalName(methodHandle.enclosingType());
+                    String methodName = methodHandle.name();
+                    String methodType =
+                            MethodType.methodType(
+                                            methodHandle.returnType(), methodHandle.argumentTypes())
+                                    .toMethodDescriptorString();
+                    return new Handle(
+                            handleKind, className, methodName, methodType, false /* itf */);
+                }
+
+                private Object decodeConstant(int index, Constant constant) {
+                    if (constant.booleanValue().length == 1) {
+                        return constant.booleanValue()[0];
+                    } else if (constant.byteValue().length == 1) {
+                        return constant.byteValue()[0];
+                    } else if (constant.charValue().length == 1) {
+                        return constant.charValue()[0];
+                    } else if (constant.shortValue().length == 1) {
+                        return constant.shortValue()[0];
+                    } else if (constant.intValue().length == 1) {
+                        return constant.intValue()[0];
+                    } else if (constant.longValue().length == 1) {
+                        return constant.longValue()[0];
+                    } else if (constant.floatValue().length == 1) {
+                        return constant.floatValue()[0];
+                    } else if (constant.doubleValue().length == 1) {
+                        return constant.doubleValue()[0];
+                    } else if (constant.stringValue().length == 1) {
+                        return constant.stringValue()[0];
+                    } else if (constant.classValue().length == 1) {
+                        return Type.getType(constant.classValue()[0]);
+                    } else {
+                        throw new Error("Bad constant at index " + index);
+                    }
+                }
+
+                private Object[] buildBootstrapArguments(CalledByIndy callsite) {
+                    Constant[] rawArgs = callsite.methodHandleExtraArgs();
+                    Object[] args = new Object[rawArgs.length];
+                    for (int i = 0; i < rawArgs.length; ++i) {
+                        args[i] = decodeConstant(i, rawArgs[i]);
+                    }
+                    return args;
+                }
+            };
+        }
+    }
+
+    private static void transform(Path inputClassPath, Path outputClassPath) throws Throwable {
+        URLClassLoader classLoader =
+                new URLClassLoader(
+                        new URL[] {inputClassPath.toUri().toURL()},
+                        ClassLoader.getSystemClassLoader());
+        String inputClassName = inputClassPath.getFileName().toString().replace(".class", "");
+        Class<?> inputClass = classLoader.loadClass(inputClassName);
+        Map<String, CalledByIndy> callsiteMap = new HashMap<>();
+
+        for (Method m : inputClass.getDeclaredMethods()) {
+            CalledByIndy calledByIndy = m.getAnnotation(CalledByIndy.class);
+            if (calledByIndy == null) {
+                continue;
+            }
+            if (calledByIndy.name() == null) {
+                throw new Error("CallByIndy annotation does not specify name");
+            }
+            final int PRIVATE_STATIC = Modifier.STATIC | Modifier.PRIVATE;
+            if ((m.getModifiers() & PRIVATE_STATIC) != PRIVATE_STATIC) {
+                throw new Error(
+                        "Method whose invocations should be replaced should be private and static");
+            }
+            callsiteMap.put(m.getName(), calledByIndy);
+        }
+        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+        try (InputStream is = Files.newInputStream(inputClassPath)) {
+            ClassReader cr = new ClassReader(is);
+            cr.accept(new BootstrapBuilder(Opcodes.ASM6, cw, callsiteMap), 0);
+        }
+        try (OutputStream os = Files.newOutputStream(outputClassPath)) {
+            os.write(cw.toByteArray());
+        }
+    }
+
+    public static void main(String[] args) throws Throwable {
+        transform(Paths.get(args[0]), Paths.get(args[1]));
+    }
+}
diff --git a/test/983-source-transform-verify/source_transform_slicer.cc b/test/983-source-transform-verify/source_transform_slicer.cc
index abf32e7..34de625 100644
--- a/test/983-source-transform-verify/source_transform_slicer.cc
+++ b/test/983-source-transform-verify/source_transform_slicer.cc
@@ -17,15 +17,10 @@
 #include "source_transform.h"
 
 #pragma clang diagnostic push
-// slicer defines its own CHECK. b/65422458
-#pragma push_macro("CHECK")
-#undef CHECK
-
 // Slicer's headers have code that triggers these warnings. b/65298177
 #pragma clang diagnostic ignored "-Wsign-compare"
-#include "reader.h"
+#include "slicer/reader.h"
 
-#pragma pop_macro("CHECK")
 #pragma clang diagnostic pop
 
 namespace art {
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 464449c..cf781d7 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -20,6 +20,7 @@
 # Dependencies for actually running a run-test.
 TEST_ART_RUN_TEST_DEPENDENCIES := \
   $(HOST_OUT_EXECUTABLES)/dx \
+  $(HOST_OUT_EXECUTABLES)/d8 \
   $(HOST_OUT_EXECUTABLES)/hiddenapi \
   $(HOST_OUT_EXECUTABLES)/jasmin \
   $(HOST_OUT_EXECUTABLES)/smali \
diff --git a/test/ti-stress/stress.cc b/test/ti-stress/stress.cc
index 7fc289f..d2da244 100644
--- a/test/ti-stress/stress.cc
+++ b/test/ti-stress/stress.cc
@@ -29,22 +29,19 @@
 #include "utils.h"
 
 #pragma clang diagnostic push
-// slicer defines its own CHECK. b/65422458
-#pragma push_macro("CHECK")
-#undef CHECK
 
 // Slicer's headers have code that triggers these warnings. b/65298177
 #pragma clang diagnostic ignored "-Wunused-parameter"
 #pragma clang diagnostic ignored "-Wsign-compare"
-#include "code_ir.h"
-#include "control_flow_graph.h"
-#include "dex_ir.h"
-#include "dex_ir_builder.h"
-#include "instrumentation.h"
-#include "reader.h"
-#include "writer.h"
 
-#pragma pop_macro("CHECK")
+#include "slicer/code_ir.h"
+#include "slicer/control_flow_graph.h"
+#include "slicer/dex_ir.h"
+#include "slicer/dex_ir_builder.h"
+#include "slicer/instrumentation.h"
+#include "slicer/reader.h"
+#include "slicer/writer.h"
+
 #pragma clang diagnostic pop
 
 namespace art {