Move JNI compiler tests to use pure JNI.

Implement JNI upcalls for x86.
Fix off by 1 bug in JNI calling convention for x86.
Fix bugs in ARM JNI upcalls.
Straw man JNI invoke nonvirtual implementations.
Match va_start with va_end in JNI internals.

Change-Id: I64d62eca41ac726ae0d007c1f41d2193db5be82e
diff --git a/src/calling_convention_arm.cc b/src/calling_convention_arm.cc
index 0300ad1..743445d 100644
--- a/src/calling_convention_arm.cc
+++ b/src/calling_convention_arm.cc
@@ -31,10 +31,7 @@
 // Managed runtime calling convention
 
 bool ManagedRuntimeCallingConvention::IsCurrentParamInRegister() {
-  if (itr_slots_ < 3) {
-    return true;
-  }
-  return false;  // everything else on the stack
+  return itr_slots_ < 3;
 }
 
 bool ManagedRuntimeCallingConvention::IsCurrentParamOnStack() {
@@ -67,8 +64,7 @@
 
 FrameOffset ManagedRuntimeCallingConvention::CurrentParamStackOffset() {
   CHECK_GE(itr_slots_, 3u);
-  return FrameOffset(displacement_.Int32Value() +
-             ((itr_slots_ - 3) * kPointerSize));
+  return FrameOffset(displacement_.Int32Value() + (itr_slots_ * kPointerSize));
 }
 
 // JNI calling convention
diff --git a/src/calling_convention_x86.cc b/src/calling_convention_x86.cc
index 9420849..ed95eb0 100644
--- a/src/calling_convention_x86.cc
+++ b/src/calling_convention_x86.cc
@@ -43,7 +43,9 @@
 }
 
 FrameOffset ManagedRuntimeCallingConvention::CurrentParamStackOffset() {
-  return FrameOffset(displacement_.Int32Value() + (itr_slots_ * kPointerSize));
+  return FrameOffset(displacement_.Int32Value() +   // displacement
+                     kPointerSize +                 // Method*
+                     (itr_slots_ * kPointerSize));  // offset into in args
 }
 
 // JNI calling convention
diff --git a/src/indirect_reference_table.cc b/src/indirect_reference_table.cc
index b30ce53..ec52cc6 100644
--- a/src/indirect_reference_table.cc
+++ b/src/indirect_reference_table.cc
@@ -16,6 +16,7 @@
 
 #include "indirect_reference_table.h"
 #include "reference_table.h"
+#include "utils.h"
 
 #include <cstdlib>
 
@@ -82,7 +83,8 @@
   size_t topIndex = segmentState.parts.topIndex;
 
   DCHECK(obj != NULL);
-  //DCHECK(dvmIsHeapAddress(obj));
+  // TODO: stronger sanity check on the object (such as in heap)
+  DCHECK(IsAligned(reinterpret_cast<intptr_t>(obj), 8));
   DCHECK(table_ != NULL);
   DCHECK_LE(alloc_entries_, max_entries_);
   DCHECK_GE(segmentState.parts.numHoles, prevState.parts.numHoles);
diff --git a/src/jni_compiler.cc b/src/jni_compiler.cc
index 440dda5..077e7bb 100644
--- a/src/jni_compiler.cc
+++ b/src/jni_compiler.cc
@@ -59,7 +59,7 @@
     // Check handle offset is within frame
     CHECK_LT(handle_offset.Uint32Value(), frame_size);
     jni_asm->LoadRef(jni_conv.InterproceduralScratchRegister(),
-                     mr_conv.MethodRegister(), Method::ClassOffset());
+                     mr_conv.MethodRegister(), Method::DeclaringClassOffset());
     jni_asm->ValidateRef(jni_conv.InterproceduralScratchRegister(), false);
     jni_asm->StoreRef(handle_offset, jni_conv.InterproceduralScratchRegister());
     jni_conv.Next();  // handlerized so move to next argument
diff --git a/src/jni_compiler_test.cc b/src/jni_compiler_test.cc
index 854df88..39ba6ed 100644
--- a/src/jni_compiler_test.cc
+++ b/src/jni_compiler_test.cc
@@ -6,7 +6,9 @@
 #include "class_linker.h"
 #include "common_test.h"
 #include "dex_file.h"
+#include "indirect_reference_table.h"
 #include "jni_compiler.h"
+#include "jni_internal.h"
 #include "mem_map.h"
 #include "runtime.h"
 #include "scoped_ptr.h"
@@ -19,143 +21,173 @@
  protected:
   virtual void SetUp() {
     CommonTest::SetUp();
-    // Create thunk code that performs the native to managed transition
-    thunk_code_.reset(MemMap::Map(kPageSize, PROT_READ | PROT_WRITE | PROT_EXEC));
-    CHECK(thunk_code_ !=  NULL);
-    Assembler thk_asm;
-    // TODO: shouldn't have machine specific code in a general purpose file
-#if defined(__i386__)
-    thk_asm.pushl(EDI);                   // preserve EDI
-    thk_asm.movl(EAX, Address(ESP, 8));   // EAX = method->GetCode()
-    thk_asm.movl(EDI, Address(ESP, 12));  // EDI = method
-    thk_asm.pushl(Immediate(0));          // push pad
-    thk_asm.pushl(Immediate(0));          // push pad
-    thk_asm.pushl(Address(ESP, 44));      // push pad  or jlong high
-    thk_asm.pushl(Address(ESP, 44));      // push jint or jlong low
-    thk_asm.pushl(Address(ESP, 44));      // push jint or jlong high
-    thk_asm.pushl(Address(ESP, 44));      // push jint or jlong low
-    thk_asm.pushl(Address(ESP, 44));      // push jobject
-    thk_asm.call(EAX);                    // Continue in method->GetCode()
-    thk_asm.addl(ESP, Immediate(28));     // pop arguments
-    thk_asm.popl(EDI);                    // restore EDI
-    thk_asm.ret();
-#elif defined(__arm__)
-    thk_asm.AddConstant(SP, -32);         // Build frame
-    thk_asm.StoreToOffset(kStoreWord, LR, SP, 28);  // Spill link register
-    thk_asm.StoreToOffset(kStoreWord, R9, SP, 24);  // Spill R9
-    thk_asm.mov(R12, ShifterOperand(R0));  // R12 = method->GetCode()
-    thk_asm.mov(R0,  ShifterOperand(R1));  // R0  = method
-    thk_asm.mov(R9,  ShifterOperand(R2));  // R9  = Thread::Current()
-    thk_asm.mov(R1,  ShifterOperand(R3));  // R1  = arg1 (jint/jlong low)
-    thk_asm.LoadFromOffset(kLoadWord, R3, SP, 44);  // R3 = arg5 (pad/jlong high)
-    thk_asm.StoreToOffset(kStoreWord, R3, SP, 4);
-    thk_asm.LoadFromOffset(kLoadWord, R3, SP, 40);  // R3 = arg4 (jint/jlong low)
-    thk_asm.StoreToOffset(kStoreWord, R3, SP, 0);
-    thk_asm.LoadFromOffset(kLoadWord, R3, SP, 36);  // R3 = arg3 (jint/jlong high)
-    thk_asm.LoadFromOffset(kLoadWord, R2, SP, 32);  // R2 = arg2 (jint/jlong high)
-    thk_asm.blx(R12);                     // Branch and link R12
-    thk_asm.LoadFromOffset(kLoadWord, LR, SP, 28);  // Fill link register
-    thk_asm.LoadFromOffset(kLoadWord, R9, SP, 24);  // Fill R9
-    thk_asm.AddConstant(SP, 32);          // Remove frame
-    thk_asm.mov(PC, ShifterOperand(LR));  // Return
-#else
-#error Unimplemented
-#endif
-    size_t cs = thk_asm.CodeSize();
-    MemoryRegion code(thunk_code_->GetAddress(), cs);
-    thk_asm.FinalizeInstructions(code);
-    thunk_entry1_ = reinterpret_cast<jint (*)(const void*, art::Method*,
-                                              Thread*, jobject, jint, jint,
-                                              jint)
-                                    >(code.pointer());
-    thunk_entry2_ = reinterpret_cast<jdouble (*)(const void*, art::Method*,
-                                                 Thread*, jobject, jdouble,
-                                                 jdouble)
-                                    >(code.pointer());
+    dex_.reset(OpenDexFileBase64(kMyClassNativesDex));
+    class_loader_ = AllocPathClassLoader(dex_.get());
   }
 
-  virtual void TearDown() {
-    // Release thunk code
-    CHECK(runtime_->DetachCurrentThread());
+  void SetupForTest(bool direct, const char* method_name,
+                    const char* method_sig, void* native_fnptr) {
+    const char* class_name = "LMyClass;";
+    Class* klass = class_linker_->FindClass(class_name, class_loader_);
+    ASSERT_TRUE(klass != NULL);
+
+    Method* method;
+    if (direct) {
+      method = klass->FindDirectMethod(method_name, method_sig);
+    } else {
+      method = klass->FindVirtualMethod(method_name, method_sig);
+    }
+    ASSERT_TRUE(method != NULL);
+
+    // Compile the native method
+    jni_compiler.Compile(&jni_asm, method);
+
+    env_ = Thread::Current()->GetJniEnv();
+
+    // TODO: when we support class loaders - env->FindClass(class_name);
+    IndirectReferenceTable& locals = reinterpret_cast<JNIEnvExt*>(env_)->locals;
+    uint32_t cookie = IRT_FIRST_SEGMENT; // TODO
+    IndirectRef klass_ref = locals.Add(cookie, klass);
+    jklass_ = reinterpret_cast<jclass>(klass_ref);
+    if (direct) {
+      jmethod_ = env_->GetStaticMethodID(jklass_, method_name, method_sig);
+    } else {
+      jmethod_ = env_->GetMethodID(jklass_, method_name, method_sig);
+    }
+    ASSERT_TRUE(jmethod_ != NULL);
+
+    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);
+    ASSERT_TRUE(jobj_ != NULL);
   }
 
-  // Run generated code associated with method passing and returning int size
-  // arguments
-  jvalue RunMethod(Method* method, jvalue a, jvalue b, jvalue c, jvalue d) {
-    jvalue result;
-    // sanity checks
-    EXPECT_NE(static_cast<void*>(NULL), method->GetCode());
-    EXPECT_EQ(0u, Thread::Current()->NumShbHandles());
-    EXPECT_EQ(Thread::kRunnable, Thread::Current()->GetState());
-    // perform call
-    result.i = (*thunk_entry1_)(method->GetCode(), method, Thread::Current(),
-                                a.l, b.i, c.i, d.i);
-    // sanity check post-call
-    EXPECT_EQ(0u, Thread::Current()->NumShbHandles());
-    EXPECT_EQ(Thread::kRunnable, Thread::Current()->GetState());
-    return result;
-  }
-
-  // Run generated code associated with method passing and returning double size
-  // arguments
-  jvalue RunMethodD(Method* method, jvalue a, jvalue b, jvalue c) {
-    jvalue result;
-    // sanity checks
-    EXPECT_NE(static_cast<void*>(NULL), method->GetCode());
-    EXPECT_EQ(0u, Thread::Current()->NumShbHandles());
-    EXPECT_EQ(Thread::kRunnable, Thread::Current()->GetState());
-    // perform call
-    result.d = (*thunk_entry2_)(method->GetCode(), method, Thread::Current(),
-                                a.l, b.d, c.d);
-    // sanity check post-call
-    EXPECT_EQ(0u, Thread::Current()->NumShbHandles());
-    EXPECT_EQ(Thread::kRunnable, Thread::Current()->GetState());
-    return result;
-  }
-
-  scoped_ptr<MemMap> thunk_code_;
-  jint (*thunk_entry1_)(const void*, Method*, Thread*, jobject, jint, jint,
-                        jint);
-  jdouble (*thunk_entry2_)(const void*, Method*, Thread*, jobject, jdouble,
-                           jdouble);
+ public:
+  static jclass jklass_;
+  static jobject jobj_;
+ protected:
+  scoped_ptr<DexFile> dex_;
+  PathClassLoader* class_loader_;
+  Assembler jni_asm;
+  JniCompiler jni_compiler;
+  JNIEnv* env_;
+  jmethodID jmethod_;
 };
 
+jclass JniCompilerTest::jklass_;
+jobject JniCompilerTest::jobj_;
+
 int gJava_MyClass_foo_calls = 0;
-void Java_MyClass_foo(JNIEnv*, jobject) {
+void Java_MyClass_foo(JNIEnv* env, jobject thisObj) {
   EXPECT_EQ(1u, Thread::Current()->NumShbHandles());
   EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
+  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
+  EXPECT_TRUE(thisObj != NULL);
+  // TODO: check JNIEnv and thisObj are sane
+  // EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
   gJava_MyClass_foo_calls++;
 }
 
+TEST_F(JniCompilerTest, CompileAndRunNoArgMethod) {
+  SetupForTest(false, "foo", "()V", reinterpret_cast<void*>(&Java_MyClass_foo));
+
+  EXPECT_EQ(0, gJava_MyClass_foo_calls);
+  env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
+  EXPECT_EQ(1, gJava_MyClass_foo_calls);
+  env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
+  EXPECT_EQ(2, gJava_MyClass_foo_calls);
+}
+
 int gJava_MyClass_fooI_calls = 0;
-jint Java_MyClass_fooI(JNIEnv*, jobject, jint x) {
+jint Java_MyClass_fooI(JNIEnv* env, jobject thisObj, jint x) {
   EXPECT_EQ(1u, Thread::Current()->NumShbHandles());
   EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
+  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
+  EXPECT_TRUE(thisObj != NULL);
+  // TODO: check JNIEnv and thisObj are sane
+  // EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
   gJava_MyClass_fooI_calls++;
   return x;
 }
 
+TEST_F(JniCompilerTest, CompileAndRunIntMethod) {
+  SetupForTest(false, "fooI", "(I)I",
+               reinterpret_cast<void*>(&Java_MyClass_fooI));
+
+  EXPECT_EQ(0, gJava_MyClass_fooI_calls);
+  jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 42);
+  EXPECT_EQ(42, result);
+  EXPECT_EQ(1, gJava_MyClass_fooI_calls);
+  result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 0xCAFED00D);
+  EXPECT_EQ(static_cast<jint>(0xCAFED00D), result);
+  EXPECT_EQ(2, gJava_MyClass_fooI_calls);
+}
+
 int gJava_MyClass_fooII_calls = 0;
-jint Java_MyClass_fooII(JNIEnv*, jobject, jint x, jint y) {
+jint Java_MyClass_fooII(JNIEnv* env, jobject thisObj, jint x, jint y) {
   EXPECT_EQ(1u, Thread::Current()->NumShbHandles());
   EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
+  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
+  EXPECT_TRUE(thisObj != NULL);
+  // TODO: check JNIEnv and thisObj are sane
+  // EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
   gJava_MyClass_fooII_calls++;
   return x - y;  // non-commutative operator
 }
 
+TEST_F(JniCompilerTest, CompileAndRunIntIntMethod) {
+  SetupForTest(false, "fooII", "(II)I",
+               reinterpret_cast<void*>(&Java_MyClass_fooII));
+
+  EXPECT_EQ(0, gJava_MyClass_fooII_calls);
+  jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 99, 10);
+  EXPECT_EQ(99 - 10, result);
+  EXPECT_EQ(1, gJava_MyClass_fooII_calls);
+  result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 0xCAFEBABE,
+                                         0xCAFED00D);
+  EXPECT_EQ(static_cast<jint>(0xCAFEBABE - 0xCAFED00D), result);
+  EXPECT_EQ(2, gJava_MyClass_fooII_calls);
+}
+
 int gJava_MyClass_fooDD_calls = 0;
-jdouble Java_MyClass_fooDD(JNIEnv*, jobject, jdouble x, jdouble y) {
+jdouble Java_MyClass_fooDD(JNIEnv* env, jobject thisObj, jdouble x, jdouble y) {
   EXPECT_EQ(1u, Thread::Current()->NumShbHandles());
   EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
+  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
+  EXPECT_TRUE(thisObj != NULL);
+  // TODO: check JNIEnv and thisObj are sane
+  // EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
   gJava_MyClass_fooDD_calls++;
   return x - y;  // non-commutative operator
 }
 
+TEST_F(JniCompilerTest, CompileAndRunDoubleDoubleMethod) {
+  SetupForTest(false, "fooDD", "(DD)D",
+               reinterpret_cast<void*>(&Java_MyClass_fooDD));
+
+  EXPECT_EQ(0, gJava_MyClass_fooDD_calls);
+  jdouble result = env_->CallNonvirtualDoubleMethod(jobj_, jklass_, jmethod_,
+                                                    99.0, 10.0);
+  EXPECT_EQ(99.0 - 10.0, result);
+  EXPECT_EQ(1, gJava_MyClass_fooDD_calls);
+  jdouble a = 3.14159265358979323846;
+  jdouble b = 0.69314718055994530942;
+  result = env_->CallNonvirtualDoubleMethod(jobj_, jklass_, jmethod_, a, b);
+  EXPECT_EQ(a - b, result);
+  EXPECT_EQ(2, gJava_MyClass_fooDD_calls);
+}
+
 int gJava_MyClass_fooIOO_calls = 0;
-jobject Java_MyClass_fooIOO(JNIEnv*, jobject thisObject, jint x, jobject y,
+jobject Java_MyClass_fooIOO(JNIEnv* env, jobject thisObj, jint x, jobject y,
                             jobject z) {
   EXPECT_EQ(3u, Thread::Current()->NumShbHandles());
   EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
+  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
+  EXPECT_TRUE(thisObj != NULL);
+  // TODO: check JNIEnv and thisObj are sane
+  // EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
   gJava_MyClass_fooIOO_calls++;
   switch (x) {
     case 1:
@@ -163,16 +195,50 @@
     case 2:
       return z;
     default:
-      return thisObject;
+      return thisObj;
   }
 }
 
+TEST_F(JniCompilerTest, CompileAndRunIntObjectObjectMethod) {
+  SetupForTest(false, "fooIOO",
+               "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+               reinterpret_cast<void*>(&Java_MyClass_fooIOO));
+
+  EXPECT_EQ(0, gJava_MyClass_fooIOO_calls);
+  jobject result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, 0, NULL, NULL);
+  EXPECT_TRUE(env_->IsSameObject(jobj_, result));
+  EXPECT_EQ(1, gJava_MyClass_fooIOO_calls);
+
+  result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, 0, NULL, jklass_);
+  EXPECT_TRUE(env_->IsSameObject(jobj_, result));
+  EXPECT_EQ(2, gJava_MyClass_fooIOO_calls);
+  result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, 1, NULL, jklass_);
+  EXPECT_TRUE(env_->IsSameObject(NULL, result));
+  EXPECT_EQ(3, gJava_MyClass_fooIOO_calls);
+  result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, 2, NULL, jklass_);
+  EXPECT_TRUE(env_->IsSameObject(jklass_, result));
+  EXPECT_EQ(4, gJava_MyClass_fooIOO_calls);
+
+  result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, 0, jklass_, NULL);
+  EXPECT_TRUE(env_->IsSameObject(jobj_, result));
+  EXPECT_EQ(5, gJava_MyClass_fooIOO_calls);
+  result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, 1, jklass_, NULL);
+  EXPECT_TRUE(env_->IsSameObject(jklass_, result));
+  EXPECT_EQ(6, gJava_MyClass_fooIOO_calls);
+  result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, 2, jklass_, NULL);
+  EXPECT_TRUE(env_->IsSameObject(NULL, result));
+  EXPECT_EQ(7, gJava_MyClass_fooIOO_calls);
+}
+
 int gJava_MyClass_fooSIOO_calls = 0;
 jobject Java_MyClass_fooSIOO(JNIEnv* env, jclass klass, jint x, jobject y,
                              jobject z) {
-  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
   EXPECT_EQ(3u, Thread::Current()->NumShbHandles());
   EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
+  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
+  EXPECT_TRUE(klass != NULL);
+  // TODO: check JNIEnv and klass are sane
+  // EXPECT_TRUE(env->IsInstanceOf(JniCompilerTest::jobj_, klass));
   gJava_MyClass_fooSIOO_calls++;
   switch (x) {
     case 1:
@@ -184,11 +250,47 @@
   }
 }
 
+
+TEST_F(JniCompilerTest, CompileAndRunStaticIntObjectObjectMethod) {
+  SetupForTest(true, "fooSIOO",
+               "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+               reinterpret_cast<void*>(&Java_MyClass_fooSIOO));
+
+  EXPECT_EQ(0, gJava_MyClass_fooSIOO_calls);
+  jobject result = env_->CallStaticObjectMethod(jklass_, jmethod_, 0, NULL, NULL);
+  EXPECT_TRUE(env_->IsSameObject(jklass_, result));
+  EXPECT_EQ(1, gJava_MyClass_fooSIOO_calls);
+
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 0, NULL, jobj_);
+  EXPECT_TRUE(env_->IsSameObject(jklass_, result));
+  EXPECT_EQ(2, gJava_MyClass_fooSIOO_calls);
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 1, NULL, jobj_);
+  EXPECT_TRUE(env_->IsSameObject(NULL, result));
+  EXPECT_EQ(3, gJava_MyClass_fooSIOO_calls);
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 2, NULL, jobj_);
+  EXPECT_TRUE(env_->IsSameObject(jobj_, result));
+  EXPECT_EQ(4, gJava_MyClass_fooSIOO_calls);
+
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 0, jobj_, NULL);
+  EXPECT_TRUE(env_->IsSameObject(jklass_, result));
+  EXPECT_EQ(5, gJava_MyClass_fooSIOO_calls);
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 1, jobj_, NULL);
+  EXPECT_TRUE(env_->IsSameObject(jobj_, result));
+  EXPECT_EQ(6, gJava_MyClass_fooSIOO_calls);
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 2, jobj_, NULL);
+  EXPECT_TRUE(env_->IsSameObject(NULL, result));
+  EXPECT_EQ(7, gJava_MyClass_fooSIOO_calls);
+}
+
 int gJava_MyClass_fooSSIOO_calls = 0;
-jobject Java_MyClass_fooSSIOO(JNIEnv*, jclass klass, jint x, jobject y,
+jobject Java_MyClass_fooSSIOO(JNIEnv* env, jclass klass, jint x, jobject y,
                              jobject z) {
   EXPECT_EQ(3u, Thread::Current()->NumShbHandles());
   EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
+  EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
+  EXPECT_TRUE(klass != NULL);
+  // TODO: check JNIEnv and klass are sane
+  // EXPECT_TRUE(env->IsInstanceOf(JniCompilerTest::jobj_, klass));
   gJava_MyClass_fooSSIOO_calls++;
   switch (x) {
     case 1:
@@ -200,307 +302,34 @@
   }
 }
 
-TEST_F(JniCompilerTest, CompileAndRunNoArgMethod) {
-  scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
-  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
-
-  Class* klass = class_linker_->FindClass("LMyClass;", class_loader);
-  ASSERT_TRUE(klass != NULL);
-
-  Method* method = klass->FindVirtualMethod("foo", "()V");
-  ASSERT_TRUE(method != NULL);
-
-  Assembler jni_asm;
-  JniCompiler jni_compiler;
-  jni_compiler.Compile(&jni_asm, method);
-
-  // TODO: should really use JNIEnv to RegisterNative, but missing a
-  // complete story on this, so hack the RegisterNative below
-  // JNIEnv* env = Thread::Current()->GetJniEnv();
-  // JNINativeMethod methods[] = {{"foo", "()V", (void*)&Java_MyClass_foo}};
-  method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_foo));
-
-  jvalue a;
-  a.l = (jobject)NULL;
-  gJava_MyClass_foo_calls = 0;
-  RunMethod(method, a, a, a, a);
-  EXPECT_EQ(1, gJava_MyClass_foo_calls);
-  RunMethod(method, a, a, a, a);
-  EXPECT_EQ(2, gJava_MyClass_foo_calls);
-}
-
-TEST_F(JniCompilerTest, CompileAndRunIntMethod) {
-  scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
-  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
-
-  Class* klass = class_linker_->FindClass("LMyClass;", class_loader);
-  ASSERT_TRUE(klass != NULL);
-
-  Method* method = klass->FindVirtualMethod("fooI", "(I)I");
-  ASSERT_TRUE(method != NULL);
-
-  Assembler jni_asm;
-  JniCompiler jni_compiler;
-  jni_compiler.Compile(&jni_asm, method);
-
-  // TODO: should really use JNIEnv to RegisterNative, but missing a
-  // complete story on this, so hack the RegisterNative below
-  method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooI));
-
-  jvalue a, b, c;
-  a.l = (jobject)NULL;
-  b.i = 42;
-  EXPECT_EQ(0, gJava_MyClass_fooI_calls);
-  c = RunMethod(method, a, b, a, a);
-  ASSERT_EQ(42, c.i);
-  EXPECT_EQ(1, gJava_MyClass_fooI_calls);
-  b.i = 0xCAFED00D;
-  c = RunMethod(method, a, b, a, a);
-  ASSERT_EQ((jint)0xCAFED00D, c.i);
-  EXPECT_EQ(2, gJava_MyClass_fooI_calls);
-}
-
-TEST_F(JniCompilerTest, CompileAndRunIntIntMethod) {
-  scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
-  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
-
-  Class* klass = class_linker_->FindClass("LMyClass;", class_loader);
-  ASSERT_TRUE(klass != NULL);
-
-  Method* method = klass->FindVirtualMethod("fooII", "(II)I");
-  ASSERT_TRUE(method != NULL);
-
-  Assembler jni_asm;
-  JniCompiler jni_compiler;
-  jni_compiler.Compile(&jni_asm, method);
-
-  // TODO: should really use JNIEnv to RegisterNative, but missing a
-  // complete story on this, so hack the RegisterNative below
-  method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooII));
-
-  jvalue a, b, c, d;
-  a.l = (jobject)NULL;
-  b.i = 99;
-  c.i = 10;
-  EXPECT_EQ(0, gJava_MyClass_fooII_calls);
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ(99 - 10, d.i);
-  EXPECT_EQ(1, gJava_MyClass_fooII_calls);
-  b.i = 0xCAFEBABE;
-  c.i = 0xCAFED00D;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jint)(0xCAFEBABE - 0xCAFED00D), d.i);
-  EXPECT_EQ(2, gJava_MyClass_fooII_calls);
-}
-
-
-TEST_F(JniCompilerTest, CompileAndRunDoubleDoubleMethod) {
-  scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
-  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
-
-  Class* klass = class_linker_->FindClass("LMyClass;", class_loader);
-  ASSERT_TRUE(klass != NULL);
-
-  Method* method = klass->FindVirtualMethod("fooDD", "(DD)D");
-  ASSERT_TRUE(method != NULL);
-
-  Assembler jni_asm;
-  JniCompiler jni_compiler;
-  jni_compiler.Compile(&jni_asm, method);
-
-  // TODO: should really use JNIEnv to RegisterNative, but missing a
-  // complete story on this, so hack the RegisterNative below
-  method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooDD));
-
-  jvalue a, b, c, d;
-  a.l = (jobject)NULL;
-  b.d = 99;
-  c.d = 10;
-  EXPECT_EQ(0, gJava_MyClass_fooDD_calls);
-  d = RunMethodD(method, a, b, c);
-  ASSERT_EQ(b.d - c.d, d.d);
-  EXPECT_EQ(1, gJava_MyClass_fooDD_calls);
-  b.d = 3.14159265358979323846;
-  c.d = 0.69314718055994530942;
-  d = RunMethodD(method, a, b, c);
-  ASSERT_EQ(b.d - c.d, d.d);
-  EXPECT_EQ(2, gJava_MyClass_fooDD_calls);
-}
-
-TEST_F(JniCompilerTest, CompileAndRunIntObjectObjectMethod) {
-  scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
-  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
-
-  Class* klass = class_linker_->FindClass("LMyClass;", class_loader);
-  ASSERT_TRUE(klass != NULL);
-
-  Method* method = klass->FindVirtualMethod(
-      "fooIOO",
-      "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
-  ASSERT_TRUE(method != NULL);
-
-  Assembler jni_asm;
-  JniCompiler jni_compiler;
-  jni_compiler.Compile(&jni_asm, method);
-
-  // TODO: should really use JNIEnv to RegisterNative, but missing a
-  // complete story on this, so hack the RegisterNative below
-  method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooIOO));
-
-  jvalue a, b, c, d, e;
-  a.l = (jobject)NULL;
-  b.i = 0;
-  c.l = (jobject)NULL;
-  d.l = (jobject)NULL;
-  EXPECT_EQ(0, gJava_MyClass_fooIOO_calls);
-  e = RunMethod(method, a, b, c, d);
-  ASSERT_EQ((jobject)NULL, e.l);
-  EXPECT_EQ(1, gJava_MyClass_fooIOO_calls);
-  a.l = (jobject)8;
-  b.i = 0;
-  c.l = (jobject)NULL;
-  d.l = (jobject)16;
-  e = RunMethod(method, a, b, c, d);
-  ASSERT_EQ((jobject)8, e.l);
-  EXPECT_EQ(2, gJava_MyClass_fooIOO_calls);
-  b.i = 1;
-  e = RunMethod(method, a, b, c, d);
-  ASSERT_EQ((jobject)NULL, e.l);
-  EXPECT_EQ(3, gJava_MyClass_fooIOO_calls);
-  b.i = 2;
-  e = RunMethod(method, a, b, c, d);
-  ASSERT_EQ((jobject)16, e.l);
-  EXPECT_EQ(4, gJava_MyClass_fooIOO_calls);
-  a.l = (jobject)8;
-  b.i = 0;
-  c.l = (jobject)16;
-  d.l = (jobject)NULL;
-  e = RunMethod(method, a, b, c, d);
-  ASSERT_EQ((jobject)8, e.l);
-  EXPECT_EQ(5, gJava_MyClass_fooIOO_calls);
-  b.i = 1;
-  e = RunMethod(method, a, b, c, d);
-  ASSERT_EQ((jobject)16, e.l);
-  EXPECT_EQ(6, gJava_MyClass_fooIOO_calls);
-  b.i = 2;
-  e = RunMethod(method, a, b, c, d);
-  ASSERT_EQ((jobject)NULL, e.l);
-  EXPECT_EQ(7, gJava_MyClass_fooIOO_calls);
-}
-
-TEST_F(JniCompilerTest, CompileAndRunStaticIntObjectObjectMethod) {
-  scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
-  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
-
-  Class* klass = class_linker_->FindClass("LMyClass;", class_loader);
-  ASSERT_TRUE(klass != NULL);
-
-  Method* method = klass->FindDirectMethod(
-      "fooSIOO",
-      "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
-  ASSERT_TRUE(method != NULL);
-
-  Assembler jni_asm;
-  JniCompiler jni_compiler;
-  jni_compiler.Compile(&jni_asm, method);
-
-  // TODO: should really use JNIEnv to RegisterNative, but missing a
-  // complete story on this, so hack the RegisterNative below
-  method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooSIOO));
-
-  jvalue a, b, c, d;
-  a.i = 0;
-  b.l = (jobject)NULL;
-  c.l = (jobject)NULL;
-  EXPECT_EQ(0, gJava_MyClass_fooSIOO_calls);
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)method->GetClass(), d.l);
-  EXPECT_EQ(1, gJava_MyClass_fooSIOO_calls);
-  a.i = 0;
-  b.l = (jobject)NULL;
-  c.l = (jobject)16;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)method->GetClass(), d.l);
-  EXPECT_EQ(2, gJava_MyClass_fooSIOO_calls);
-  a.i = 1;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)NULL, d.l);
-  EXPECT_EQ(3, gJava_MyClass_fooSIOO_calls);
-  a.i = 2;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)16, d.l);
-  EXPECT_EQ(4, gJava_MyClass_fooSIOO_calls);
-  a.i = 0;
-  b.l = (jobject)16;
-  c.l = (jobject)NULL;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)method->GetClass(), d.l);
-  EXPECT_EQ(5, gJava_MyClass_fooSIOO_calls);
-  a.i = 1;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)16, d.l);
-  EXPECT_EQ(6, gJava_MyClass_fooSIOO_calls);
-  a.i = 2;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)NULL, d.l);
-  EXPECT_EQ(7, gJava_MyClass_fooSIOO_calls);
-}
-
 TEST_F(JniCompilerTest, CompileAndRunStaticSynchronizedIntObjectObjectMethod) {
-  scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
-  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
+  SetupForTest(true, "fooSSIOO",
+               "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+               reinterpret_cast<void*>(&Java_MyClass_fooSSIOO));
 
-  Class* klass = class_linker_->FindClass("LMyClass;", class_loader);
-  ASSERT_TRUE(klass != NULL);
-
-  Method* method = klass->FindDirectMethod(
-      "fooSSIOO",
-      "(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
-  ASSERT_TRUE(method != NULL);
-
-  Assembler jni_asm;
-  JniCompiler jni_compiler;
-  jni_compiler.Compile(&jni_asm, method);
-
-  // TODO: should really use JNIEnv to RegisterNative, but missing a
-  // complete story on this, so hack the RegisterNative below
-  method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooSSIOO));
-
-  jvalue a, b, c, d;
-  a.i = 0;
-  b.l = (jobject)NULL;
-  c.l = (jobject)NULL;
   EXPECT_EQ(0, gJava_MyClass_fooSSIOO_calls);
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)method->GetClass(), d.l);
+  jobject result = env_->CallStaticObjectMethod(jklass_, jmethod_, 0, NULL, NULL);
+  EXPECT_TRUE(env_->IsSameObject(jklass_, result));
   EXPECT_EQ(1, gJava_MyClass_fooSSIOO_calls);
-  a.i = 0;
-  b.l = (jobject)NULL;
-  c.l = (jobject)16;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)method->GetClass(), d.l);
+
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 0, NULL, jobj_);
+  EXPECT_TRUE(env_->IsSameObject(jklass_, result));
   EXPECT_EQ(2, gJava_MyClass_fooSSIOO_calls);
-  a.i = 1;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)NULL, d.l);
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 1, NULL, jobj_);
+  EXPECT_TRUE(env_->IsSameObject(NULL, result));
   EXPECT_EQ(3, gJava_MyClass_fooSSIOO_calls);
-  a.i = 2;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)16, d.l);
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 2, NULL, jobj_);
+  EXPECT_TRUE(env_->IsSameObject(jobj_, result));
   EXPECT_EQ(4, gJava_MyClass_fooSSIOO_calls);
-  a.i = 0;
-  b.l = (jobject)16;
-  c.l = (jobject)NULL;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)method->GetClass(), d.l);
+
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 0, jobj_, NULL);
+  EXPECT_TRUE(env_->IsSameObject(jklass_, result));
   EXPECT_EQ(5, gJava_MyClass_fooSSIOO_calls);
-  a.i = 1;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)16, d.l);
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 1, jobj_, NULL);
+  EXPECT_TRUE(env_->IsSameObject(jobj_, result));
   EXPECT_EQ(6, gJava_MyClass_fooSSIOO_calls);
-  a.i = 2;
-  d = RunMethod(method, a, b, c, a);
-  ASSERT_EQ((jobject)NULL, d.l);
+  result = env_->CallStaticObjectMethod(jklass_, jmethod_, 2, jobj_, NULL);
+  EXPECT_TRUE(env_->IsSameObject(NULL, result));
   EXPECT_EQ(7, gJava_MyClass_fooSSIOO_calls);
 }
 
@@ -510,41 +339,24 @@
   gSuspendCounterHandler_calls++;
   Thread::Current()->DecrementSuspendCount();
 }
+
 TEST_F(JniCompilerTest, SuspendCountAcknowledgement) {
-  scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
-  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
-
-  Class* klass = class_linker_->FindClass("LMyClass;", class_loader);
-  ASSERT_TRUE(klass != NULL);
-
-  Method* method = klass->FindVirtualMethod("fooI", "(I)I");
-  ASSERT_TRUE(method != NULL);
-
-  Assembler jni_asm;
-  JniCompiler jni_compiler;
-  jni_compiler.Compile(&jni_asm, method);
-
-  // TODO: should really use JNIEnv to RegisterNative, but missing a
-  // complete story on this, so hack the RegisterNative below
-  method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooI));
+  SetupForTest(false, "fooI", "(I)I",
+               reinterpret_cast<void*>(&Java_MyClass_fooI));
   Thread::Current()->RegisterSuspendCountEntryPoint(&SuspendCountHandler);
 
-  gSuspendCounterHandler_calls = 0;
   gJava_MyClass_fooI_calls = 0;
-  jvalue a, b, c;
-  a.l = (jobject)NULL;
-  b.i = 42;
-  c = RunMethod(method, a, b, a, a);
-  ASSERT_EQ(42, c.i);
+  jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 42);
+  EXPECT_EQ(42, result);
   EXPECT_EQ(1, gJava_MyClass_fooI_calls);
   EXPECT_EQ(0, gSuspendCounterHandler_calls);
   Thread::Current()->IncrementSuspendCount();
-  c = RunMethod(method, a, b, a, a);
-  ASSERT_EQ(42, c.i);
+  result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 42);
+  EXPECT_EQ(42, result);
   EXPECT_EQ(2, gJava_MyClass_fooI_calls);
   EXPECT_EQ(1, gSuspendCounterHandler_calls);
-  c = RunMethod(method, a, b, a, a);
-  ASSERT_EQ(42, c.i);
+  result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 42);
+  EXPECT_EQ(42, result);
   EXPECT_EQ(3, gJava_MyClass_fooI_calls);
   EXPECT_EQ(1, gSuspendCounterHandler_calls);
 }
@@ -555,38 +367,21 @@
   gExceptionHandler_calls++;
   Thread::Current()->ClearException();
 }
+
 TEST_F(JniCompilerTest, ExceptionHandling) {
-  scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
-  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
-
-  Class* klass = class_linker_->FindClass("LMyClass;", class_loader);
-  ASSERT_TRUE(klass != NULL);
-
-  Method* method = klass->FindVirtualMethod("foo", "()V");
-  ASSERT_TRUE(method != NULL);
-
-  Assembler jni_asm;
-  JniCompiler jni_compiler;
-  jni_compiler.Compile(&jni_asm, method);
-
-  // TODO: should really use JNIEnv to RegisterNative, but missing a
-  // complete story on this, so hack the RegisterNative below
-  method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_foo));
+  SetupForTest(false, "foo", "()V", reinterpret_cast<void*>(&Java_MyClass_foo));
   Thread::Current()->RegisterExceptionEntryPoint(&ExceptionHandler);
 
-  gExceptionHandler_calls = 0;
   gJava_MyClass_foo_calls = 0;
-  jvalue a;
-  a.l = (jobject)NULL;
-  RunMethod(method, a, a, a, a);
+  env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
   EXPECT_EQ(1, gJava_MyClass_foo_calls);
   EXPECT_EQ(0, gExceptionHandler_calls);
   // TODO: create a real exception here
-  Thread::Current()->SetException(reinterpret_cast<Object*>(8));
-  RunMethod(method, a, a, a, a);
+  Thread::Current()->SetException(reinterpret_cast<Object*>(jobj_));
+  env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
   EXPECT_EQ(2, gJava_MyClass_foo_calls);
   EXPECT_EQ(1, gExceptionHandler_calls);
-  RunMethod(method, a, a, a, a);
+  env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
   EXPECT_EQ(3, gJava_MyClass_foo_calls);
   EXPECT_EQ(1, gExceptionHandler_calls);
 }
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index fd66d73..9fd0e5f 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -20,6 +20,12 @@
 
 namespace art {
 
+jobject NewObjectV(JNIEnv* env, jclass clazz, jmethodID methodID, va_list args);
+void CallNonvirtualVoidMethodV(JNIEnv* env, jobject obj, jclass clazz,
+                               jmethodID methodID, va_list args);
+void CallNonvirtualVoidMethodA(JNIEnv* env, jobject obj, jclass clazz,
+                               jmethodID methodID, jvalue* args);
+
 enum JNI_OnLoadState {
   kPending = 0,     /* initial state, must be zero */
   kFailed,
@@ -499,28 +505,36 @@
   return arg_array.release();
 }
 
-JValue InvokeWithArgArray(ScopedJniThreadState& ts,
-    Object* obj, jmethodID method_id, byte* args) {
+JValue InvokeWithArgArray(ScopedJniThreadState& ts, jobject obj,
+                          jmethodID method_id, byte* args) {
   // TODO: DecodeReference
   Method* method = reinterpret_cast<Method*>(method_id);
+  Object* rcvr = Decode<Object*>(ts, obj);
   // Call the invoke stub associated with the method
   // Pass everything as arguments
   const Method::InvokeStub* stub = method->GetInvokeStub();
   CHECK(stub != NULL);
   JValue result;
-  (*stub)(method, obj, ts.Self(), args, &result);
+  // TODO: we should always have code associated with a method
+  if (method->GetCode()) {
+    (*stub)(method, rcvr, ts.Self(), args, &result);
+  } else {
+    // TODO: pretty print method here
+    LOG(WARNING) << "Not invoking method with no associated code";
+    result.j = 0;
+  }
   return result;
 }
 
-JValue InvokeWithJValues(ScopedJniThreadState& ts,
-    Object* obj, jmethodID method_id, jvalue* args) {
+JValue InvokeWithJValues(ScopedJniThreadState& ts, jobject obj,
+                         jmethodID method_id, jvalue* args) {
   Method* method = reinterpret_cast<Method*>(method_id);
   scoped_array<byte> arg_array(CreateArgArray(ts, method, args));
   return InvokeWithArgArray(ts, obj, method_id, arg_array.get());
 }
 
-JValue InvokeWithVarArgs(ScopedJniThreadState& ts,
-    Object* obj, jmethodID method_id, va_list args) {
+JValue InvokeWithVarArgs(ScopedJniThreadState& ts, jobject obj,
+                         jmethodID method_id, va_list args) {
   Method* method = reinterpret_cast<Method*>(method_id);
   scoped_array<byte> arg_array(CreateArgArray(ts, method, args));
   return InvokeWithArgArray(ts, obj, method_id, arg_array.get());
@@ -761,8 +775,8 @@
 
 jboolean IsSameObject(JNIEnv* env, jobject obj1, jobject obj2) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return JNI_FALSE;
+  return (Decode<Object*>(ts, obj1) == Decode<Object*>(ts, obj2))
+         ? JNI_TRUE : JNI_FALSE;
 }
 
 jint EnsureLocalCapacity(JNIEnv* env, jint) {
@@ -779,22 +793,31 @@
 
 jobject NewObject(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return NULL;
+  va_list args;
+  va_start(args, methodID);
+  jobject result = NewObjectV(env, clazz, methodID, args);
+  va_end(args);
+  return result;
 }
 
 jobject NewObjectV(JNIEnv* env,
     jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return NULL;
+  Class* klass = Decode<Class*>(ts, clazz);
+  Object* result = klass->NewInstance();
+  jobject local_result = AddLocalReference<jobject>(ts, result);
+  CallNonvirtualVoidMethodV(env, local_result, clazz, methodID, args);
+  return local_result;
 }
 
 jobject NewObjectA(JNIEnv* env,
     jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return NULL;
+  Class* klass = Decode<Class*>(ts, clazz);
+  Object* result = klass->NewInstance();
+  jobject local_result = AddLocalReference<jobjectArray>(ts, result);
+  CallNonvirtualVoidMethodA(env, local_result, clazz, methodID, args);
+  return local_result;
 }
 
 jclass GetObjectClass(JNIEnv* env, jobject obj) {
@@ -1056,208 +1079,223 @@
 jobject CallNonvirtualObjectMethod(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return NULL;
+  va_list ap;
+  va_start(ap, methodID);
+  JValue result = InvokeWithVarArgs(ts, obj, methodID, ap);
+  jobject local_result = AddLocalReference<jobject>(ts, result.l);
+  va_end(ap);
+  return local_result;
 }
 
 jobject CallNonvirtualObjectMethodV(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return NULL;
+  JValue result = InvokeWithVarArgs(ts, obj, methodID, args);
+  return AddLocalReference<jobject>(ts, result.l);
 }
 
 jobject CallNonvirtualObjectMethodA(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, jvalue*  args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return NULL;
+  JValue result = InvokeWithJValues(ts, obj, methodID, args);
+  return AddLocalReference<jobject>(ts, result.l);
 }
 
 jboolean CallNonvirtualBooleanMethod(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return JNI_FALSE;
+  va_list ap;
+  va_start(ap, methodID);
+  JValue result = InvokeWithVarArgs(ts, obj, methodID, ap);
+  va_end(ap);
+  return result.z;
 }
 
 jboolean CallNonvirtualBooleanMethodV(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return JNI_FALSE;
+  return InvokeWithVarArgs(ts, obj, methodID, args).z;
 }
 
 jboolean CallNonvirtualBooleanMethodA(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, jvalue*  args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return JNI_FALSE;
+  return InvokeWithJValues(ts, obj, methodID, args).z;
 }
 
 jbyte CallNonvirtualByteMethod(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  va_list ap;
+  va_start(ap, methodID);
+  JValue result = InvokeWithVarArgs(ts, obj, methodID, ap);
+  va_end(ap);
+  return result.b;
 }
 
 jbyte CallNonvirtualByteMethodV(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithVarArgs(ts, obj, methodID, args).b;
 }
 
 jbyte CallNonvirtualByteMethodA(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithJValues(ts, obj, methodID, args).b;
 }
 
 jchar CallNonvirtualCharMethod(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  va_list ap;
+  va_start(ap, methodID);
+  JValue result = InvokeWithVarArgs(ts, obj, methodID, ap);
+  va_end(ap);
+  return result.c;
 }
 
 jchar CallNonvirtualCharMethodV(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithVarArgs(ts, obj, methodID, args).c;
 }
 
 jchar CallNonvirtualCharMethodA(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithJValues(ts, obj, methodID, args).c;
 }
 
 jshort CallNonvirtualShortMethod(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  va_list ap;
+  va_start(ap, methodID);
+  JValue result = InvokeWithVarArgs(ts, obj, methodID, ap);
+  va_end(ap);
+  return result.s;
 }
 
 jshort CallNonvirtualShortMethodV(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithVarArgs(ts, obj, methodID, args).s;
 }
 
 jshort CallNonvirtualShortMethodA(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithJValues(ts, obj, methodID, args).s;
 }
 
 jint CallNonvirtualIntMethod(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  va_list ap;
+  va_start(ap, methodID);
+  JValue result = InvokeWithVarArgs(ts, obj, methodID, ap);
+  va_end(ap);
+  return result.i;
 }
 
 jint CallNonvirtualIntMethodV(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithVarArgs(ts, obj, methodID, args).i;
 }
 
 jint CallNonvirtualIntMethodA(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithJValues(ts, obj, methodID, args).i;
 }
 
 jlong CallNonvirtualLongMethod(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  va_list ap;
+  va_start(ap, methodID);
+  JValue result = InvokeWithVarArgs(ts, obj, methodID, ap);
+  va_end(ap);
+  return result.j;
 }
 
 jlong CallNonvirtualLongMethodV(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithVarArgs(ts, obj, methodID, args).j;
 }
 
 jlong CallNonvirtualLongMethodA(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithJValues(ts, obj, methodID, args).j;
 }
 
 jfloat CallNonvirtualFloatMethod(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  va_list ap;
+  va_start(ap, methodID);
+  JValue result = InvokeWithVarArgs(ts, obj, methodID, ap);
+  va_end(ap);
+  return result.f;
 }
 
 jfloat CallNonvirtualFloatMethodV(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithVarArgs(ts, obj, methodID, args).f;
 }
 
 jfloat CallNonvirtualFloatMethodA(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithJValues(ts, obj, methodID, args).f;
 }
 
 jdouble CallNonvirtualDoubleMethod(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  va_list ap;
+  va_start(ap, methodID);
+  JValue result = InvokeWithVarArgs(ts, obj, methodID, ap);
+  va_end(ap);
+  return result.d;
 }
 
 jdouble CallNonvirtualDoubleMethodV(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithVarArgs(ts, obj, methodID, args).d;
 }
 
 jdouble CallNonvirtualDoubleMethodA(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithJValues(ts, obj, methodID, args).d;
 }
 
 void CallNonvirtualVoidMethod(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
+  va_list ap;
+  va_start(ap, methodID);
+  InvokeWithVarArgs(ts, obj, methodID, ap);
+  va_end(ap);
 }
 
 void CallNonvirtualVoidMethodV(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
+  InvokeWithVarArgs(ts, obj, methodID, args);
 }
 
 void CallNonvirtualVoidMethodA(JNIEnv* env,
     jobject obj, jclass clazz, jmethodID methodID, jvalue*  args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
+  InvokeWithJValues(ts, obj, methodID, args);
 }
 
 jfieldID GetFieldID(JNIEnv* env,
@@ -1409,7 +1447,9 @@
   va_list ap;
   va_start(ap, methodID);
   JValue result = InvokeWithVarArgs(ts, NULL, methodID, ap);
-  return AddLocalReference<jobject>(ts, result.l);
+  jobject local_result = AddLocalReference<jobject>(ts, result.l);
+  va_end(ap);
+  return local_result;
 }
 
 jobject CallStaticObjectMethodV(JNIEnv* env,
@@ -1431,7 +1471,9 @@
   ScopedJniThreadState ts(env);
   va_list ap;
   va_start(ap, methodID);
-  return InvokeWithVarArgs(ts, NULL, methodID, ap).z;
+  JValue result = InvokeWithVarArgs(ts, NULL, methodID, ap);
+  va_end(ap);
+  return result.z;
 }
 
 jboolean CallStaticBooleanMethodV(JNIEnv* env,
@@ -1450,7 +1492,9 @@
   ScopedJniThreadState ts(env);
   va_list ap;
   va_start(ap, methodID);
-  return InvokeWithVarArgs(ts, NULL, methodID, ap).b;
+  JValue result = InvokeWithVarArgs(ts, NULL, methodID, ap);
+  va_end(ap);
+  return result.b;
 }
 
 jbyte CallStaticByteMethodV(JNIEnv* env,
@@ -1469,7 +1513,9 @@
   ScopedJniThreadState ts(env);
   va_list ap;
   va_start(ap, methodID);
-  return InvokeWithVarArgs(ts, NULL, methodID, ap).c;
+  JValue result = InvokeWithVarArgs(ts, NULL, methodID, ap);
+  va_end(ap);
+  return result.c;
 }
 
 jchar CallStaticCharMethodV(JNIEnv* env,
@@ -1488,7 +1534,9 @@
   ScopedJniThreadState ts(env);
   va_list ap;
   va_start(ap, methodID);
-  return InvokeWithVarArgs(ts, NULL, methodID, ap).s;
+  JValue result = InvokeWithVarArgs(ts, NULL, methodID, ap);
+  va_end(ap);
+  return result.s;
 }
 
 jshort CallStaticShortMethodV(JNIEnv* env,
@@ -1507,7 +1555,9 @@
   ScopedJniThreadState ts(env);
   va_list ap;
   va_start(ap, methodID);
-  return InvokeWithVarArgs(ts, NULL, methodID, ap).i;
+  JValue result = InvokeWithVarArgs(ts, NULL, methodID, ap);
+  va_end(ap);
+  return result.i;
 }
 
 jint CallStaticIntMethodV(JNIEnv* env,
@@ -1526,7 +1576,9 @@
   ScopedJniThreadState ts(env);
   va_list ap;
   va_start(ap, methodID);
-  return InvokeWithVarArgs(ts, NULL, methodID, ap).j;
+  JValue result = InvokeWithVarArgs(ts, NULL, methodID, ap);
+  va_end(ap);
+  return result.j;
 }
 
 jlong CallStaticLongMethodV(JNIEnv* env,
@@ -1545,7 +1597,9 @@
   ScopedJniThreadState ts(env);
   va_list ap;
   va_start(ap, methodID);
-  return InvokeWithVarArgs(ts, NULL, methodID, ap).f;
+  JValue result = InvokeWithVarArgs(ts, NULL, methodID, ap);
+  va_end(ap);
+  return result.f;
 }
 
 jfloat CallStaticFloatMethodV(JNIEnv* env,
@@ -1564,7 +1618,9 @@
   ScopedJniThreadState ts(env);
   va_list ap;
   va_start(ap, methodID);
-  return InvokeWithVarArgs(ts, NULL, methodID, ap).d;
+  JValue result = InvokeWithVarArgs(ts, NULL, methodID, ap);
+  va_end(ap);
+  return result.d;
 }
 
 jdouble CallStaticDoubleMethodV(JNIEnv* env,
@@ -1584,6 +1640,7 @@
   va_list ap;
   va_start(ap, methodID);
   InvokeWithVarArgs(ts, NULL, methodID, ap);
+  va_end(ap);
 }
 
 void CallStaticVoidMethodV(JNIEnv* env,
diff --git a/src/jni_internal_arm.cc b/src/jni_internal_arm.cc
index b2777d6..e126b6e 100644
--- a/src/jni_internal_arm.cc
+++ b/src/jni_internal_arm.cc
@@ -19,8 +19,8 @@
 // R0 = method pointer
 // R1 = receiver pointer or NULL for static methods
 // R2 = (managed) thread pointer
-// R3 = argument array or NULL for void arugment methods
-// [SP] = JValue result or NULL for void returns
+// R3 = argument array or NULL for no argument methods
+// [SP] = JValue* result or NULL for void returns
 //
 // As the JNI call has already transitioned the thread into the
 // "running" state the remaining responsibilities of this routine are
@@ -29,77 +29,76 @@
 // the stack, if needed.  On return, the thread register must be
 // shuffled and the return value must be store into the result JValue.
 void CreateInvokeStub(Assembler* assembler, Method* method) {
-  size_t num_arg_words = method->NumArgArrayBytes() / kWordSize;
-  // TODO: the incoming argument array should have implicit arguments.
-  size_t max_register_words = method->IsStatic() ? 3 : 2;
-  size_t num_register_words = std::min(num_arg_words, max_register_words);
-  size_t num_stack_words = num_arg_words - num_register_words;
+  // Size of frame - spill of R9/LR + Method* + possible receiver + arg array
+  size_t unpadded_frame_size = (3 * kPointerSize) +
+                               (method->IsStatic() ? 0 : kPointerSize) +
+                               method->NumArgArrayBytes();
+  size_t frame_size = RoundUp(unpadded_frame_size, kStackAlignment);
 
-  // For now we allocate stack space for stacked outgoing arguments.
-  size_t stack_parameters_size = num_stack_words*kWordSize;
-  if (num_arg_words != RoundUp(num_arg_words,8)) {
-    // Ensure 8-byte alignment.
-    stack_parameters_size += kWordSize;
-  }
-
+  // Spill R9 and LR
   RegList save = (1 << R9);
   __ PushList(save | (1 << LR));
 
-  // Allocate a frame large enough for the stacked arguments.
-  __ AddConstant(SP, -stack_parameters_size);
-
   // Move the managed thread pointer into R9.
   __ mov(R9, ShifterOperand(R2));
 
-  // Move all stacked arguments into place.
-  size_t first_stack_word = num_register_words;
-  if (num_arg_words > max_register_words) {
-    for (size_t i = first_stack_word, j = 0; i < num_arg_words; ++i, ++j) {
-      int r3_offset = i * kWordSize;
-      int sp_offset = j * kWordSize;
-      __ LoadFromOffset(kLoadWord, IP, R3, r3_offset);
-      __ StoreToOffset(kStoreWord, IP, SP, sp_offset);
-    }
+  // Move frame down for arguments less 2 pushed values above
+  __ AddConstant(SP, -frame_size + (2 * kPointerSize));
+
+  // Can either get 3 or 2 arguments into registers
+  size_t reg_bytes = (method->IsStatic() ? 3 : 2) * kPointerSize;
+  // Bytes passed by stack
+  size_t stack_bytes;
+  if( method->NumArgArrayBytes() > reg_bytes){
+    stack_bytes = method->NumArgArrayBytes() - reg_bytes;
+  } else {
+    stack_bytes = 0;
+    reg_bytes = method->NumArgArrayBytes();
+  }
+
+  // Copy values by stack
+  for(size_t off = 0; off < stack_bytes; off += kPointerSize) {
+    // we're displaced off of r3 by bytes that'll go in registers
+    int r3_offset = reg_bytes + off;
+    __ LoadFromOffset(kLoadWord, IP, R3, r3_offset);
+
+    // we're displaced off of the arguments by the spill space for the incoming
+    // arguments and the Method*
+    int sp_offset = reg_bytes + kPointerSize + off;
+    __ StoreToOffset(kStoreWord, IP, SP, sp_offset);
   }
 
   // Move all the register arguments into place.
   if (method->IsStatic()) {
-    if (num_register_words > 0) {
+    if (reg_bytes > 0) {
       __ LoadFromOffset(kLoadWord, R1, R3, 0);
-    }
-    if (num_register_words > 1) {
-      __ LoadFromOffset(kLoadWord, R2, R3, 4);
-    }
-    if (num_register_words > 2) {
-      __ LoadFromOffset(kLoadWord, R3, R3, 8);
+      if (reg_bytes > 4) {
+        __ LoadFromOffset(kLoadWord, R2, R3, 4);
+        if (reg_bytes > 8) {
+          __ LoadFromOffset(kLoadWord, R3, R3, 8);
+        }
+      }
     }
   } else {
-    if (num_register_words > 0) {
+    if (reg_bytes > 0) {
       __ LoadFromOffset(kLoadWord, R2, R3, 0);
-    }
-    if (num_register_words > 1) {
-      __ LoadFromOffset(kLoadWord, R3, R3, 4);
+      if (reg_bytes > 4) {
+        __ LoadFromOffset(kLoadWord, R3, R3, 4);
+      }
     }
   }
 
-  // Allocate the spill area for outgoing arguments.
-  __ AddConstant(SP, -((num_register_words+1)*kWordSize));
-
   // Load the code pointer we are about to call.
   __ LoadFromOffset(kLoadWord, IP, R0, method->GetCodeOffset());
 
   // Do the call.
   __ blx(IP);
 
-  // Deallocate the spill area for outgoing arguments.
-  __ AddConstant(SP, ((num_register_words+1)*kWordSize));
-
   // If the method returns a value, store it to the result pointer.
   char ch = method->GetShorty()[0];
   if (ch != 'V') {
-    // Load the result JValue pointer.  It is the first stacked
-    // argument so it is stored above the stacked R9 and LR values.
-    __ LoadFromOffset(kLoadWord, IP, SP, stack_parameters_size + 2*kWordSize);
+    // Load the result JValue pointer of the stub caller's out args.
+    __ LoadFromOffset(kLoadWord, IP, SP, frame_size);
     if (ch == 'D' || ch == 'J') {
       __ StoreToOffset(kStoreWordPair, R0, IP, 0);
     } else {
@@ -107,7 +106,10 @@
     }
   }
 
-  __ AddConstant(SP, stack_parameters_size);
+  // Remove the frame less the spilled R9 and LR
+  __ AddConstant(SP, frame_size - (2 * kPointerSize));
+
+  // Pop R9 and the LR into PC
   __ PopList(save | (1 << PC));
 }
 
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index 9957565..f51b6c9 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -76,11 +76,9 @@
   env_->ExceptionClear();
 
   // Check that java.lang.Object.equals() does exist
-#if defined(__arm__)
   method = env_->GetMethodID(jlobject, "equals", "(Ljava/lang/Object;)Z");
   EXPECT_NE(static_cast<jmethodID>(NULL), method);
   EXPECT_FALSE(env_->ExceptionCheck());
-#endif
 
   // Check that GetMethodID for java.lang.String.valueOf(int) fails as the
   // method is static
@@ -121,13 +119,11 @@
   env_->ExceptionClear();
 
   // Check that java.lang.String.valueOf(int) does exist
-#if defined(__arm__)
   jclass jlstring = env_->FindClass("java/lang/String");
   method = env_->GetStaticMethodID(jlstring, "valueOf",
                                    "(I)Ljava/lang/String;");
   EXPECT_NE(static_cast<jmethodID>(NULL), method);
   EXPECT_FALSE(env_->ExceptionCheck());
-#endif
 }
 
 TEST_F(JniInternalTest, RegisterNatives) {
diff --git a/src/jni_internal_x86.cc b/src/jni_internal_x86.cc
index 82fbb57..05542b7 100644
--- a/src/jni_internal_x86.cc
+++ b/src/jni_internal_x86.cc
@@ -7,8 +7,76 @@
 
 namespace art {
 
+
+#define __ assembler->
+
+// Creates a function which invokes a managed method with an array of
+// arguments.
+//
+// Immediately after the call, the environment looks like this:
+//
+// [SP+0 ] = Return address
+// [SP+4 ]= method pointer
+// [SP+8 ] = receiver pointer or NULL for static methods
+// [SP+12] = (managed) thread pointer
+// [SP+16] = argument array or NULL for no argument methods
+// [SP+20] = JValue* result or NULL for void returns
+//
+// As the JNI call has already transitioned the thread into the
+// "running" state the remaining responsibilities of this routine are
+// to save the native registers and set up the managed registers. On
+// return, the return value must be store into the result JValue.
 void CreateInvokeStub(Assembler* assembler, Method* method) {
-  UNIMPLEMENTED(FATAL);
+  // Size of frame - spill of EDI + Method* + possible receiver + arg array
+  size_t frame_size = (2 * kPointerSize) +
+                      (method->IsStatic() ? 0 : kPointerSize) +
+                      method->NumArgArrayBytes();
+  size_t pad_size = RoundUp(frame_size, kStackAlignment) - frame_size;
+
+  __ pushl(EDI);                   // preserve EDI
+  __ movl(EDI, Address(ESP, 8));   // EDI = method
+  __ movl(EAX, Address(ESP, 12));  // EAX = receiver
+  __ movl(EDX, Address(ESP, 20));  // EDX = arg array
+
+  // Push padding
+  if (pad_size != 0) {
+    __ addl(ESP, Immediate(-pad_size));
+  }
+
+  // Push/copy arguments
+  for (size_t off = method->NumArgArrayBytes(); off > 0; off -= kPointerSize) {
+    __ pushl(Address(EDX, off - kPointerSize));
+  }
+  if (!method->IsStatic()) {
+    __ pushl(EAX);
+  }
+  __ pushl(EDI);  // Method* at bottom of fake managed frame
+  __ call(Address(EDI, method->GetCodeOffset()));  // Call code off of method
+
+  // pop arguments and padding up to saved EDI
+  __ addl(ESP, Immediate(frame_size + pad_size - kPointerSize));
+  char ch = method->GetShorty()[0];
+  if (ch != 'V') {
+    // Load the result JValue pointer.
+    __ movl(EDI, Address(ESP, 24));
+    switch(ch) {
+      case 'D':
+        __ fstpl(Address(EDI, 0));
+        break;
+      case 'F':
+        __ fstps(Address(EDI, 0));
+        break;
+      case 'J':
+        __ movl(Address(EDI, 0), EAX);
+        __ movl(Address(EDI, 4), EDX);
+        break;
+      default:
+        __ movl(Address(EDI, 0), EAX);
+        break;
+    }
+  }
+  __ popl(EDI); // restore EDI
+  __ ret();
 }
 
 }  // namespace art
diff --git a/src/object.h b/src/object.h
index 2384fde..c78103b 100644
--- a/src/object.h
+++ b/src/object.h
@@ -375,8 +375,8 @@
     return declaring_class_;
   }
 
-  static MemberOffset ClassOffset() {
-    return MemberOffset(OFFSETOF_MEMBER(Method, klass_));
+  static MemberOffset DeclaringClassOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(Method, declaring_class_));
   }
 
   // Returns true if the method is declared public.