Merge "Allow ArtMethod::Invoke in unstarted runtimes."
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 18afa02..dbea0d8 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3231,12 +3231,8 @@
   mirror::ArtMethod* clinit = klass->FindClassInitializer();
   if (clinit != NULL) {
     CHECK(can_init_statics);
-    if (LIKELY(Runtime::Current()->IsStarted())) {
-      JValue result;
-      clinit->Invoke(self, NULL, 0, &result, "V");
-    } else {
-      art::interpreter::EnterInterpreterFromInvoke(self, clinit, NULL, NULL, NULL);
-    }
+    JValue result;
+    clinit->Invoke(self, NULL, 0, &result, "V");
   }
 
   uint64_t t1 = NanoTime();
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 1e1a8c1..7232e54 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -27,10 +27,6 @@
 static void UnstartedRuntimeJni(Thread* self, ArtMethod* method,
                                 Object* receiver, uint32_t* args, JValue* result)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  DCHECK(Runtime::Current()->IsActiveTransaction()) << "Calling native method "
-                                                    << PrettyMethod(method)
-                                                    << " in unstarted runtime should only happen"
-                                                    << " in a transaction";
   std::string name(PrettyMethod(method));
   if (name == "java.lang.ClassLoader dalvik.system.VMStack.getCallingClassLoader()") {
     result->SetL(NULL);
@@ -71,7 +67,11 @@
     result->SetL(Array::CreateMultiArray(self, sirt_class, sirt_dimensions));
   } else if (name == "java.lang.Object java.lang.Throwable.nativeFillInStackTrace()") {
     ScopedObjectAccessUnchecked soa(self);
-    result->SetL(soa.Decode<Object*>(self->CreateInternalStackTrace<true>(soa)));
+    if (Runtime::Current()->IsActiveTransaction()) {
+      result->SetL(soa.Decode<Object*>(self->CreateInternalStackTrace<true>(soa)));
+    } else {
+      result->SetL(soa.Decode<Object*>(self->CreateInternalStackTrace<false>(soa)));
+    }
   } else if (name == "int java.lang.System.identityHashCode(java.lang.Object)") {
     mirror::Object* obj = reinterpret_cast<Object*>(args[0]);
     result->SetI((obj != nullptr) ? obj->IdentityHashCode() : 0);
@@ -82,13 +82,22 @@
     jlong offset = (static_cast<uint64_t>(args[2]) << 32) | args[1];
     jint expectedValue = args[3];
     jint newValue = args[4];
-    bool success = obj->CasField32<true>(MemberOffset(offset), expectedValue, newValue);
+    bool success;
+    if (Runtime::Current()->IsActiveTransaction()) {
+      success = obj->CasField32<true>(MemberOffset(offset), expectedValue, newValue);
+    } else {
+      success = obj->CasField32<false>(MemberOffset(offset), expectedValue, newValue);
+    }
     result->SetZ(success ? JNI_TRUE : JNI_FALSE);
   } else if (name == "void sun.misc.Unsafe.putObject(java.lang.Object, long, java.lang.Object)") {
     Object* obj = reinterpret_cast<Object*>(args[0]);
     jlong offset = (static_cast<uint64_t>(args[2]) << 32) | args[1];
     Object* newValue = reinterpret_cast<Object*>(args[3]);
-    obj->SetFieldObject<true>(MemberOffset(offset), newValue);
+    if (Runtime::Current()->IsActiveTransaction()) {
+      obj->SetFieldObject<true>(MemberOffset(offset), newValue);
+    } else {
+      obj->SetFieldObject<false>(MemberOffset(offset), newValue);
+    }
   } else if (name == "int sun.misc.Unsafe.getArrayBaseOffsetForComponentType(java.lang.Class)") {
     mirror::Class* component = reinterpret_cast<Object*>(args[0])->AsClass();
     Primitive::Type primitive_type = component->GetPrimitiveType();
@@ -97,9 +106,13 @@
     mirror::Class* component = reinterpret_cast<Object*>(args[0])->AsClass();
     Primitive::Type primitive_type = component->GetPrimitiveType();
     result->SetI(Primitive::ComponentSize(primitive_type));
-  } else {
+  } else if (Runtime::Current()->IsActiveTransaction()) {
     AbortTransaction(self, "Attempt to invoke native method in non-started runtime: %s",
                      name.c_str());
+
+  } else {
+    LOG(FATAL) << "Calling native method " << PrettyMethod(method) << " in an unstarted "
+        "non-transactional runtime";
   }
 }
 
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 071b658..14fc25c 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -322,11 +322,7 @@
   ASSERT_TRUE(mid2 != NULL);
   // Make sure we can actually use it.
   jstring s = env_->NewStringUTF("poop");
-  // TODO: this should return 4, but the runtime skips the method
-  // invoke because the runtime isn't started. In the future it would
-  // be nice to use interpretter for things like this. This still does
-  // validate that we have a sane jmethodID value.
-  ASSERT_EQ(0, env_->CallIntMethod(s, mid2));
+  ASSERT_EQ(4, env_->CallIntMethod(s, mid2));
 }
 
 void BogusMethod() {
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index 7453d4d..4275f25 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -287,9 +287,11 @@
   Runtime* runtime = Runtime::Current();
   // Call the invoke stub, passing everything as arguments.
   if (UNLIKELY(!runtime->IsStarted())) {
-    LOG(INFO) << "Not invoking " << PrettyMethod(this) << " for a runtime that isn't started";
-    if (result != NULL) {
-      result->SetJ(0);
+    if (IsStatic()) {
+      art::interpreter::EnterInterpreterFromInvoke(self, this, nullptr, args, result);
+    } else {
+      Object* receiver = reinterpret_cast<StackReference<Object>*>(&args[0])->AsMirrorPtr();
+      art::interpreter::EnterInterpreterFromInvoke(self, this, receiver, args + 1, result);
     }
   } else {
     const bool kLogInvocationStartAndReturn = false;