Add infrastructure for registering built-in native methods.

While I'm here, make fiddling with Thread state easier.

Change-Id: I3d215a3a852aa8970c3974b2edefce9dd261ccd7
diff --git a/src/dex_file.cc b/src/dex_file.cc
index 4b56435..3d66909 100644
--- a/src/dex_file.cc
+++ b/src/dex_file.cc
@@ -217,8 +217,7 @@
     Thread* current_thread = Thread::Current();
     Thread::State old;
     if (current_thread != NULL) {
-        old = current_thread->GetState();
-        current_thread->SetState(Thread::kNative);
+        old = current_thread->SetState(Thread::kNative);
     }
     UniquePtr<LockedFd> fd(LockedFd::CreateAndLock(cache_path_tmp, 0644));
     if (current_thread != NULL) {
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 5acb155..8fb1618 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -500,10 +500,8 @@
         LOG(INFO) << "[" << *self << " waiting for \"" << path_ << "\" "
                   << "JNI_OnLoad...]";
       }
-      Thread::State old_state = self->GetState();
-      self->SetState(Thread::kWaiting); // TODO: VMWAIT
+      ScopedThreadStateChange tsc(self, Thread::kWaiting); // TODO: VMWAIT
       pthread_cond_wait(&jni_on_load_cond_, jni_on_load_lock_->GetImpl());
-      self->SetState(old_state);
     }
 
     bool okay = (jni_on_load_result_ == kOkay);
@@ -2744,10 +2742,11 @@
   // want to switch from RUNNING to VMWAIT while it executes.  This allows
   // the GC to ignore us.
   Thread* self = Thread::Current();
-  Thread::State old_state = self->GetState();
-  self->SetState(Thread::kWaiting); // TODO: VMWAIT
-  void* handle = dlopen(path.c_str(), RTLD_LAZY);
-  self->SetState(old_state);
+  void* handle = NULL;
+  {
+    ScopedThreadStateChange tsc(self, Thread::kWaiting); // TODO: VMWAIT
+    handle = dlopen(path.c_str(), RTLD_LAZY);
+  }
 
   if (verbose_jni) {
     LOG(INFO) << "[Call to dlopen(\"" << path << "\") returned " << handle << "]";
@@ -2792,13 +2791,14 @@
     const ClassLoader* old_class_loader = self->GetClassLoaderOverride();
     self->SetClassLoaderOverride(class_loader);
 
-    old_state = self->GetState();
-    self->SetState(Thread::kNative);
-    if (verbose_jni) {
-      LOG(INFO) << "[Calling JNI_OnLoad in \"" << path << "\"]";
+    int version = 0;
+    {
+      ScopedThreadStateChange tsc(self, Thread::kNative);
+      if (verbose_jni) {
+        LOG(INFO) << "[Calling JNI_OnLoad in \"" << path << "\"]";
+      }
+      version = (*jni_on_load)(this, NULL);
     }
-    int version = (*jni_on_load)(this, NULL);
-    self->SetState(old_state);
 
     self->SetClassLoaderOverride(old_class_loader);;
 
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index 42ab158..3e3e381 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -283,7 +283,7 @@
 
   // Check that registering native methods is successful
   {
-    JNINativeMethod methods[] = {{"hashCode", "()I", reinterpret_cast<void*>(BogusMethod)}};
+    JNINativeMethod methods[] = {{"getClass", "()Ljava/lang/Class;", reinterpret_cast<void*>(BogusMethod)}};
     env_->RegisterNatives(jlobject, methods, 1);
   }
   EXPECT_FALSE(env_->ExceptionCheck());
diff --git a/src/runtime.cc b/src/runtime.cc
index 1ac45f4..b126ad9 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -288,15 +288,7 @@
   }
   instance_ = runtime.release();
 
-  // Most JNI libraries can just use System.loadLibrary, but you can't
-  // if you're the library that implements System.loadLibrary!
-  Thread* self = Thread::Current();
-  Thread::State old_state = self->GetState();
-  self->SetState(Thread::kNative);
-  JniConstants::init(self->GetJniEnv());
-  LoadJniLibrary(instance_->GetJavaVM(), "javacore");
-  self->SetState(old_state);
-
+  instance_->InitLibraries();
   instance_->signal_catcher_ = new SignalCatcher;
 
   return instance_;
@@ -338,6 +330,52 @@
   return true;
 }
 
+void Runtime::InitLibraries() {
+  Thread* self = Thread::Current();
+  JNIEnv* env = self->GetJniEnv();
+
+  // Must be in the kNative state for JNI-based method registration.
+  ScopedThreadStateChange tsc(self, Thread::kNative);
+
+  // First set up the native methods provided by the runtime itself.
+  RegisterRuntimeNativeMethods(env);
+
+  // Now set up libcore, which is just a JNI library with a JNI_OnLoad.
+  // Most JNI libraries can just use System.loadLibrary, but you can't
+  // if you're the library that implements System.loadLibrary!
+  JniConstants::init(env);
+  LoadJniLibrary(instance_->GetJavaVM(), "javacore");
+}
+
+void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) {
+#define REGISTER(FN) extern void FN(JNIEnv*); FN(env)
+  //REGISTER(register_dalvik_bytecode_OpcodeInfo);
+  //REGISTER(register_dalvik_system_DexFile);
+  //REGISTER(register_dalvik_system_VMDebug);
+  //REGISTER(register_dalvik_system_VMRuntime);
+  //REGISTER(register_dalvik_system_VMStack);
+  //REGISTER(register_dalvik_system_Zygote);
+  //REGISTER(register_java_lang_Class);
+  //REGISTER(register_java_lang_Object);
+  //REGISTER(register_java_lang_Runtime);
+  //REGISTER(register_java_lang_String);
+  //REGISTER(register_java_lang_System_); // The _ avoids collision with libcore.
+  //REGISTER(register_java_lang_Thread);
+  //REGISTER(register_java_lang_Throwable);
+  //REGISTER(register_java_lang_VMClassLoader);
+  //REGISTER(register_java_lang_reflect_AccessibleObject);
+  //REGISTER(register_java_lang_reflect_Array);
+  //REGISTER(register_java_lang_reflect_Constructor);
+  //REGISTER(register_java_lang_reflect_Field);
+  //REGISTER(register_java_lang_reflect_Method);
+  //REGISTER(register_java_lang_reflect_Proxy);
+  //REGISTER(register_java_util_concurrent_atomic_AtomicLong);
+  //REGISTER(register_org_apache_harmony_dalvik_ddmc_DdmServer);
+  //REGISTER(register_org_apache_harmony_dalvik_ddmc_DdmVmInternal);
+  //REGISTER(register_sun_misc_Unsafe);
+#undef REGISTER
+}
+
 void Runtime::DumpStatistics(std::ostream& os) {
   // TODO: dump other runtime statistics?
   os << "Loaded classes: " << class_linker_->NumLoadedClasses() << "\n";
diff --git a/src/runtime.h b/src/runtime.h
index 40db814..198bfeb 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -104,6 +104,8 @@
   void BlockSignals();
 
   bool Init(const Options& options, bool ignore_unrecognized);
+  void InitLibraries();
+  void RegisterRuntimeNativeMethods(JNIEnv*);
 
   // The default stack size for managed threads created by the runtime.
   size_t stack_size_;
diff --git a/src/scoped_jni_thread_state.h b/src/scoped_jni_thread_state.h
index dd39cc3..0da03d6 100644
--- a/src/scoped_jni_thread_state.h
+++ b/src/scoped_jni_thread_state.h
@@ -15,11 +15,11 @@
   explicit ScopedJniThreadState(JNIEnv* env)
       : env_(reinterpret_cast<JNIEnvExt*>(env)) {
     self_ = ThreadForEnv(env);
-    self_->SetState(Thread::kRunnable);
+    old_thread_state_ = self_->SetState(Thread::kRunnable);
   }
 
   ~ScopedJniThreadState() {
-    self_->SetState(Thread::kNative);
+    self_->SetState(old_thread_state_);
   }
 
   JNIEnvExt* Env() {
@@ -49,6 +49,7 @@
 
   JNIEnvExt* env_;
   Thread* self_;
+  Thread::State old_thread_state_;
   DISALLOW_COPY_AND_ASSIGN(ScopedJniThreadState);
 };
 
diff --git a/src/signal_catcher.cc b/src/signal_catcher.cc
index b83e71d..5979a0f 100644
--- a/src/signal_catcher.cc
+++ b/src/signal_catcher.cc
@@ -74,6 +74,24 @@
   Heap::CollectGarbage();
 }
 
+int WaitForSignal(Thread* thread, sigset_t& mask) {
+  ScopedThreadStateChange tsc(thread, Thread::kWaiting); // TODO: VMWAIT
+
+  // Signals for sigwait() must be blocked but not ignored.  We
+  // block signals like SIGQUIT for all threads, so the condition
+  // is met.  When the signal hits, we wake up, without any signal
+  // handlers being invoked.
+
+  // Sleep in sigwait() until a signal arrives. gdb causes EINTR failures.
+  int signal_number;
+  int rc = TEMP_FAILURE_RETRY(sigwait(&mask, &signal_number));
+  if (rc != 0) {
+    PLOG(FATAL) << "sigwait failed";
+  }
+
+  return signal_number;
+}
+
 void* SignalCatcher::Run(void*) {
   CHECK(Runtime::Current()->AttachCurrentThread("Signal Catcher", NULL, true));
   Thread* self = Thread::Current();
@@ -88,31 +106,12 @@
   sigaddset(&mask, SIGUSR1);
 
   while (true) {
-    self->SetState(Thread::kWaiting); // TODO: VMWAIT
-
-    // Signals for sigwait() must be blocked but not ignored.  We
-    // block signals like SIGQUIT for all threads, so the condition
-    // is met.  When the signal hits, we wake up, without any signal
-    // handlers being invoked.
-
-    // Sleep in sigwait() until a signal arrives. gdb causes EINTR failures.
-    int signal_number;
-    int rc = TEMP_FAILURE_RETRY(sigwait(&mask, &signal_number));
-    if (rc != 0) {
-      PLOG(FATAL) << "sigwait failed";
-    }
-
-    if (!halt_) {
-      LOG(INFO) << *self << ": reacting to signal " << signal_number;
-    }
-
-    // Set our status to runnable, self-suspending if GC in progress.
-    self->SetState(Thread::kRunnable);
-
+    int signal_number = WaitForSignal(self, mask);
     if (halt_) {
       return NULL;
     }
 
+    LOG(INFO) << *self << ": reacting to signal " << signal_number;
     switch (signal_number) {
     case SIGQUIT:
       HandleSigQuit();
diff --git a/src/thread.h b/src/thread.h
index 41bb0da..86e78b7 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -320,8 +320,10 @@
     return state_;
   }
 
-  void SetState(State new_state) {
+  State SetState(State new_state) {
+    State old_state = state_;
     state_ = new_state;
+    return old_state;
   }
 
   static ThreadOffset SuspendCountOffset() {
@@ -531,8 +533,7 @@
     }
     Thread::State old_state;
     if (current_thread != NULL) {
-      old_state = current_thread->GetState();
-      current_thread->SetState(Thread::kWaiting);  // TODO: VMWAIT
+      old_state = current_thread->SetState(Thread::kWaiting);  // TODO: VMWAIT
     } else {
       // happens during VM shutdown
       old_state = Thread::kUnknown;  // TODO: something else
@@ -553,6 +554,22 @@
   DISALLOW_COPY_AND_ASSIGN(ThreadListLock);
 };
 
+class ScopedThreadStateChange {
+ public:
+  ScopedThreadStateChange(Thread* thread, Thread::State new_state) : thread_(thread) {
+    old_thread_state_ = thread_->SetState(new_state);
+  }
+
+  ~ScopedThreadStateChange() {
+    thread_->SetState(old_thread_state_);
+  }
+
+ private:
+  Thread* thread_;
+  Thread::State old_thread_state_;
+  DISALLOW_COPY_AND_ASSIGN(ScopedThreadStateChange);
+};
+
 }  // namespace art
 
 #endif  // ART_SRC_THREAD_H_