Add the runtime lookup of native method implementations.

Plus other bits of cleanup.

Change-Id: I8584001d7eeb118f8e29c4a62652a18b94333be8
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index a95ff81..00976c1 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -2,9 +2,11 @@
 
 #include "jni_internal.h"
 
-#include <cstdarg>
 #include <dlfcn.h>
 #include <sys/mman.h>
+
+#include <cstdarg>
+#include <map>
 #include <utility>
 #include <vector>
 
@@ -28,9 +30,9 @@
 
 // TODO: this should be in our anonymous namespace, but is currently needed
 // for testing in "jni_internal_test.cc".
-bool EnsureInvokeStub(Method* method) {
+void EnsureInvokeStub(Method* method) {
   if (method->GetInvokeStub() != NULL) {
-    return true;
+    return;
   }
   // TODO: use signature to find a matching stub
   // TODO: failed, acquire a lock on the stub table
@@ -46,98 +48,8 @@
   MemoryRegion region(addr, length);
   assembler.FinalizeInstructions(region);
   method->SetInvokeStub(reinterpret_cast<Method::InvokeStub*>(region.pointer()));
-  return true;
 }
 
-// TODO: this can't be in our anonymous namespace because of the map in JavaVM.
-class SharedLibrary {
-public:
-  SharedLibrary(const std::string& path, void* handle, Object* class_loader)
-      : path_(path),
-        handle_(handle),
-        jni_on_load_lock_(Mutex::Create("JNI_OnLoad lock")),
-        jni_on_load_tid_(Thread::Current()->GetId()),
-        jni_on_load_result_(kPending) {
-    pthread_cond_init(&jni_on_load_cond_, NULL);
-  }
-
-  ~SharedLibrary() {
-    delete jni_on_load_lock_;
-  }
-
-  Object* GetClassLoader() {
-    return class_loader_;
-  }
-
-  /*
-   * Check the result of an earlier call to JNI_OnLoad on this library.  If
-   * the call has not yet finished in another thread, wait for it.
-   */
-  bool CheckOnLoadResult(JavaVMExt* vm) {
-    Thread* self = Thread::Current();
-    if (jni_on_load_tid_ == self->GetId()) {
-      // Check this so we don't end up waiting for ourselves.  We need
-      // to return "true" so the caller can continue.
-      LOG(INFO) << *self << " recursive attempt to load library "
-                << "\"" << path_ << "\"";
-      return true;
-    }
-
-    MutexLock mu(jni_on_load_lock_);
-    while (jni_on_load_result_ == kPending) {
-      if (vm->verbose_jni) {
-        LOG(INFO) << "[" << *self << " waiting for \"" << path_ << "\" "
-                  << "JNI_OnLoad...]";
-      }
-      Thread::State old_state = self->GetState();
-      self->SetState(Thread::kWaiting); // TODO: VMWAIT
-      pthread_cond_wait(&jni_on_load_cond_, &(jni_on_load_lock_->lock_impl_));
-      self->SetState(old_state);
-    }
-
-    bool okay = (jni_on_load_result_ == kOkay);
-    if (vm->verbose_jni) {
-      LOG(INFO) << "[Earlier JNI_OnLoad for \"" << path_ << "\" "
-                << (okay ? "succeeded" : "failed") << "]";
-    }
-    return okay;
-  }
-
-  void SetResult(bool result) {
-    jni_on_load_result_ = result ? kOkay : kFailed;
-    jni_on_load_tid_ = 0;
-
-    // Broadcast a wakeup to anybody sleeping on the condition variable.
-    MutexLock mu(jni_on_load_lock_);
-    pthread_cond_broadcast(&jni_on_load_cond_);
-  }
-
- private:
-  enum JNI_OnLoadState {
-    kPending,
-    kFailed,
-    kOkay,
-  };
-
-  // Path to library "/system/lib/libjni.so".
-  std::string path_;
-
-  // The void* returned by dlopen(3).
-  void* handle_;
-
-  // The ClassLoader this library is associated with.
-  Object* class_loader_;
-
-  // Guards remaining items.
-  Mutex* jni_on_load_lock_;
-  // Wait for JNI_OnLoad in other thread.
-  pthread_cond_t jni_on_load_cond_;
-  // Recursive invocation guard.
-  uint32_t jni_on_load_tid_;
-  // Result of earlier JNI_OnLoad call.
-  JNI_OnLoadState jni_on_load_result_;
-};
-
 namespace {
 
 // Entry/exit processing for all JNI calls.
@@ -459,11 +371,7 @@
     return NULL;
   }
 
-  bool success = EnsureInvokeStub(method);
-  if (!success) {
-    // TODO: throw OutOfMemoryException
-    return NULL;
-  }
+  EnsureInvokeStub(method);
 
   return reinterpret_cast<jmethodID>(AddWeakGlobalReference(ts, method));
 }
@@ -603,8 +511,164 @@
   return runtime->AttachCurrentThread(args.name, p_env, as_daemon) ? JNI_OK : JNI_ERR;
 }
 
+class SharedLibrary {
+ public:
+  SharedLibrary(const std::string& path, void* handle, Object* class_loader)
+      : path_(path),
+        handle_(handle),
+        jni_on_load_lock_(Mutex::Create("JNI_OnLoad lock")),
+        jni_on_load_tid_(Thread::Current()->GetId()),
+        jni_on_load_result_(kPending) {
+    pthread_cond_init(&jni_on_load_cond_, NULL);
+  }
+
+  ~SharedLibrary() {
+    delete jni_on_load_lock_;
+  }
+
+  Object* GetClassLoader() {
+    return class_loader_;
+  }
+
+  std::string GetPath() {
+    return path_;
+  }
+
+  /*
+   * Check the result of an earlier call to JNI_OnLoad on this library.  If
+   * the call has not yet finished in another thread, wait for it.
+   */
+  bool CheckOnLoadResult(JavaVMExt* vm) {
+    Thread* self = Thread::Current();
+    if (jni_on_load_tid_ == self->GetId()) {
+      // Check this so we don't end up waiting for ourselves.  We need
+      // to return "true" so the caller can continue.
+      LOG(INFO) << *self << " recursive attempt to load library "
+                << "\"" << path_ << "\"";
+      return true;
+    }
+
+    MutexLock mu(jni_on_load_lock_);
+    while (jni_on_load_result_ == kPending) {
+      if (vm->verbose_jni) {
+        LOG(INFO) << "[" << *self << " waiting for \"" << path_ << "\" "
+                  << "JNI_OnLoad...]";
+      }
+      Thread::State old_state = self->GetState();
+      self->SetState(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);
+    if (vm->verbose_jni) {
+      LOG(INFO) << "[Earlier JNI_OnLoad for \"" << path_ << "\" "
+                << (okay ? "succeeded" : "failed") << "]";
+    }
+    return okay;
+  }
+
+  void SetResult(bool result) {
+    jni_on_load_result_ = result ? kOkay : kFailed;
+    jni_on_load_tid_ = 0;
+
+    // Broadcast a wakeup to anybody sleeping on the condition variable.
+    MutexLock mu(jni_on_load_lock_);
+    pthread_cond_broadcast(&jni_on_load_cond_);
+  }
+
+  void* FindSymbol(const std::string& symbol_name) {
+    return dlsym(handle_, symbol_name.c_str());
+  }
+
+ private:
+  enum JNI_OnLoadState {
+    kPending,
+    kFailed,
+    kOkay,
+  };
+
+  // Path to library "/system/lib/libjni.so".
+  std::string path_;
+
+  // The void* returned by dlopen(3).
+  void* handle_;
+
+  // The ClassLoader this library is associated with.
+  Object* class_loader_;
+
+  // Guards remaining items.
+  Mutex* jni_on_load_lock_;
+  // Wait for JNI_OnLoad in other thread.
+  pthread_cond_t jni_on_load_cond_;
+  // Recursive invocation guard.
+  uint32_t jni_on_load_tid_;
+  // Result of earlier JNI_OnLoad call.
+  JNI_OnLoadState jni_on_load_result_;
+};
+
 }  // namespace
 
+// This exists mainly to keep implementation details out of the header file.
+class Libraries {
+ public:
+  Libraries() {
+  }
+
+  ~Libraries() {
+    // Delete our map values. (The keys will be cleaned up by the map itself.)
+    for (It it = libraries_.begin(); it != libraries_.end(); ++it) {
+      delete it->second;
+    }
+  }
+
+  SharedLibrary* Get(const std::string& path) {
+    return libraries_[path];
+  }
+
+  void Put(const std::string& path, SharedLibrary* library) {
+    libraries_[path] = library;
+  }
+
+  // See section 11.3 "Linking Native Methods" of the JNI spec.
+  void* FindNativeMethod(const Method* m) {
+    std::string jni_short_name(JniShortName(m));
+    std::string jni_long_name(JniLongName(m));
+    ClassLoader* declaring_class_loader = m->GetDeclaringClass()->GetClassLoader();
+    for (It it = libraries_.begin(); it != libraries_.end(); ++it) {
+      SharedLibrary* library = it->second;
+      if (library->GetClassLoader() != declaring_class_loader) {
+        // We only search libraries loaded by the appropriate ClassLoader.
+        continue;
+      }
+      // Try the short name then the long name...
+      void* fn = library->FindSymbol(jni_short_name);
+      if (fn == NULL) {
+        fn = library->FindSymbol(jni_long_name);
+      }
+      if (fn != NULL) {
+        if (Runtime::Current()->GetJavaVM()->verbose_jni) {
+          LOG(INFO) << "[Found native code for " << PrettyMethod(m, true)
+                    << " in \"" << library->GetPath() << "\"]";
+        }
+        return fn;
+      }
+    }
+    std::string detail;
+    detail += "No implementation found for ";
+    detail += PrettyMethod(m, true);
+    LOG(ERROR) << detail;
+    Thread::Current()->ThrowNewException("Ljava/lang/UnsatisfiedLinkError;",
+        "%s", detail.c_str());
+    return NULL;
+  }
+
+ private:
+  typedef std::map<std::string, SharedLibrary*>::iterator It; // TODO: C++0x auto
+
+  std::map<std::string, SharedLibrary*> libraries_;
+};
+
 class JNI {
  public:
 
@@ -1837,7 +1901,7 @@
     size_t byte_count = s->GetUtfLength();
     char* bytes = new char[byte_count + 1];
     if (bytes == NULL) {
-      UNIMPLEMENTED(WARNING) << "need to Throw OOME";
+      ts.Self()->ThrowOutOfMemoryError();
       return NULL;
     }
     const uint16_t* chars = s->GetCharArray()->GetData() + s->GetOffset();
@@ -2641,7 +2705,9 @@
       globals_lock(Mutex::Create("JNI global reference table lock")),
       globals(kGlobalsInitial, kGlobalsMax, kGlobal),
       weak_globals_lock(Mutex::Create("JNI weak global reference table lock")),
-      weak_globals(kWeakGlobalsInitial, kWeakGlobalsMax, kWeakGlobal) {
+      weak_globals(kWeakGlobalsInitial, kWeakGlobalsMax, kWeakGlobal),
+      libraries_lock(Mutex::Create("JNI shared libraries map lock")),
+      libraries(new Libraries) {
   functions = &gInvokeInterface;
 }
 
@@ -2649,10 +2715,10 @@
   delete pins_lock;
   delete globals_lock;
   delete weak_globals_lock;
+  delete libraries_lock;
+  delete libraries;
 }
 
-/*
- */
 bool JavaVMExt::LoadNativeLibrary(const std::string& path, ClassLoader* class_loader, std::string& detail) {
   detail.clear();
 
@@ -2660,7 +2726,12 @@
   // matches, return successfully without doing anything.
   // TODO: for better results we should canonicalize the pathname (or even compare
   // inodes). This implementation is fine if everybody is using System.loadLibrary.
-  SharedLibrary* library = libraries[path];
+  SharedLibrary* library;
+  {
+    // TODO: move the locking (and more of this logic) into Libraries.
+    MutexLock mu(libraries_lock);
+    library = libraries->Get(path);
+  }
   if (library != NULL) {
     if (library->GetClassLoader() != class_loader) {
       // The library will be associated with class_loader. The JNI
@@ -2727,69 +2798,90 @@
   }
 
   // Create a new entry.
-  library = new SharedLibrary(path, handle, class_loader);
-
-  libraries[path] = library;
-
-  //  if (pNewEntry != pActualEntry) {
-  //    LOG(INFO) << "WOW: we lost a race to add a shared library (\"" << path << "\" ClassLoader=" << class_loader <<")";
-  //    freeSharedLibEntry(pNewEntry);
-  //    return CheckOnLoadResult(this, pActualEntry);
-  //  } else
   {
-    if (verbose_jni) {
-      LOG(INFO) << "[Added shared library \"" << path << "\" for ClassLoader " << class_loader << "]";
+    // TODO: move the locking (and more of this logic) into Libraries.
+    MutexLock mu(libraries_lock);
+    library = libraries->Get(path);
+    if (library != NULL) {
+      LOG(INFO) << "WOW: we lost a race to add shared library: "
+                << "\"" << path << "\" ClassLoader=" << class_loader;
+      return library->CheckOnLoadResult(this);
     }
-
-    bool result = true;
-    void* sym = dlsym(handle, "JNI_OnLoad");
-    if (sym == NULL) {
-      if (verbose_jni) {
-        LOG(INFO) << "[No JNI_OnLoad found in \"" << path << "\"]";
-      }
-    } else {
-      // Call JNI_OnLoad.  We have to override the current class
-      // loader, which will always be "null" since the stuff at the
-      // top of the stack is around Runtime.loadLibrary().  (See
-      // the comments in the JNI FindClass function.)
-      typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
-      JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
-      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 = (*jni_on_load)(this, NULL);
-      self->SetState(old_state);
-
-      self->SetClassLoaderOverride(old_class_loader);;
-
-      if (version != JNI_VERSION_1_2 &&
-      version != JNI_VERSION_1_4 &&
-      version != JNI_VERSION_1_6) {
-        LOG(WARNING) << "JNI_OnLoad in \"" << path << "\" returned "
-                     << "bad version: " << version;
-        // It's unwise to call dlclose() here, but we can mark it
-        // as bad and ensure that future load attempts will fail.
-        // We don't know how far JNI_OnLoad got, so there could
-        // be some partially-initialized stuff accessible through
-        // newly-registered native method calls.  We could try to
-        // unregister them, but that doesn't seem worthwhile.
-        result = false;
-      } else {
-        if (verbose_jni) {
-          LOG(INFO) << "[Returned " << (result ? "successfully" : "failure")
-                    << " from JNI_OnLoad in \"" << path << "\"]";
-        }
-      }
-    }
-
-    library->SetResult(result);
-    return result;
+    library = new SharedLibrary(path, handle, class_loader);
+    libraries->Put(path, library);
   }
+
+  if (verbose_jni) {
+    LOG(INFO) << "[Added shared library \"" << path << "\" for ClassLoader " << class_loader << "]";
+  }
+
+  bool result = true;
+  void* sym = dlsym(handle, "JNI_OnLoad");
+  if (sym == NULL) {
+    if (verbose_jni) {
+      LOG(INFO) << "[No JNI_OnLoad found in \"" << path << "\"]";
+    }
+  } else {
+    // Call JNI_OnLoad.  We have to override the current class
+    // loader, which will always be "null" since the stuff at the
+    // top of the stack is around Runtime.loadLibrary().  (See
+    // the comments in the JNI FindClass function.)
+    typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
+    JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
+    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 = (*jni_on_load)(this, NULL);
+    self->SetState(old_state);
+
+    self->SetClassLoaderOverride(old_class_loader);;
+
+    if (version != JNI_VERSION_1_2 &&
+    version != JNI_VERSION_1_4 &&
+    version != JNI_VERSION_1_6) {
+      LOG(WARNING) << "JNI_OnLoad in \"" << path << "\" returned "
+                   << "bad version: " << version;
+      // It's unwise to call dlclose() here, but we can mark it
+      // as bad and ensure that future load attempts will fail.
+      // We don't know how far JNI_OnLoad got, so there could
+      // be some partially-initialized stuff accessible through
+      // newly-registered native method calls.  We could try to
+      // unregister them, but that doesn't seem worthwhile.
+      result = false;
+    } else {
+      if (verbose_jni) {
+        LOG(INFO) << "[Returned " << (result ? "successfully" : "failure")
+                  << " from JNI_OnLoad in \"" << path << "\"]";
+      }
+    }
+  }
+
+  library->SetResult(result);
+  return result;
+}
+
+void* JavaVMExt::FindCodeForNativeMethod(Method* m) {
+  CHECK(m->IsNative());
+
+  Class* c = m->GetDeclaringClass();
+
+  // If this is a static method, it could be called before the class
+  // has been initialized.
+  if (m->IsStatic()) {
+    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c)) {
+      return NULL;
+    }
+  } else {
+    CHECK_GE(c->GetStatus(), Class::kStatusInitializing);
+  }
+
+  MutexLock mu(libraries_lock);
+  return libraries->FindNativeMethod(m);
 }
 
 }  // namespace art