Merge "ART: Forward-declare Context"
diff --git a/Android.mk b/Android.mk
index 237dcd8..a2d8f64 100644
--- a/Android.mk
+++ b/Android.mk
@@ -63,7 +63,6 @@
 include $(art_path)/tools/amm/Android.mk
 include $(art_path)/tools/dexfuzz/Android.mk
 include $(art_path)/tools/veridex/Android.mk
-include $(art_path)/libart_fake/Android.mk
 
 ART_HOST_DEPENDENCIES := \
   $(ART_HOST_EXECUTABLES) \
@@ -388,9 +387,6 @@
     profman \
     libadbconnection \
 
-# For nosy apps, we provide a fake library that avoids namespace issues and gives some warnings.
-LOCAL_REQUIRED_MODULES += libart_fake
-
 # Potentially add in debug variants:
 #
 # * We will never add them if PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD = false.
diff --git a/libart_fake/Android.mk b/libart_fake/Android.mk
deleted file mode 100644
index 96e6a14..0000000
--- a/libart_fake/Android.mk
+++ /dev/null
@@ -1,35 +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.
-#
-
-LOCAL_PATH := $(my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libart_fake
-LOCAL_INSTALLED_MODULE_STEM := libart.so
-LOCAL_SDK_VERSION := 9
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_SRC_FILES := fake.cc
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_SHARED_LIBRARIES := liblog
-
-ifdef TARGET_2ND_ARCH
-    LOCAL_MODULE_PATH_32 := $(TARGET_OUT)/fake-libs
-    LOCAL_MODULE_PATH_64 := $(TARGET_OUT)/fake-libs64
-else
-    LOCAL_MODULE_PATH := $(TARGET_OUT)/fake-libs
-endif
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/libart_fake/README.md b/libart_fake/README.md
deleted file mode 100644
index 6e3621e..0000000
--- a/libart_fake/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-libart_fake
-====
-
-A fake libart made to satisfy some misbehaving apps that will attempt to link
-against libart.so.
diff --git a/libart_fake/fake.cc b/libart_fake/fake.cc
deleted file mode 100644
index 8842421..0000000
--- a/libart_fake/fake.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#define LOG_TAG "libart_fake"
-
-#include <android/log.h>
-
-#define LOGIT(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
-namespace art {
-class Dbg {
- public:
-  void SuspendVM();
-  void ResumeVM();
-};
-
-class FaultManager {
- public:
-  void EnsureArtActionInFrontOfSignalChain();
-};
-
-void Dbg::SuspendVM() {
-  LOGIT("Linking to and calling into libart.so internal functions is not supported. "
-        "This call to '%s' is being ignored.", __func__);
-}
-void Dbg::ResumeVM() {
-  LOGIT("Linking to and calling into libart.so internal functions is not supported. "
-        "This call to '%s' is being ignored.", __func__);
-}
-void FaultManager::EnsureArtActionInFrontOfSignalChain() {
-  LOGIT("Linking to and calling into libart.so internal functions is not supported. "
-        "This call to '%s' is being ignored.", __func__);
-}
-};  // namespace art
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 4e1276e..89826c6 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -114,7 +114,7 @@
   "kOomeWhenThrowingOome",
   "kOomeWhenHandlingStackOverflow",
   "kNoClassDefFoundError",
-  "kClassLoader",
+  "kSpecialRoots",
 };
 
 // Map is so that we don't allocate multiple dex files for the same OatDexFile.
diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc
index bd5b598..7a2b638 100644
--- a/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/openjdkjvmti/OpenjdkJvmTi.cc
@@ -1552,7 +1552,7 @@
 
   {
     // Make sure we can deopt anything we need to.
-    art::ScopedObjectAccess soa(art::Thread::Current());
+    art::ScopedSuspendAll ssa(__FUNCTION__);
     gDeoptManager->FinishSetup();
   }
 
diff --git a/openjdkjvmti/deopt_manager.h b/openjdkjvmti/deopt_manager.h
index d9f34a5..065baa7 100644
--- a/openjdkjvmti/deopt_manager.h
+++ b/openjdkjvmti/deopt_manager.h
@@ -107,7 +107,7 @@
 
   void FinishSetup()
       REQUIRES(!deoptimization_status_lock_, !art::Roles::uninterruptible_)
-      REQUIRES_SHARED(art::Locks::mutator_lock_);
+      REQUIRES(art::Locks::mutator_lock_);
 
   static DeoptManager* Get();
 
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index 4bcb7b2..3d175a8 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -188,7 +188,9 @@
     if (obsoleted_methods_.find(old_method) != obsoleted_methods_.end()) {
       // We cannot ensure that the right dex file is used in inlined frames so we don't support
       // redefining them.
-      DCHECK(!IsInInlinedFrame()) << "Inlined frames are not supported when using redefinition";
+      DCHECK(!IsInInlinedFrame()) << "Inlined frames are not supported when using redefinition: "
+                                  << old_method->PrettyMethod() << " is inlined into "
+                                  << GetOuterMethod()->PrettyMethod();
       art::ArtMethod* new_obsolete_method = obsolete_maps_->FindObsoleteVersion(old_method);
       if (new_obsolete_method == nullptr) {
         // Create a new Obsolete Method and put it in the list.
diff --git a/runtime/art_method.h b/runtime/art_method.h
index aed9f62..aabd093 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -476,7 +476,7 @@
   }
 
   ProfilingInfo* GetProfilingInfo(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_) {
-    if (UNLIKELY(IsNative()) || UNLIKELY(IsProxyMethod())) {
+    if (UNLIKELY(IsNative() || IsProxyMethod() || !IsInvokable())) {
       return nullptr;
     }
     return reinterpret_cast<ProfilingInfo*>(GetDataPtrSize(pointer_size));
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index adf01c3..663af81 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -681,13 +681,13 @@
   //
   // The performance cost of this is non-negligible during native-debugging due to the
   // forced JIT, so we keep the AOT code in that case in exchange for limited native debugging.
+  ScopedSuspendAll ssa(__FUNCTION__);
   if (!runtime->IsJavaDebuggable() &&
       !runtime->GetInstrumentation()->IsForcedInterpretOnly() &&
       !runtime->IsNativeDebuggable()) {
     runtime->DeoptimizeBootImage();
   }
 
-  ScopedSuspendAll ssa(__FUNCTION__);
   if (RequiresDeoptimization()) {
     runtime->GetInstrumentation()->EnableDeoptimization();
   }
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 27c47bc..36f7b3d 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -848,7 +848,7 @@
         new_quick_code = GetQuickInstrumentationEntryPoint();
         if (!method->IsNative() && Runtime::Current()->GetJit() != nullptr) {
           // Native methods use trampoline entrypoints during interpreter tracing.
-          DCHECK(!Runtime::Current()->GetJit()->GetCodeCache()->GetGarbageCollectCode());
+          DCHECK(!Runtime::Current()->GetJit()->GetCodeCache()->GetGarbageCollectCodeUnsafe());
           ProfilingInfo* profiling_info = method->GetProfilingInfo(kRuntimePointerSize);
           // Tracing will look at the saved entry point in the profiling info to know the actual
           // entrypoint, so we store it here.
@@ -1050,14 +1050,6 @@
     level = InstrumentationLevel::kInstrumentWithInterpreter;
   } else {
     level = InstrumentationLevel::kInstrumentWithInstrumentationStubs;
-    if (Runtime::Current()->GetJit() != nullptr) {
-      // TODO b/110263880 It would be better if we didn't need to do this.
-      // Since we need to hold the method entrypoint across a suspend to ensure instrumentation
-      // hooks are called correctly we have to disable jit-gc to ensure that the entrypoint doesn't
-      // go away. Furthermore we need to leave this off permanently since one could get the same
-      // effect by causing this to be toggled on and off.
-      Runtime::Current()->GetJit()->GetCodeCache()->SetGarbageCollectCode(false);
-    }
   }
   ConfigureStubs(key, level);
 }
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index d976fec..c1b9a1a 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -390,7 +390,7 @@
 
   // Zygote should never collect code to share the memory with the children.
   if (is_zygote) {
-    jit_code_cache->SetGarbageCollectCode(false);
+    jit_code_cache->garbage_collect_code_ = false;
   }
 
   if (!jit_code_cache->InitializeMappings(rwx_memory_allowed, is_zygote, error_msg)) {
@@ -1247,6 +1247,24 @@
   }
 }
 
+void JitCodeCache::ClearEntryPointsInZygoteExecSpace() {
+  MutexLock mu(Thread::Current(), lock_);
+  // Iterate over profiling infos to know which methods may have been JITted. Note that
+  // to be JITted, a method must have a profiling info.
+  for (ProfilingInfo* info : profiling_infos_) {
+    ArtMethod* method = info->GetMethod();
+    if (IsInZygoteExecSpace(method->GetEntryPointFromQuickCompiledCode())) {
+      method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
+    }
+    // If zygote does method tracing, or in some configuration where
+    // the JIT zygote does GC, we also need to clear the saved entry point
+    // in the profiling info.
+    if (IsInZygoteExecSpace(info->GetSavedEntryPoint())) {
+      info->SetSavedEntryPoint(nullptr);
+    }
+  }
+}
+
 size_t JitCodeCache::CodeCacheSizeLocked() {
   return used_memory_for_code_;
 }
@@ -1437,17 +1455,14 @@
 
 void JitCodeCache::GarbageCollectCache(Thread* self) {
   ScopedTrace trace(__FUNCTION__);
-  if (!garbage_collect_code_) {
-    MutexLock mu(self, lock_);
-    IncreaseCodeCacheCapacity();
-    return;
-  }
-
   // Wait for an existing collection, or let everyone know we are starting one.
   {
     ScopedThreadSuspension sts(self, kSuspended);
     MutexLock mu(self, lock_);
-    if (WaitForPotentialCollectionToComplete(self)) {
+    if (!garbage_collect_code_) {
+      IncreaseCodeCacheCapacity();
+      return;
+    } else if (WaitForPotentialCollectionToComplete(self)) {
       return;
     } else {
       number_of_collections_++;
@@ -1573,6 +1588,29 @@
   FreeAllMethodHeaders(method_headers);
 }
 
+bool JitCodeCache::GetGarbageCollectCode() {
+  MutexLock mu(Thread::Current(), lock_);
+  return garbage_collect_code_;
+}
+
+void JitCodeCache::SetGarbageCollectCode(bool value) {
+  Thread* self = Thread::Current();
+  MutexLock mu(self, lock_);
+  if (garbage_collect_code_ != value) {
+    if (garbage_collect_code_) {
+      // When dynamically disabling the garbage collection, we neee
+      // to make sure that a potential current collection is finished, and also
+      // clear the saved entry point in profiling infos to avoid dangling pointers.
+      WaitForPotentialCollectionToComplete(self);
+      for (ProfilingInfo* info : profiling_infos_) {
+        info->SetSavedEntryPoint(nullptr);
+      }
+    }
+    // Update the flag while holding the lock to ensure no thread will try to GC.
+    garbage_collect_code_ = value;
+  }
+}
+
 void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) {
   ScopedTrace trace(__FUNCTION__);
   {
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index e2f3357..8a6cebe 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -246,13 +246,13 @@
   void MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method)
       REQUIRES(!lock_) REQUIRES(Locks::mutator_lock_);
 
-  // Dynamically change whether we want to garbage collect code. Should only be used during JIT
-  // initialization or by tests.
-  void SetGarbageCollectCode(bool value) {
-    garbage_collect_code_ = value;
-  }
+  // Dynamically change whether we want to garbage collect code.
+  void SetGarbageCollectCode(bool value) REQUIRES(!lock_);
 
-  bool GetGarbageCollectCode() const {
+  bool GetGarbageCollectCode() REQUIRES(!lock_);
+
+  // Unsafe variant for debug checks.
+  bool GetGarbageCollectCodeUnsafe() const NO_THREAD_SAFETY_ANALYSIS {
     return garbage_collect_code_;
   }
 
@@ -264,6 +264,11 @@
 
   void PostForkChildAction(bool is_system_server, bool is_zygote);
 
+  // Clear the entrypoints of JIT compiled methods that belong in the zygote space.
+  // This is used for removing non-debuggable JIT code at the point we realize the runtime
+  // is debuggable.
+  void ClearEntryPointsInZygoteExecSpace() REQUIRES(!lock_) REQUIRES(Locks::mutator_lock_);
+
  private:
   JitCodeCache();
 
@@ -451,7 +456,7 @@
   bool last_collection_increased_code_cache_ GUARDED_BY(lock_);
 
   // Whether we can do garbage collection. Not 'const' as tests may override this.
-  bool garbage_collect_code_;
+  bool garbage_collect_code_ GUARDED_BY(lock_);
 
   // The size in bytes of used memory for the data portion of the code cache.
   size_t used_memory_for_data_ GUARDED_BY(lock_);
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 9ce4749..26fc5e9 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -203,8 +203,11 @@
     runtime->AddCompilerOption("--debuggable");
     runtime_flags |= DEBUG_GENERATE_MINI_DEBUG_INFO;
     runtime->SetJavaDebuggable(true);
-    // Deoptimize the boot image as it may be non-debuggable.
-    runtime->DeoptimizeBootImage();
+    {
+      // Deoptimize the boot image as it may be non-debuggable.
+      ScopedSuspendAll ssa(__FUNCTION__);
+      runtime->DeoptimizeBootImage();
+    }
     runtime_flags &= ~DEBUG_JAVA_DEBUGGABLE;
     needs_non_debuggable_classes = true;
   }
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index b925b96..eea7c67 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1473,6 +1473,8 @@
     if (IsJavaDebuggable()) {
       // Now that we have loaded the boot image, deoptimize its methods if we are running
       // debuggable, as the code may have been compiled non-debuggable.
+      ScopedThreadSuspension sts(self, ThreadState::kNative);
+      ScopedSuspendAll ssa(__FUNCTION__);
       DeoptimizeBootImage();
     }
   } else {
@@ -1572,10 +1574,14 @@
   // Runtime initialization is largely done now.
   // We load plugins first since that can modify the runtime state slightly.
   // Load all plugins
-  for (auto& plugin : plugins_) {
-    std::string err;
-    if (!plugin.Load(&err)) {
-      LOG(FATAL) << plugin << " failed to load: " << err;
+  {
+    // The init method of plugins expect the state of the thread to be non runnable.
+    ScopedThreadSuspension sts(self, ThreadState::kNative);
+    for (auto& plugin : plugins_) {
+      std::string err;
+      if (!plugin.Load(&err)) {
+        LOG(FATAL) << plugin << " failed to load: " << err;
+      }
     }
   }
 
@@ -2648,6 +2654,7 @@
       : instrumentation_(instrumentation) {}
 
   bool operator()(ObjPtr<mirror::Class> klass) override REQUIRES(Locks::mutator_lock_) {
+    DCHECK(Locks::mutator_lock_->IsExclusiveHeld(Thread::Current()));
     auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
     for (auto& m : klass->GetMethods(pointer_size)) {
       const void* code = m.GetEntryPointFromQuickCompiledCode();
@@ -2674,9 +2681,13 @@
   // we patch entry points of methods in boot image to interpreter bridge, as
   // boot image code may be AOT compiled as not debuggable.
   if (!GetInstrumentation()->IsForcedInterpretOnly()) {
-    ScopedObjectAccess soa(Thread::Current());
     UpdateEntryPointsClassVisitor visitor(GetInstrumentation());
     GetClassLinker()->VisitClasses(&visitor);
+    jit::Jit* jit = GetJit();
+    if (jit != nullptr) {
+      // Code JITted by the zygote is not compiled debuggable.
+      jit->GetCodeCache()->ClearEntryPointsInZygoteExecSpace();
+    }
   }
 }
 
diff --git a/runtime/runtime.h b/runtime/runtime.h
index a249c9f..1acb75b 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -634,7 +634,7 @@
   void SetJavaDebuggable(bool value);
 
   // Deoptimize the boot image, called for Java debuggable apps.
-  void DeoptimizeBootImage();
+  void DeoptimizeBootImage() REQUIRES(Locks::mutator_lock_);
 
   bool IsNativeDebuggable() const {
     return is_native_debuggable_;
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 25939d2..9a677de 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -568,7 +568,9 @@
     cur_shadow_frame_->SetMethod(method);
   } else {
     DCHECK(cur_quick_frame_ != nullptr);
-    CHECK(!IsInInlinedFrame()) << "We do not support setting inlined method's ArtMethod!";
+    CHECK(!IsInInlinedFrame()) << "We do not support setting inlined method's ArtMethod: "
+                               << GetMethod()->PrettyMethod() << " is inlined into "
+                               << GetOuterMethod()->PrettyMethod();
     *cur_quick_frame_ = method;
   }
 }
diff --git a/runtime/trace.cc b/runtime/trace.cc
index ce955d8..074e846 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -38,6 +38,8 @@
 #include "entrypoints/quick/quick_entrypoints.h"
 #include "gc/scoped_gc_critical_section.h"
 #include "instrumentation.h"
+#include "jit/jit.h"
+#include "jit/jit_code_cache.h"
 #include "mirror/class-inl.h"
 #include "mirror/dex_cache-inl.h"
 #include "mirror/object-inl.h"
@@ -390,6 +392,15 @@
   // Enable count of allocs if specified in the flags.
   bool enable_stats = false;
 
+  if (runtime->GetJit() != nullptr) {
+    // TODO b/110263880 It would be better if we didn't need to do this.
+    // Since we need to hold the method entrypoint across a suspend to ensure instrumentation
+    // hooks are called correctly we have to disable jit-gc to ensure that the entrypoint doesn't
+    // go away. Furthermore we need to leave this off permanently since one could get the same
+    // effect by causing this to be toggled on and off.
+    runtime->GetJit()->GetCodeCache()->SetGarbageCollectCode(false);
+  }
+
   // Create Trace object.
   {
     // Required since EnableMethodTracing calls ConfigureStubs which visits class linker classes.
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 1679821..91eba21 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -2654,6 +2654,7 @@
             // See if instance-of was preceded by a move-object operation, common due to the small
             // register encoding space of instance-of, and propagate type information to the source
             // of the move-object.
+            // Note: this is only valid if the move source was not clobbered.
             uint32_t move_idx = instance_of_idx - 1;
             while (0 != move_idx && !GetInstructionFlags(move_idx).IsOpcode()) {
               move_idx--;
@@ -2663,28 +2664,25 @@
                             work_insn_idx_)) {
               break;
             }
+            auto maybe_update_fn = [&instance_of_inst, update_line, this, &cast_type](
+                uint16_t move_src,
+                uint16_t move_trg)
+                REQUIRES_SHARED(Locks::mutator_lock_) {
+              if (move_trg == instance_of_inst.VRegB_22c() &&
+                  move_src != instance_of_inst.VRegA_22c()) {
+                update_line->SetRegisterType<LockOp::kKeep>(this, move_src, cast_type);
+              }
+            };
             const Instruction& move_inst = code_item_accessor_.InstructionAt(move_idx);
             switch (move_inst.Opcode()) {
               case Instruction::MOVE_OBJECT:
-                if (move_inst.VRegA_12x() == instance_of_inst.VRegB_22c()) {
-                  update_line->SetRegisterType<LockOp::kKeep>(this,
-                                                              move_inst.VRegB_12x(),
-                                                              cast_type);
-                }
+                maybe_update_fn(move_inst.VRegB_12x(), move_inst.VRegA_12x());
                 break;
               case Instruction::MOVE_OBJECT_FROM16:
-                if (move_inst.VRegA_22x() == instance_of_inst.VRegB_22c()) {
-                  update_line->SetRegisterType<LockOp::kKeep>(this,
-                                                              move_inst.VRegB_22x(),
-                                                              cast_type);
-                }
+                maybe_update_fn(move_inst.VRegB_22x(), move_inst.VRegA_22x());
                 break;
               case Instruction::MOVE_OBJECT_16:
-                if (move_inst.VRegA_32x() == instance_of_inst.VRegB_22c()) {
-                  update_line->SetRegisterType<LockOp::kKeep>(this,
-                                                              move_inst.VRegB_32x(),
-                                                              cast_type);
-                }
+                maybe_update_fn(move_inst.VRegB_32x(), move_inst.VRegA_32x());
                 break;
               default:
                 break;
diff --git a/test/689-zygote-jit-deopt/expected.txt b/test/689-zygote-jit-deopt/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/689-zygote-jit-deopt/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/689-zygote-jit-deopt/info.txt b/test/689-zygote-jit-deopt/info.txt
new file mode 100644
index 0000000..100032e
--- /dev/null
+++ b/test/689-zygote-jit-deopt/info.txt
@@ -0,0 +1,2 @@
+Regression test for debuggable apps that need to deoptimize
+methods JIT-compiled by the zygote.
diff --git a/test/689-zygote-jit-deopt/src/Main.java b/test/689-zygote-jit-deopt/src/Main.java
new file mode 100644
index 0000000..330663e
--- /dev/null
+++ b/test/689-zygote-jit-deopt/src/Main.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.
+ */
+
+public class Main {
+  public static void main(String[] args) {
+    System.loadLibrary(args[0]);
+    if (!hasJit()) {
+      return;
+    }
+    ensureJitCompiled(Object.class, "toString");
+    transitionJitFromZygote();
+    deoptimizeBootImage();
+    if (hasJitCompiledEntrypoint(Object.class, "toString")) {
+      throw new Error("Expected Object.toString to be deoptimized");
+    }
+  }
+
+  private static native boolean hasJit();
+  private static native void ensureJitCompiled(Class<?> cls, String name);
+  private static native boolean hasJitCompiledEntrypoint(Class<?> cls, String name);
+  private static native void deoptimizeBootImage();
+  private static native void transitionJitFromZygote();
+}
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index f3c3f03..291de72 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -73,4 +73,5 @@
 b/30458218
 b/31313170
 ConstClassAliasing
+b/121191566
 Done!
diff --git a/test/800-smali/smali/b_121191566.smali b/test/800-smali/smali/b_121191566.smali
new file mode 100644
index 0000000..bcf9ef5
--- /dev/null
+++ b/test/800-smali/smali/b_121191566.smali
@@ -0,0 +1,26 @@
+.class public LB121191566;
+.super Ljava/lang/Object;
+
+
+.method public constructor <init>()V
+.registers 1
+       invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+       return-void
+.end method
+
+.method public static run(Ljava/lang/Object;)Z
+.registers 5
+       move-object v3, v4
+       instance-of v4, v3, Ljava/lang/String;
+       if-eqz v4, :Branch
+       # The peephole must not overwrite v4 (from the move-object). Use an integral move
+       # to check.
+       move v0, v4
+       goto :End
+:Branch
+       # See above.
+       move v0, v4
+:End
+       # Triple-check: the merge should be consistent.
+       return v0
+.end method
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index 9b06e9e..d7979e1 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -193,6 +193,8 @@
         testCases.add(new TestCase("b/31313170", "B31313170", "run", null, null, 0));
         testCases.add(new TestCase("ConstClassAliasing", "ConstClassAliasing", "run", null, null,
                 null, true));
+        testCases.add(new TestCase("b/121191566", "B121191566", "run", new Object[] { "a" }, null,
+                true, false));
     }
 
     public void runTests() {
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index 55631a9..bbc3039 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -161,6 +161,19 @@
   return !interpreter;
 }
 
+static ArtMethod* GetMethod(ScopedObjectAccess& soa, jclass cls, const ScopedUtfChars& chars)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  CHECK(chars.c_str() != nullptr);
+  ArtMethod* method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(
+        chars.c_str(), kRuntimePointerSize);
+  if (method == nullptr) {
+    method = soa.Decode<mirror::Class>(cls)->FindDeclaredVirtualMethodByName(
+        chars.c_str(), kRuntimePointerSize);
+  }
+  DCHECK(method != nullptr) << "Unable to find method called " << chars.c_str();
+  return method;
+}
+
 extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJitCompiledEntrypoint(JNIEnv* env,
                                                                          jclass,
                                                                          jclass cls,
@@ -172,9 +185,7 @@
   Thread* self = Thread::Current();
   ScopedObjectAccess soa(self);
   ScopedUtfChars chars(env, method_name);
-  CHECK(chars.c_str() != nullptr);
-  ArtMethod* method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(
-        chars.c_str(), kRuntimePointerSize);
+  ArtMethod* method = GetMethod(soa, cls, chars);
   ScopedAssertNoThreadSuspension sants(__FUNCTION__);
   return jit->GetCodeCache()->ContainsPc(
       Runtime::Current()->GetInstrumentation()->GetCodeForInvoke(method));
@@ -191,9 +202,7 @@
   Thread* self = Thread::Current();
   ScopedObjectAccess soa(self);
   ScopedUtfChars chars(env, method_name);
-  CHECK(chars.c_str() != nullptr);
-  ArtMethod* method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(
-        chars.c_str(), kRuntimePointerSize);
+  ArtMethod* method = GetMethod(soa, cls, chars);
   return jit->GetCodeCache()->ContainsMethod(method);
 }
 
@@ -262,14 +271,7 @@
     ScopedObjectAccess soa(self);
 
     ScopedUtfChars chars(env, method_name);
-    CHECK(chars.c_str() != nullptr);
-    method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(
-        chars.c_str(), kRuntimePointerSize);
-    if (method == nullptr) {
-      method = soa.Decode<mirror::Class>(cls)->FindDeclaredVirtualMethodByName(
-          chars.c_str(), kRuntimePointerSize);
-    }
-    DCHECK(method != nullptr) << "Unable to find method called " << chars.c_str();
+    method = GetMethod(soa, cls, chars);
   }
   ForceJitCompiled(self, method);
 }
@@ -353,4 +355,20 @@
   return (jit != nullptr) ? jit->HotMethodThreshold() : 0;
 }
 
+extern "C" JNIEXPORT void JNICALL Java_Main_transitionJitFromZygote(JNIEnv*, jclass) {
+  jit::Jit* jit = Runtime::Current()->GetJit();
+  if (jit == nullptr) {
+    return;
+  }
+  // Mimic the transition behavior a zygote fork would have.
+  jit->PreZygoteFork();
+  jit->GetCodeCache()->PostForkChildAction(/*is_system_server=*/ false, /*is_zygote=*/ false);
+  jit->PostForkChildAction(/*is_zygote=*/ false);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_deoptimizeBootImage(JNIEnv*, jclass) {
+  ScopedSuspendAll ssa(__FUNCTION__);
+  Runtime::Current()->DeoptimizeBootImage();
+}
+
 }  // namespace art
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index 3c65b01..3f9ceea 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -223,5 +223,17 @@
     "libcore.libcore.net.InetAddressUtilsTest#test_parseNonNumericAddress[8]",
     "libcore.libcore.net.InetAddressUtilsTest#test_parseNonNumericAddress[9]"
   ]
+},
+{
+  description: "Apex related",
+  result: EXEC_FAILED,
+  modes: [device],
+  bug: 122642227,
+  names: [
+    "libcore.java.lang.SystemTest#testSystemProperties_mutable",
+    "libcore.libcore.icu.TimeZoneIntegrationTest#testTimeZoneDataVersion",
+    "libcore.libcore.icu.TimeZoneIntegrationTest#testTimeZoneDebugInfo",
+    "libcore.libcore.icu.TimeZoneIntegrationTest#testTzDataSetVersions"
+  ]
 }
 ]