Merge "Implemented ABS vectorization."
diff --git a/runtime/arch/arm/instruction_set_features_arm_test.cc b/runtime/arch/arm/instruction_set_features_arm_test.cc
index 6d5dd6d..3582351 100644
--- a/runtime/arch/arm/instruction_set_features_arm_test.cc
+++ b/runtime/arch/arm/instruction_set_features_arm_test.cc
@@ -34,6 +34,18 @@
   EXPECT_STREQ("div,atomic_ldrd_strd,-armv8a", krait_features->GetFeatureString().c_str());
   EXPECT_EQ(krait_features->AsBitmap(), 3U);
 
+  // Build features for a 32-bit ARM kryo processor.
+  std::unique_ptr<const InstructionSetFeatures> kryo_features(
+      InstructionSetFeatures::FromVariant(kArm, "kryo", &error_msg));
+  ASSERT_TRUE(kryo_features.get() != nullptr) << error_msg;
+
+  ASSERT_EQ(kryo_features->GetInstructionSet(), kArm);
+  EXPECT_TRUE(kryo_features->Equals(kryo_features.get()));
+  EXPECT_TRUE(kryo_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
+  EXPECT_TRUE(kryo_features->AsArmInstructionSetFeatures()->HasAtomicLdrdAndStrd());
+  EXPECT_STREQ("div,atomic_ldrd_strd,armv8a", kryo_features->GetFeatureString().c_str());
+  EXPECT_EQ(kryo_features->AsBitmap(), 7U);
+
   // Build features for a 32-bit ARM denver processor.
   std::unique_ptr<const InstructionSetFeatures> denver_features(
       InstructionSetFeatures::FromVariant(kArm, "denver", &error_msg));
@@ -86,6 +98,18 @@
   EXPECT_STREQ("div,atomic_ldrd_strd,-armv8a", krait_features->GetFeatureString().c_str());
   EXPECT_EQ(krait_features->AsBitmap(), 3U);
 
+  // Build features for a 32-bit ARM with LPAE and div processor.
+  std::unique_ptr<const InstructionSetFeatures> kryo_features(
+      base_features->AddFeaturesFromString("atomic_ldrd_strd,div", &error_msg));
+  ASSERT_TRUE(kryo_features.get() != nullptr) << error_msg;
+
+  ASSERT_EQ(kryo_features->GetInstructionSet(), kArm);
+  EXPECT_TRUE(kryo_features->Equals(krait_features.get()));
+  EXPECT_TRUE(kryo_features->AsArmInstructionSetFeatures()->HasDivideInstruction());
+  EXPECT_TRUE(kryo_features->AsArmInstructionSetFeatures()->HasAtomicLdrdAndStrd());
+  EXPECT_STREQ("div,atomic_ldrd_strd,-armv8a", kryo_features->GetFeatureString().c_str());
+  EXPECT_EQ(kryo_features->AsBitmap(), 3U);
+
   // Build features for a 32-bit ARM processor with LPAE and div flipped.
   std::unique_ptr<const InstructionSetFeatures> denver_features(
       base_features->AddFeaturesFromString("div,atomic_ldrd_strd,armv8a", &error_msg));
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 72aa785..029de46 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -1614,6 +1614,11 @@
     DELIVER_PENDING_EXCEPTION
 END art_quick_to_interpreter_bridge
 
+/*
+ * Called to attempt to execute an obsolete method.
+ */
+ONE_ARG_RUNTIME_EXCEPTION art_invoke_obsolete_method_stub, artInvokeObsoleteMethod
+
     /*
      * Routine that intercepts method calls and returns.
      */
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 26622f0..b2bbd0d 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -2152,6 +2152,11 @@
     RETURN_OR_DELIVER_PENDING_EXCEPTION
 END art_quick_to_interpreter_bridge
 
+/*
+ * Called to attempt to execute an obsolete method.
+ */
+ONE_ARG_RUNTIME_EXCEPTION art_invoke_obsolete_method_stub, artInvokeObsoleteMethod
+
 
 //
 // Instrumentation-related stubs
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 808536b..722a679 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -1877,6 +1877,14 @@
     DELIVER_PENDING_EXCEPTION
 END art_quick_to_interpreter_bridge
 
+    .extern artInvokeObsoleteMethod
+ENTRY art_invoke_obsolete_method_stub
+    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
+    la      $t9, artInvokeObsoleteMethod
+    jalr    $t9                                 # (Method* method, Thread* self)
+    move    $a1, rSELF                          # pass Thread::Current
+END art_invoke_obsolete_method_stub
+
     /*
      * Routine that intercepts method calls and returns.
      */
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 9c92805..9402232 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1818,6 +1818,13 @@
     DELIVER_PENDING_EXCEPTION
 END art_quick_to_interpreter_bridge
 
+    .extern artInvokeObsoleteMethod
+ENTRY art_invoke_obsolete_method_stub
+    SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
+    jal     artInvokeObsoleteMethod    # (Method* method, Thread* self)
+    move    $a1, rSELF                 # pass Thread::Current
+END art_invoke_obsolete_method_stub
+
     /*
      * Routine that intercepts method calls and returns.
      */
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 5f38dc8..6c0bcc9 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1938,6 +1938,11 @@
 END_FUNCTION art_quick_to_interpreter_bridge
 
     /*
+     * Called by managed code, saves callee saves and then calls artInvokeObsoleteMethod
+     */
+ONE_ARG_RUNTIME_EXCEPTION art_invoke_obsolete_method_stub, artInvokeObsoleteMethod
+
+    /*
      * Routine that intercepts method calls and returns.
      */
 DEFINE_FUNCTION art_quick_instrumentation_entry
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index e87b165..8e2acab 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1902,6 +1902,12 @@
 END_FUNCTION art_quick_to_interpreter_bridge
 
     /*
+     * Called to catch an attempt to invoke an obsolete method.
+     * RDI = method being called.
+     */
+ONE_ARG_RUNTIME_EXCEPTION art_invoke_obsolete_method_stub, artInvokeObsoleteMethod
+
+    /*
      * Routine that intercepts method calls and returns.
      */
 DEFINE_FUNCTION art_quick_instrumentation_entry
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 80a8773..5a71be6 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -337,7 +337,8 @@
       // Ensure that we won't be accidentally calling quick compiled code when -Xint.
       if (kIsDebugBuild && runtime->GetInstrumentation()->IsForcedInterpretOnly()) {
         CHECK(!runtime->UseJitCompilation());
-        const void* oat_quick_code = (IsNative() || !IsInvokable() || IsProxyMethod())
+        const void* oat_quick_code =
+            (IsNative() || !IsInvokable() || IsProxyMethod() || IsObsolete())
             ? nullptr
             : GetOatMethodQuickCode(runtime->GetClassLinker()->GetImagePointerSize());
         CHECK(oat_quick_code == nullptr || oat_quick_code != GetEntryPointFromQuickCompiledCode())
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 3c18704..b8ff2c2 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -8507,6 +8507,15 @@
   }
 }
 
+void ClassLinker::SetEntryPointsForObsoleteMethod(ArtMethod* method) const {
+  DCHECK(method->IsObsolete());
+  // We cannot mess with the entrypoints of native methods because they are used to determine how
+  // large the method's quick stack frame is. Without this information we cannot walk the stacks.
+  if (!method->IsNative()) {
+    method->SetEntryPointFromQuickCompiledCode(GetInvokeObsoleteMethodStub());
+  }
+}
+
 void ClassLinker::DumpForSigQuit(std::ostream& os) {
   ScopedObjectAccess soa(Thread::Current());
   ReaderMutexLock mu(soa.Self(), *Locks::classlinker_classes_lock_);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index ef51d82..a26e63b 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -544,6 +544,10 @@
   void SetEntryPointsToInterpreter(ArtMethod* method) const
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Set the entrypoints up for an obsolete method.
+  void SetEntryPointsForObsoleteMethod(ArtMethod* method) const
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   // Attempts to insert a class into a class table.  Returns null if
   // the class was inserted, otherwise returns an existing class with
   // the same descriptor and ClassLoader.
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 4f4bed0..6758d75 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -313,6 +313,14 @@
                               ArtMethod::PrettyMethod(method).c_str()).c_str());
 }
 
+// InternalError
+
+void ThrowInternalError(const char* fmt, ...) {
+  va_list args;
+  va_start(args, fmt);
+  ThrowException("Ljava/lang/InternalError;", nullptr, fmt, &args);
+  va_end(args);
+}
 
 // IOException
 
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index 55a8938..4afef79 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -151,6 +151,12 @@
 void ThrowIncompatibleClassChangeErrorForMethodConflict(ArtMethod* method)
     REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
 
+// InternalError
+
+void ThrowInternalError(const char* fmt, ...)
+    __attribute__((__format__(__printf__, 1, 2)))
+    REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+
 // IOException
 
 void ThrowIOException(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)))
diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
index 1520e13..565b4ed 100644
--- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
@@ -29,6 +29,15 @@
   self->QuickDeliverException();
 }
 
+extern "C" NO_RETURN uint64_t artInvokeObsoleteMethod(ArtMethod* method, Thread* self)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  DCHECK(method->IsObsolete());
+  ScopedQuickEntrypointChecks sqec(self);
+  ThrowInternalError("Attempting to invoke obsolete version of '%s'.",
+                     method->PrettyMethod().c_str());
+  self->QuickDeliverException();
+}
+
 // Called by generated code to throw an exception.
 extern "C" NO_RETURN void artDeliverExceptionFromCode(mirror::Throwable* exception, Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/entrypoints/runtime_asm_entrypoints.h b/runtime/entrypoints/runtime_asm_entrypoints.h
index 2842c5a..4ca52de 100644
--- a/runtime/entrypoints/runtime_asm_entrypoints.h
+++ b/runtime/entrypoints/runtime_asm_entrypoints.h
@@ -40,6 +40,12 @@
   return reinterpret_cast<const void*>(art_quick_to_interpreter_bridge);
 }
 
+// Return the address of stub code for attempting to invoke an obsolete method.
+extern "C" void art_invoke_obsolete_method_stub(ArtMethod*);
+static inline const void* GetInvokeObsoleteMethodStub() {
+  return reinterpret_cast<const void*>(art_invoke_obsolete_method_stub);
+}
+
 // Return the address of quick stub code for handling JNI calls.
 extern "C" void art_quick_generic_jni_trampoline(ArtMethod*);
 static inline const void* GetQuickGenericJniStub() {
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 67e949f..bf49e84 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -360,6 +360,14 @@
     return;
   }
 
+  // This can happen if we are in forced interpreter mode and an obsolete method is called using
+  // reflection.
+  if (UNLIKELY(method->IsObsolete())) {
+    ThrowInternalError("Attempting to invoke obsolete version of '%s'.",
+                       method->PrettyMethod().c_str());
+    return;
+  }
+
   const char* old_cause = self->StartAssertNoThreadSuspension("EnterInterpreterFromInvoke");
   const DexFile::CodeItem* code_item = method->GetCodeItem();
   uint16_t num_regs;
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 6ca951f..11f8505 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -451,8 +451,12 @@
   Thread* const self = Thread::Current();
   for (const DexFile* dex_file : class_linker->GetBootClassPath()) {
     CHECK(dex_file != nullptr);
+    // In fallback mode, not all boot classpath components might be registered, yet.
+    if (!class_linker->IsDexFileRegistered(self, *dex_file)) {
+      continue;
+    }
     ObjPtr<mirror::DexCache> const dex_cache = class_linker->FindDexCache(self, *dex_file);
-    CHECK(dex_cache != nullptr);  // Boot class path dex caches are never unloaded.
+    DCHECK(dex_cache != nullptr);  // Boot class path dex caches are never unloaded.
     for (size_t j = 0; j < dex_cache->NumStrings(); j++) {
       ObjPtr<mirror::String> string = dex_cache->GetResolvedString(dex::StringIndex(j));
       if (string != nullptr) {
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index 6e0d9a0..7d95de8 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -186,6 +186,7 @@
         DCHECK_EQ(new_obsolete_method->GetDeclaringClass(), old_method->GetDeclaringClass());
         new_obsolete_method->SetIsObsolete();
         new_obsolete_method->SetDontCompile();
+        cl->SetEntryPointsForObsoleteMethod(new_obsolete_method);
         obsolete_maps_->RecordObsolete(old_method, new_obsolete_method);
         // Update JIT Data structures to point to the new method.
         art::jit::Jit* jit = art::Runtime::Current()->GetJit();
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 0628643..333128b 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -649,7 +649,7 @@
   }
 
   const void* code = method->GetEntryPointFromQuickCompiledCode();
-  if (code == GetQuickInstrumentationEntryPoint()) {
+  if (code == GetQuickInstrumentationEntryPoint() || code == GetInvokeObsoleteMethodStub()) {
     return;
   }
 
diff --git a/sigchainlib/OWNERS b/sigchainlib/OWNERS
new file mode 100644
index 0000000..450fc12
--- /dev/null
+++ b/sigchainlib/OWNERS
@@ -0,0 +1,4 @@
+# Default maintainers and code reviewers:
+jmgao@google.com
+dimitry@google.com
+sehr@google.com
diff --git a/test/984-obsolete-invoke/expected.txt b/test/984-obsolete-invoke/expected.txt
new file mode 100644
index 0000000..8052c46
--- /dev/null
+++ b/test/984-obsolete-invoke/expected.txt
@@ -0,0 +1,10 @@
+hello
+transforming calling function
+Retrieving obsolete method from current stack
+goodbye
+Invoking redefined version of method.
+Hello - Transformed
+Not doing anything here
+Goodbye - Transformed
+invoking obsolete method
+Caught expected error from attempting to invoke an obsolete method.
diff --git a/test/984-obsolete-invoke/info.txt b/test/984-obsolete-invoke/info.txt
new file mode 100644
index 0000000..48e0de0
--- /dev/null
+++ b/test/984-obsolete-invoke/info.txt
@@ -0,0 +1,4 @@
+Tests basic obsolete method support
+
+Tests that a specific method of potentially executing obsolete method code does
+not work.
diff --git a/test/984-obsolete-invoke/obsolete_invoke.cc b/test/984-obsolete-invoke/obsolete_invoke.cc
new file mode 100644
index 0000000..27e36ba
--- /dev/null
+++ b/test/984-obsolete-invoke/obsolete_invoke.cc
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#include "android-base/macros.h"
+#include "jni.h"
+#include "jvmti.h"
+#include "mirror/class-inl.h"
+#include "scoped_local_ref.h"
+
+// Test infrastructure
+#include "test_env.h"
+
+#include "jvmti_helper.h"
+
+namespace art {
+namespace Test984ObsoleteInvoke {
+
+static constexpr size_t kNumFrames = 30;
+
+extern "C" JNIEXPORT jobject JNICALL Java_Main_getFirstObsoleteMethod984(JNIEnv* env, jclass) {
+  jthread cur;
+  jint frame_count;
+  jvmtiFrameInfo frames[kNumFrames];
+  // jint cur_start = 0;
+  if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetCurrentThread(&cur))) {
+    // ERROR
+    return nullptr;
+  }
+  if (JvmtiErrorToException(env, jvmti_env,
+                            jvmti_env->GetStackTrace(cur,
+                                                     0,
+                                                     kNumFrames,
+                                                     frames,
+                                                     &frame_count))) {
+    // ERROR
+    return nullptr;
+  }
+  for (jint i = 0; i < frame_count; i++) {
+    jmethodID method = frames[i].method;
+    jboolean is_obsolete = false;
+    if (JvmtiErrorToException(env, jvmti_env, jvmti_env->IsMethodObsolete(method, &is_obsolete))) {
+      // ERROR
+      return nullptr;
+    }
+    if (is_obsolete) {
+      return env->ToReflectedMethod(env->FindClass("java/lang/reflect/Method"),
+                                    method,
+                                    JNI_TRUE);
+    }
+  }
+  ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+  env->ThrowNew(rt_exception.get(), "Unable to find obsolete method!");
+  return nullptr;
+}
+
+}  // namespace Test984ObsoleteInvoke
+}  // namespace art
diff --git a/test/984-obsolete-invoke/run b/test/984-obsolete-invoke/run
new file mode 100755
index 0000000..c6e62ae
--- /dev/null
+++ b/test/984-obsolete-invoke/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+./default-run "$@" --jvmti
diff --git a/test/984-obsolete-invoke/src/Main.java b/test/984-obsolete-invoke/src/Main.java
new file mode 100644
index 0000000..1a8d9bc
--- /dev/null
+++ b/test/984-obsolete-invoke/src/Main.java
@@ -0,0 +1,108 @@
+/*
+ * 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 java.lang.reflect.Method;
+import java.util.Base64;
+
+public class Main {
+  // class Transform {
+  //   public static void sayHi(Runnable r) {
+  //     System.out.println("Hello - Transformed");
+  //     r.run();
+  //     System.out.println("Goodbye - Transformed");
+  //   }
+  // }
+  private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+    "yv66vgAAADQAJAoACAARCQASABMIABQKABUAFgsAFwAYCAAZBwAaBwAbAQAGPGluaXQ+AQADKClW" +
+    "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
+    "KVYBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkACgcAHAwAHQAeAQATSGVsbG8gLSBU" +
+    "cmFuc2Zvcm1lZAcAHwwAIAAhBwAiDAAjAAoBABVHb29kYnllIC0gVHJhbnNmb3JtZWQBAAlUcmFu" +
+    "c2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZh" +
+    "L2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZh" +
+    "L2xhbmcvU3RyaW5nOylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuACAABwAIAAAAAAACAAAA" +
+    "CQAKAAEACwAAAB0AAQABAAAABSq3AAGxAAAAAQAMAAAABgABAAAAAQAJAA0ADgABAAsAAAA7AAIA" +
+    "AQAAABeyAAISA7YABCq5AAUBALIAAhIGtgAEsQAAAAEADAAAABIABAAAAAMACAAEAA4ABQAWAAYA" +
+    "AQAPAAAAAgAQ");
+  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+    "ZGV4CjAzNQCMekj2NPwzrEp/v+2yzzSg8xZvBtU1bC1QAwAAcAAAAHhWNBIAAAAAAAAAALACAAAR" +
+    "AAAAcAAAAAcAAAC0AAAAAwAAANAAAAABAAAA9AAAAAUAAAD8AAAAAQAAACQBAAAMAgAARAEAAKIB" +
+    "AACqAQAAwQEAANYBAADjAQAA+gEAAA4CAAAkAgAAOAIAAEwCAABcAgAAXwIAAGMCAAB3AgAAfAIA" +
+    "AIUCAACKAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACgAAAAoAAAAGAAAAAAAAAAsAAAAGAAAA" +
+    "lAEAAAsAAAAGAAAAnAEAAAUAAQANAAAAAAAAAAAAAAAAAAEAEAAAAAEAAgAOAAAAAgAAAAAAAAAD" +
+    "AAAADwAAAAAAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAJ8CAAAAAAAAAQABAAEAAACRAgAABAAAAHAQ" +
+    "AwAAAA4AAwABAAIAAACWAgAAFAAAAGIAAAAbAQIAAABuIAIAEAByEAQAAgBiAAAAGwEBAAAAbiAC" +
+    "ABAADgABAAAAAwAAAAEAAAAEAAY8aW5pdD4AFUdvb2RieWUgLSBUcmFuc2Zvcm1lZAATSGVsbG8g" +
+    "LSBUcmFuc2Zvcm1lZAALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEv" +
+    "bGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJM" +
+    "amF2YS9sYW5nL1N5c3RlbTsADlRyYW5zZm9ybS5qYXZhAAFWAAJWTAASZW1pdHRlcjogamFjay00" +
+    "LjMxAANvdXQAB3ByaW50bG4AA3J1bgAFc2F5SGkAAQAHDgADAQAHDoc8hwAAAAIAAICABMQCAQnc" +
+    "AgAAAA0AAAAAAAAAAQAAAAAAAAABAAAAEQAAAHAAAAACAAAABwAAALQAAAADAAAAAwAAANAAAAAE" +
+    "AAAAAQAAAPQAAAAFAAAABQAAAPwAAAAGAAAAAQAAACQBAAABIAAAAgAAAEQBAAABEAAAAgAAAJQB" +
+    "AAACIAAAEQAAAKIBAAADIAAAAgAAAJECAAAAIAAAAQAAAJ8CAAAAEAAAAQAAALACAAA=");
+
+  public static void main(String[] args) {
+    doTest();
+  }
+
+  // The Method that holds an obsolete method pointer. We will fill it in by getting a jmethodID
+  // from a stack with an obsolete method in it. There should be no other ways to obtain an obsolete
+  // jmethodID in ART without unsafe casts.
+  public static Method obsolete_method = null;
+
+  public static void doTest() {
+    // Capture the obsolete method.
+    //
+    // NB The obsolete method must be direct so that we will not look in the receiver type to get
+    // the actual method.
+    Transform.sayHi(() -> {
+      System.out.println("transforming calling function");
+      doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+      System.out.println("Retrieving obsolete method from current stack");
+      // This should get the obsolete sayHi method (as the only obsolete method on the current
+      // threads stack).
+      Main.obsolete_method = getFirstObsoleteMethod984();
+    });
+
+    // Prove we did actually redefine something.
+    System.out.println("Invoking redefined version of method.");
+    Transform.sayHi(() -> { System.out.println("Not doing anything here"); });
+
+    System.out.println("invoking obsolete method");
+    try {
+      obsolete_method.invoke(null, (Runnable)() -> {
+        throw new Error("Unexpected code running from invoke of obsolete method!");
+      });
+      throw new Error("Running obsolete method did not throw exception");
+    } catch (Throwable e) {
+      if (e instanceof InternalError || e.getCause() instanceof InternalError) {
+        System.out.println("Caught expected error from attempting to invoke an obsolete method.");
+      } else {
+        System.out.println("Unexpected error type for calling obsolete method! Expected either "
+            + "an InternalError or something that is caused by an InternalError.");
+        throw new Error("Unexpected error caught: ", e);
+      }
+    }
+  }
+
+  // Transforms the class
+  private static native void doCommonClassRedefinition(Class<?> target,
+                                                       byte[] classfile,
+                                                       byte[] dexfile);
+
+  // Gets the first obsolete method on the current threads stack (NB only looks through the first 30
+  // stack frames).
+  private static native Method getFirstObsoleteMethod984();
+}
diff --git a/test/984-obsolete-invoke/src/Transform.java b/test/984-obsolete-invoke/src/Transform.java
new file mode 100644
index 0000000..536de84
--- /dev/null
+++ b/test/984-obsolete-invoke/src/Transform.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+class Transform {
+  // This method must be 'static' so that when we try to invoke it through a j.l.r.Method we will
+  // simply use the jmethodID directly and not do any lookup in any receiver object.
+  public static void sayHi(Runnable r) {
+    System.out.println("hello");
+    r.run();
+    System.out.println("goodbye");
+  }
+}
diff --git a/test/Android.bp b/test/Android.bp
index 40f7edd..b79006f 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -298,6 +298,7 @@
         "945-obsolete-native/obsolete_native.cc",
         "980-redefine-object/redefine_object.cc",
         "983-source-transform-verify/source_transform.cc",
+        "984-obsolete-invoke/obsolete_invoke.cc",
     ],
 }
 
diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py
index 282ac48..0ab50af 100755
--- a/test/testrunner/run_build_test_target.py
+++ b/test/testrunner/run_build_test_target.py
@@ -16,14 +16,14 @@
 
 """Build and run go/ab/git_master-art-host target
 
+This script is executed by the android build server and must not be moved,
+or changed in an otherwise backwards-incompatible manner.
+
 Provided with a target name, the script setup the environment for
 building the test target by taking config information from
 from target_config.py.
 
-If the target field is defined in the configuration for the target, it
-invokes `make` to build the target, otherwise, it assumes
-that the its is a run-test target, and invokes testrunner.py
-script for building and running the run-tests.
+See target_config.py for the configuration syntax.
 """
 
 import argparse
@@ -35,10 +35,26 @@
 import env
 
 parser = argparse.ArgumentParser()
-parser.add_argument('build_target')
 parser.add_argument('-j', default='1', dest='n_threads')
+# either -l/--list OR build-target is required (but not both).
+group = parser.add_mutually_exclusive_group(required=True)
+group.add_argument('build_target', nargs='?')
+group.add_argument('-l', '--list', action='store_true', help='List all possible run-build targets.')
 options = parser.parse_args()
 
+##########
+
+if options.list:
+  print "List of all known build_target: "
+  for k in sorted(target_config.iterkeys()):
+    print " * " + k
+  # TODO: would be nice if this was the same order as the target config file.
+  sys.exit(1)
+
+if not target_config.get(options.build_target):
+  sys.stderr.write("error: invalid build_target, see -l/--list.\n")
+  sys.exit(1)
+
 target = target_config[options.build_target]
 n_threads = options.n_threads
 custom_env = target.get('env', {})
@@ -46,28 +62,46 @@
 print custom_env
 os.environ.update(custom_env)
 
-if target.get('target'):
+if target.get('make'):
   build_command = 'make'
   build_command += ' -j' + str(n_threads)
   build_command += ' -C ' + env.ANDROID_BUILD_TOP
-  build_command += ' ' + target.get('target')
+  build_command += ' ' + target.get('make')
   # Add 'dist' to avoid Jack issues b/36169180.
   build_command += ' dist'
-  sys.stdout.write(str(build_command))
+  sys.stdout.write(str(build_command) + '\n')
   sys.stdout.flush()
   if subprocess.call(build_command.split()):
     sys.exit(1)
 
-if target.get('run-tests'):
+if target.get('golem'):
+  machine_type = target.get('golem')
+  # use art-opt-cc by default since it mimics the default preopt config.
+  default_golem_config = 'art-opt-cc'
+
+  os.chdir(env.ANDROID_BUILD_TOP)
+  cmd =  ['art/tools/golem/build-target.sh']
+  cmd += ['-j' + str(n_threads)]
+  cmd += ['--showcommands']
+  cmd += ['--machine-type=%s' %(machine_type)]
+  cmd += ['--golem=%s' %(default_golem_config)]
+  cmd += ['--tarball']
+  sys.stdout.write(str(cmd) + '\n')
+  sys.stdout.flush()
+
+  if subprocess.call(cmd):
+    sys.exit(1)
+
+if target.get('run-test'):
   run_test_command = [os.path.join(env.ANDROID_BUILD_TOP,
                                    'art/test/testrunner/testrunner.py')]
-  run_test_command += target.get('flags', [])
+  run_test_command += target.get('run-test', [])
   run_test_command += ['-j', str(n_threads)]
   run_test_command += ['-b']
   run_test_command += ['--host']
   run_test_command += ['--verbose']
 
-  sys.stdout.write(str(run_test_command))
+  sys.stdout.write(str(run_test_command) + '\n')
   sys.stdout.flush()
   if subprocess.call(run_test_command):
     sys.exit(1)
diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py
index 5a6ecff..82f7832 100644
--- a/test/testrunner/target_config.py
+++ b/test/testrunner/target_config.py
@@ -1,72 +1,86 @@
 target_config = {
+
+# Configuration syntax:
+#
+#   Required keys: (Use one or more of these)
+#    * golem - specify a golem machine-type to build, e.g. android-armv8
+#              (uses art/tools/golem/build-target.sh)
+#    * make - specify a make target to build, e.g. build-art-host
+#    * run-test - runs the tests in art/test/ directory with testrunner.py,
+#                 specify a list of arguments to pass to testrunner.py
+#
+#   Optional keys: (Use any of these)
+#    * env - Add additional environment variable to the current environment.
+#
+# *** IMPORTANT ***:
+#    This configuration is used by the android build server. Targets must not be renamed
+#    or removed.
+#
+
+##########################################
+
+    # ART run-test configurations
+    # (calls testrunner which builds and then runs the test targets)
+
     'art-test' : {
-        'target' : 'test-art-host-gtest',
-        'run-tests' : True,
-        'flags' : [],
+        'make' : 'test-art-host-gtest',
+        'run-test' : [],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
     'art-interpreter' : {
-        'run-tests' : True,
-        'flags' : ['--interpreter'],
+        'run-test' : ['--interpreter'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
     'art-interpreter-access-checks' : {
-        'run-tests' : True,
-        'flags' : ['--interp-ac'],
+        'run-test' : ['--interp-ac'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
     'art-jit' : {
-        'run-tests' : True,
-        'flags' : ['--jit'],
+        'run-test' : ['--jit'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
     'art-gcstress-gcverify': {
-        'run-tests' : True,
-        'flags' : ['--gcstress',
-                   '--gcverify'],
+        'run-test': ['--gcstress',
+                     '--gcverify'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false',
             'ART_DEFAULT_GC_TYPE' : 'SS'
         }
     },
     'art-interpreter-gcstress' : {
-        'run-tests' : True,
-        'flags': ['--interpreter',
-                  '--gcstress'],
+        'run-test' : ['--interpreter',
+                      '--gcstress'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false',
             'ART_DEFAULT_GC_TYPE' : 'SS'
         }
     },
     'art-optimizing-gcstress' : {
-        'run-tests' : True,
-        'flags': ['--gcstress',
-                  '--optimizing'],
+        'run-test' : ['--gcstress',
+                      '--optimizing'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false',
             'ART_DEFAULT_GC_TYPE' : 'SS'
         }
     },
     'art-jit-gcstress' : {
-        'run-tests' : True,
-        'flags': ['--jit',
-                  '--gcstress'],
+        'run-test' : ['--jit',
+                      '--gcstress'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false',
             'ART_DEFAULT_GC_TYPE' : 'SS'
         }
     },
     'art-read-barrier' : {
-        'run-tests' : True,
-        'flags': ['--interpreter',
+        'run-test': ['--interpreter',
                   '--optimizing'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'true',
@@ -74,19 +88,17 @@
         }
     },
     'art-read-barrier-gcstress' : {
-        'run-tests' : True,
-        'flags' : ['--interpreter',
-                   '--optimizing',
-                   '--gcstress'],
+        'run-test' : ['--interpreter',
+                      '--optimizing',
+                      '--gcstress'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'true',
             'ART_HEAP_POISONING' : 'true'
         }
     },
     'art-read-barrier-table-lookup' : {
-        'run-tests' : True,
-        'flags' : ['--interpreter',
-                   '--optimizing'],
+        'run-test' : ['--interpreter',
+                      '--optimizing'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'true',
             'ART_READ_BARRIER_TYPE' : 'TABLELOOKUP',
@@ -94,39 +106,35 @@
         }
     },
     'art-debug-gc' : {
-        'run-tests' : True,
-        'flags' : ['--interpreter',
-                   '--optimizing'],
+        'run-test' : ['--interpreter',
+                      '--optimizing'],
         'env' : {
             'ART_TEST_DEBUG_GC' : 'true',
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
     'art-ss-gc' : {
-        'run-tests' : True,
-        'flags' : ['--interpreter',
-                   '--optimizing',
-                   '--jit'],
+        'run-test' : ['--interpreter',
+                      '--optimizing',
+                      '--jit'],
         'env' : {
             'ART_DEFAULT_GC_TYPE' : 'SS',
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
     'art-gss-gc' : {
-        'run-tests' : True,
-        'flags' : ['--interpreter',
-                   '--optimizing',
-                   '--jit'],
+        'run-test' : ['--interpreter',
+                      '--optimizing',
+                      '--jit'],
         'env' : {
             'ART_DEFAULT_GC_TYPE' : 'GSS',
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
     'art-ss-gc-tlab' : {
-        'run-tests' : True,
-        'flags' : ['--interpreter',
-                   '--optimizing',
-                   '--jit'],
+        'run-test' : ['--interpreter',
+                      '--optimizing',
+                      '--jit'],
         'env' : {
             'ART_DEFAULT_GC_TYPE' : 'SS',
             'ART_USE_TLAB' : 'true',
@@ -134,10 +142,9 @@
         }
     },
     'art-gss-gc-tlab' : {
-        'run-tests' : True,
-        'flags' : ['--interpreter',
-                   '--optimizing',
-                   '--jit'],
+        'run-test' : ['--interpreter',
+                      '--optimizing',
+                      '--jit'],
         'env' : {
             'ART_DEFAULT_GC_TYPE' : 'GSS',
             'ART_USE_TLAB' : 'true',
@@ -145,87 +152,82 @@
         }
     },
     'art-tracing' : {
-        'run-tests' : True,
-        'flags' : ['--trace'],
+        'run-test' : ['--trace'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
     'art-interpreter-tracing' : {
-        'run-tests' : True,
-        'flags' : ['--interpreter',
-                   '--trace'],
+        'run-test' : ['--interpreter',
+                      '--trace'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false',
         }
     },
     'art-forcecopy' : {
-        'run-tests' : True,
-        'flags' : ['--forcecopy'],
+        'run-test' : ['--forcecopy'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false',
         }
     },
     'art-no-prebuild' : {
-        'run-tests' : True,
-        'flags' : ['--no-prebuild'],
+        'run-test' : ['--no-prebuild'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false',
         }
     },
     'art-no-image' : {
-        'run-tests' : True,
-        'flags' : ['--no-image'],
+        'run-test' : ['--no-image'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false',
         }
     },
     'art-interpreter-no-image' : {
-        'run-tests' : True,
-        'flags' : ['--interpreter',
-                   '--no-image'],
+        'run-test' : ['--interpreter',
+                      '--no-image'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false',
         }
     },
     'art-relocate-no-patchoat' : {
-        'run-tests' : True,
-        'flags' : ['--relocate-npatchoat'],
+        'run-test' : ['--relocate-npatchoat'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false',
         }
     },
     'art-no-dex2oat' : {
-        'run-tests' : True,
-        'flags' : ['--no-dex2oat'],
+        'run-test' : ['--no-dex2oat'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false',
         }
     },
     'art-heap-poisoning' : {
-        'run-tests' : True,
-        'flags' : ['--interpreter',
-                   '--optimizing'],
+        'run-test' : ['--interpreter',
+                      '--optimizing'],
         'env' : {
             'ART_USE_READ_BARRIER' : 'false',
             'ART_HEAP_POISONING' : 'true'
         }
     },
+
+    # ART gtest configurations
+    # (calls make 'target' which builds and then runs the gtests).
+
     'art-gtest' : {
-        'target' :  'test-art-host-gtest',
+        'make' :  'test-art-host-gtest',
         'env' : {
             'ART_USE_READ_BARRIER' : 'true'
         }
     },
     'art-gtest-read-barrier': {
-        'target' :  'test-art-host-gtest',
+        'make' :  'test-art-host-gtest',
         'env' : {
             'ART_USE_READ_BARRIER' : 'true',
             'ART_HEAP_POISONING' : 'true'
         }
     },
     'art-gtest-read-barrier-table-lookup': {
-        'target' :  'test-art-host-gtest',
+        'make' :  'test-art-host-gtest',
         'env': {
             'ART_USE_READ_BARRIER' : 'true',
             'ART_READ_BARRIER_TYPE' : 'TABLELOOKUP',
@@ -233,21 +235,21 @@
         }
     },
     'art-gtest-ss-gc': {
-        'target' :  'test-art-host-gtest',
+        'make' :  'test-art-host-gtest',
         'env': {
             'ART_DEFAULT_GC_TYPE' : 'SS',
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
     'art-gtest-gss-gc': {
-        'target' :  'test-art-host-gtest',
+        'make' :  'test-art-host-gtest',
         'env' : {
             'ART_DEFAULT_GC_TYPE' : 'GSS',
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
     'art-gtest-ss-gc-tlab': {
-        'target' :  'test-art-host-gtest',
+        'make' :  'test-art-host-gtest',
         'env': {
             'ART_DEFAULT_GC_TYPE' : 'SS',
             'ART_USE_TLAB' : 'true',
@@ -255,7 +257,7 @@
         }
     },
     'art-gtest-gss-gc-tlab': {
-        'target' :  'test-art-host-gtest',
+        'make' :  'test-art-host-gtest',
         'env': {
             'ART_DEFAULT_GC_TYPE' : 'GSS',
             'ART_USE_TLAB' : 'true',
@@ -263,29 +265,54 @@
         }
     },
     'art-gtest-debug-gc' : {
-        'target' :  'test-art-host-gtest',
+        'make' :  'test-art-host-gtest',
         'env' : {
             'ART_TEST_DEBUG_GC' : 'true',
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
     'art-gtest-valgrind32': {
-        'target' : 'valgrind-test-art-host32',
+        'make' : 'valgrind-test-art-host32',
         'env': {
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
     'art-gtest-valgrind64': {
-        'target' : 'valgrind-test-art-host64',
+        'make' : 'valgrind-test-art-host64',
         'env': {
             'ART_USE_READ_BARRIER' : 'false'
         }
     },
     'art-gtest-heap-poisoning': {
-        'target' : 'valgrind-test-art-host64',
+        'make' : 'valgrind-test-art-host64',
         'env' : {
             'ART_HEAP_POISONING' : 'true',
             'ART_USE_READ_BARRIER' : 'false'
         }
-    }
+    },
+
+   # ART Golem build targets used by go/lem (continuous ART benchmarking),
+   # (art-opt-cc is used by default since it mimics the default preopt config),
+   #
+   # calls golem/build-target.sh which builds a golem tarball of the target name,
+   #     e.g. 'golem: android-armv7' produces an 'android-armv7.tar.gz' upon success.
+
+    'art-golem-android-armv7': {
+        'golem' : 'android-armv7'
+    },
+    'art-golem-android-armv8': {
+        'golem' : 'android-armv8'
+    },
+    'art-golem-linux-armv7': {
+        'golem' : 'linux-armv7'
+    },
+    'art-golem-linux-armv8': {
+        'golem' : 'linux-armv8'
+    },
+    'art-golem-linux-ia32': {
+        'golem' : 'linux-ia32'
+    },
+    'art-golem-linux-x64': {
+        'golem' : 'linux-x64'
+    },
 }