Generating stub code for registering a jni function at runtime.

On ARM and x86. Added a unit test.

Change-Id: I6b1ee09ad18295108f406ce21d73555796ecbba6
diff --git a/src/jni_compiler.cc b/src/jni_compiler.cc
index bb1e965..9da1ab0 100644
--- a/src/jni_compiler.cc
+++ b/src/jni_compiler.cc
@@ -17,7 +17,15 @@
 
 namespace art {
 
-JniCompiler::JniCompiler(InstructionSet insns) {
+namespace arm {
+ByteArray* CreateJniStub();
+}
+
+namespace x86 {
+ByteArray* CreateJniStub();
+}
+
+JniCompiler::JniCompiler(InstructionSet insns) : jni_stub_(NULL) {
   if (insns == kThumb2) {
     // currently only ARM code generation is supported
     instruction_set_ = kArm;
@@ -60,6 +68,17 @@
   // Cache of IsStatic as we call it often enough
   const bool is_static = native_method->IsStatic();
 
+  // 0. native_method->RegisterNative(jni_stub_ stuff). Note that jni_stub_ will invoke dlsym.
+  if (jni_stub_ == NULL) {
+    if (instruction_set_ == kArm) {
+      jni_stub_ = arm::CreateJniStub();
+    } else {
+      jni_stub_ = x86::CreateJniStub();
+    }
+  }
+  native_method->RegisterNative(jni_stub_->GetData());
+  // TODO: Need to make sure that the stub is copied into the image. I.e.,
+  // ByteArray* needs to be reachable either as a root or from the object graph.
 
   // 1. Build the frame
   const size_t frame_size(jni_conv->FrameSize());
diff --git a/src/jni_compiler.h b/src/jni_compiler.h
index ec2e23c..25514b9 100644
--- a/src/jni_compiler.h
+++ b/src/jni_compiler.h
@@ -5,6 +5,7 @@
 
 #include "constants.h"
 #include "macros.h"
+#include "object.h"
 
 namespace art {
 
@@ -37,6 +38,8 @@
 
   InstructionSet instruction_set_;
 
+  ByteArray* jni_stub_;  // Stub to perform native method symbol lookup
+
   DISALLOW_COPY_AND_ASSIGN(JniCompiler);
 };
 
diff --git a/src/jni_compiler_test.cc b/src/jni_compiler_test.cc
index 343ea1d..216350b 100644
--- a/src/jni_compiler_test.cc
+++ b/src/jni_compiler_test.cc
@@ -52,8 +52,10 @@
     }
     ASSERT_TRUE(jmethod_ != NULL);
 
-    JNINativeMethod methods[] = {{method_name, method_sig, native_fnptr}};
-    ASSERT_EQ(JNI_OK, env_->RegisterNatives(jklass_, methods, 1));
+    if (native_fnptr) {
+      JNINativeMethod methods[] = {{method_name, method_sig, native_fnptr}};
+      ASSERT_EQ(JNI_OK, env_->RegisterNatives(jklass_, methods, 1));
+    }
 
     jmethodID constructor = env_->GetMethodID(jklass_, "<init>", "()V");
     jobj_ = env_->NewObject(jklass_, constructor);
@@ -92,6 +94,22 @@
   EXPECT_EQ(2, gJava_MyClass_foo_calls);
 }
 
+TEST_F(JniCompilerTest, CompileAndRunIntMethodThroughStub) {
+  SetupForTest(false,
+               "bar",
+               "(I)I",
+               NULL /* dlsym will find &Java_MyClass_bar later */);
+
+  std::string path("libarttest.so");
+  std::string reason;
+  ASSERT_TRUE(Runtime::Current()->GetJavaVM()->LoadNativeLibrary(
+      path, const_cast<ClassLoader*>(class_loader_), reason))
+      << path << ": " << reason;
+
+  jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 24);
+  EXPECT_EQ(25, result);
+}
+
 int gJava_MyClass_fooI_calls = 0;
 jint Java_MyClass_fooI(JNIEnv* env, jobject thisObj, jint x) {
   EXPECT_EQ(1u, Thread::Current()->NumSirtReferences());
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 83a147f..954fafd 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -25,6 +25,25 @@
 
 namespace art {
 
+void* FindNativeMethod(Thread* thread) {
+  DCHECK(Thread::Current() == thread);
+
+  Method* method = const_cast<Method*>(thread->GetCurrentMethod());
+  DCHECK(method != NULL);
+
+  // Lookup symbol address for method, on failure we'll return NULL with an
+  // exception set, otherwise we return the address of the method we found.
+  void* native_code = thread->GetJniEnv()->vm->FindCodeForNativeMethod(method);
+  if (native_code == NULL) {
+    DCHECK(thread->IsExceptionPending());
+    return NULL;
+  } else {
+    // Register so that future calls don't come here
+    method->RegisterNative(native_code);
+    return native_code;
+  }
+}
+
 /*
  * Add a local reference for an object to the current stack frame.  When
  * the native function returns, the reference will be discarded.
@@ -452,6 +471,7 @@
   SharedLibrary(const std::string& path, void* handle, Object* class_loader)
       : path_(path),
         handle_(handle),
+        class_loader_(class_loader),
         jni_on_load_lock_("JNI_OnLoad lock"),
         jni_on_load_thread_id_(Thread::Current()->GetThinLockId()),
         jni_on_load_result_(kPending) {
diff --git a/src/jni_internal.h b/src/jni_internal.h
index a723b32..20aa716 100644
--- a/src/jni_internal.h
+++ b/src/jni_internal.h
@@ -22,6 +22,7 @@
 class Thread;
 
 void JniAbort(const char* jni_function_name);
+void* FindNativeMethod(Thread* thread);
 
 template<typename T> T Decode(JNIEnv*, jobject);
 template<typename T> T AddLocalReference(JNIEnv*, const Object*);
diff --git a/src/jni_tests.cc b/src/jni_tests.cc
new file mode 100644
index 0000000..934ccac
--- /dev/null
+++ b/src/jni_tests.cc
@@ -0,0 +1,9 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include "jni.h"
+#include <stdio.h>
+
+extern "C"
+JNIEXPORT jint JNICALL Java_MyClass_bar(JNIEnv* env, jobject thisObj, jint count) {
+  return count + 1;
+}
diff --git a/src/stub_arm.cc b/src/stub_arm.cc
new file mode 100644
index 0000000..2e2f6a6
--- /dev/null
+++ b/src/stub_arm.cc
@@ -0,0 +1,56 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include "assembler_arm.h"
+#include "jni_internal.h"
+#include "object.h"
+
+#define __ assembler->
+
+namespace art {
+namespace arm {
+
+ByteArray* CreateJniStub() {
+  UniquePtr<ArmAssembler> assembler( static_cast<ArmAssembler*>(Assembler::Create(kArm)) );
+
+  RegList save = (1 << R0) | (1 << R1) | (1 << R2) | (1 << R3) | (1 << LR);
+
+  // Build frame and save registers. Save 5 registers.
+  __ PushList(save);
+  // Ensure 16-byte alignment
+  __ AddConstant(SP, -12);
+
+  // Pass Thread::Current() in R0
+  __ mov(R0, ShifterOperand(R9));
+
+  // Call FindNativeMethod
+  __ LoadImmediate(R12, reinterpret_cast<int32_t>(&FindNativeMethod));
+  __ blx(R12);
+
+  // Save result of FindNativeMethod in R12
+  __ mov(R12, ShifterOperand(R0));
+
+  // Restore registers (including outgoing arguments)
+  __ AddConstant(SP, 12);
+  __ PopList(save);
+
+  __ cmp(R12, ShifterOperand(0));
+
+  // If R12 != 0 tail call into native code
+  __ mov(PC, ShifterOperand(R12), NE);
+
+  // Return to caller to handle exception
+  __ mov(PC, ShifterOperand(LR));
+
+  assembler->EmitSlowPaths();
+
+  size_t cs = assembler->CodeSize();
+  ByteArray* jni_stub = ByteArray::Alloc(cs);
+  CHECK(jni_stub != NULL);
+  MemoryRegion code(jni_stub->GetData(), jni_stub->GetLength());
+  assembler->FinalizeInstructions(code);
+
+  return jni_stub;
+}
+
+} // namespace arm
+} // namespace art
diff --git a/src/stub_x86.cc b/src/stub_x86.cc
new file mode 100644
index 0000000..bddfc6c
--- /dev/null
+++ b/src/stub_x86.cc
@@ -0,0 +1,47 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include "assembler_x86.h"
+#include "jni_internal.h"
+#include "object.h"
+
+#define __ assembler->
+
+namespace art {
+namespace x86 {
+
+ByteArray* CreateJniStub() {
+  UniquePtr<X86Assembler> assembler( static_cast<X86Assembler*>(Assembler::Create(kX86)) );
+
+  // Pad stack to ensure 16-byte alignment
+  __ pushl(Immediate(0));
+  __ pushl(Immediate(0));
+  __ pushl(Immediate(0));
+  __ fs()->movl(ECX, Address::Absolute(Thread::SelfOffset()));
+  __ pushl(ECX);  // Thread*
+
+  __ Call(reinterpret_cast<int32_t>(&FindNativeMethod), X86ManagedRegister::FromCpuRegister(ECX));
+
+  __ addl(ESP, Immediate(16));
+
+  Label no_native_code_found;  // forward declaration
+  __ cmpl(EAX, Immediate(0));
+  __ j(kEqual, &no_native_code_found);
+
+  __ jmp(EAX);  // Tail call into native code
+
+  __ Bind(&no_native_code_found);
+  __ ret(); // return to caller to handle exception
+
+  assembler->EmitSlowPaths();
+
+  size_t cs = assembler->CodeSize();
+  ByteArray* jni_stub = ByteArray::Alloc(cs);
+  CHECK(jni_stub != NULL);
+  MemoryRegion code(jni_stub->GetData(), jni_stub->GetLength());
+  assembler->FinalizeInstructions(code);
+
+  return jni_stub;
+}
+
+} // namespace x86
+} // namespace art