Merge "Implement upcalls with compiled JNI callback bridges." into dalvik-dev
diff --git a/build/Android.common.mk b/build/Android.common.mk
index a3bb5fc..c07e6bd 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -62,6 +62,7 @@
 	$(LIBART_COMMON_SRC_FILES) \
 	src/assembler_arm.cc \
 	src/calling_convention_arm.cc \
+	src/jni_internal_arm.cc \
 	src/logging_android.cc \
 	src/managed_register_arm.cc \
 	src/runtime_android.cc \
@@ -71,6 +72,7 @@
 	$(LIBART_COMMON_SRC_FILES) \
 	src/assembler_x86.cc \
 	src/calling_convention_x86.cc \
+	src/jni_internal_x86.cc \
 	src/logging_linux.cc \
 	src/managed_register_x86.cc \
 	src/runtime_linux.cc \
@@ -86,8 +88,8 @@
 	src/dex_instruction_visitor_test.cc \
 	src/exception_test.cc \
 	src/intern_table_test.cc \
+	src/jni_internal_test.cc.arm \
 	src/jni_compiler_test.cc.arm \
-	src/jni_internal_test.cc \
 	src/object_test.cc \
 	src/runtime_test.cc \
 	src/space_test.cc \
diff --git a/src/assembler_arm.cc b/src/assembler_arm.cc
index b75263a..9b8cc3a 100644
--- a/src/assembler_arm.cc
+++ b/src/assembler_arm.cc
@@ -1361,6 +1361,58 @@
   vstrd(reg, Address(base, offset), cond);
 }
 
+void Assembler::Push(Register rd, Condition cond) {
+  str(rd, Address(SP, -kRegisterSize, Address::PreIndex), cond);
+}
+
+void Assembler::Pop(Register rd, Condition cond) {
+  ldr(rd, Address(SP, kRegisterSize, Address::PostIndex), cond);
+}
+
+void Assembler::PushList(RegList regs, Condition cond) {
+  stm(DB_W, SP, regs, cond);
+}
+
+void Assembler::PopList(RegList regs, Condition cond) {
+  ldm(IA_W, SP, regs, cond);
+}
+
+void Assembler::Mov(Register rd, Register rm, Condition cond) {
+  if (rd != rm) {
+    mov(rd, ShifterOperand(rm), cond);
+  }
+}
+
+void Assembler::Lsl(Register rd, Register rm, uint32_t shift_imm,
+                    Condition cond) {
+  CHECK_NE(shift_imm, 0u);  // Do not use Lsl if no shift is wanted.
+  mov(rd, ShifterOperand(rm, LSL, shift_imm), cond);
+}
+
+void Assembler::Lsr(Register rd, Register rm, uint32_t shift_imm,
+                    Condition cond) {
+  CHECK_NE(shift_imm, 0u);  // Do not use Lsr if no shift is wanted.
+  if (shift_imm == 32) shift_imm = 0;  // Comply to UAL syntax.
+  mov(rd, ShifterOperand(rm, LSR, shift_imm), cond);
+}
+
+void Assembler::Asr(Register rd, Register rm, uint32_t shift_imm,
+                    Condition cond) {
+  CHECK_NE(shift_imm, 0u);  // Do not use Asr if no shift is wanted.
+  if (shift_imm == 32) shift_imm = 0;  // Comply to UAL syntax.
+  mov(rd, ShifterOperand(rm, ASR, shift_imm), cond);
+}
+
+void Assembler::Ror(Register rd, Register rm, uint32_t shift_imm,
+                    Condition cond) {
+  CHECK_NE(shift_imm, 0u);  // Use Rrx instruction.
+  mov(rd, ShifterOperand(rm, ROR, shift_imm), cond);
+}
+
+void Assembler::Rrx(Register rd, Register rm, Condition cond) {
+  mov(rd, ShifterOperand(rm, ROR, 0), cond);
+}
+
 // Emit code that will create an activation on the stack
 void Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg) {
   CHECK(IsAligned(frame_size, 16));
diff --git a/src/common_test.h b/src/common_test.h
index 0e0f388..b9e7bcc 100644
--- a/src/common_test.h
+++ b/src/common_test.h
@@ -305,6 +305,86 @@
   "ABwBAAAFAAAAAwAAAGwBAAAGAAAAAQAAAIQBAAABIAAAAgAAAKQBAAACIAAAHAAAADwCAAADIAAA"
   "AgAAAOICAAAAIAAAAQAAAPUCAAAAEAAAAQAAABwDAAA=";
 
+// class Main {
+//   public static void main(String args[]) {
+//   }
+// }
+static const char kMainDex[] =
+  "ZGV4CjAzNQAPNypTL1TulODHFdpEa2pP98I7InUu7uQgAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAI"
+  "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAAAwAQAA8AAAACIB"
+  "AAAqAQAAMgEAAEYBAABRAQAAVAEAAFgBAABtAQAAAQAAAAIAAAAEAAAABgAAAAQAAAACAAAAAAAA"
+  "AAUAAAACAAAAHAEAAAAAAAAAAAAAAAABAAcAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAADAAAA"
+  "AAAAAH4BAAAAAAAAAQABAAEAAABzAQAABAAAAHAQAgAAAA4AAQABAAAAAAB4AQAAAQAAAA4AAAAB"
+  "AAAAAwAGPGluaXQ+AAZMTWFpbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwAJTWFpbi5qYXZhAAFWAAJW"
+  "TAATW0xqYXZhL2xhbmcvU3RyaW5nOwAEbWFpbgABAAcOAAMBAAcOAAAAAgAAgIAE8AEBCYgCDAAA"
+  "AAAAAAABAAAAAAAAAAEAAAAIAAAAcAAAAAIAAAAEAAAAkAAAAAMAAAACAAAAoAAAAAUAAAADAAAA"
+  "uAAAAAYAAAABAAAA0AAAAAEgAAACAAAA8AAAAAEQAAABAAAAHAEAAAIgAAAIAAAAIgEAAAMgAAAC"
+  "AAAAcwEAAAAgAAABAAAAfgEAAAAQAAABAAAAjAEAAA==";
+
+// class StaticLeafMethods {
+//   static void nop() {
+//   }
+//   static byte identity(byte x) {
+//     return x;
+//   }
+//   static int identity(int x) {
+//     return x;
+//   }
+//   static int sum(int a, int b) {
+//     return a + b;
+//   }
+//   static int sum(int a, int b, int c) {
+//     return a + b + c;
+//   }
+//   static int sum(int a, int b, int c, int d) {
+//     return a + b + c + d;
+//   }
+//   static int sum(int a, int b, int c, int d, int e) {
+//     return a + b + c + d + e;
+//   }
+//   static double identity(double x) {
+//     return x;
+//   }
+//   static double sum(double a, double b) {
+//     return a + b;
+//   }
+//   static double sum(double a, double b, double c) {
+//     return a + b + c;
+//   }
+//   static double sum(double a, double b, double c, double d) {
+//     return a + b + c + d;
+//   }
+//   static double sum(double a, double b, double c, double d, double e) {
+//     return a + b + c + d + e;
+//   }
+// }
+static const char kStaticLeafMethodsDex[] =
+  "ZGV4CjAzNQD8gEpaFD0w5dM8dsPaCQ3wIh0xaUjfni+IBQAAcAAAAHhWNBIAAAAAAAAAAPQEAAAW"
+  "AAAAcAAAAAYAAADIAAAADAAAAOAAAAAAAAAAAAAAAA4AAABwAQAAAQAAAOABAACIAwAAAAIAAK4D"
+  "AAC2AwAAuQMAAL0DAADAAwAAxAMAAMkDAADPAwAA1gMAAN4DAADhAwAA5QMAAOoDAADwAwAA9wMA"
+  "AP8DAAAUBAAAKAQAAEAEAABDBAAATQQAAFIEAAABAAAAAwAAAAkAAAAPAAAAEAAAABIAAAACAAAA"
+  "AAAAADgDAAAEAAAAAQAAAEADAAAFAAAAAQAAAEgDAAAGAAAAAQAAAFADAAAHAAAAAQAAAFwDAAAI"
+  "AAAAAQAAAGgDAAAKAAAAAgAAAHgDAAALAAAAAgAAAIADAAAMAAAAAgAAAIgDAAANAAAAAgAAAJQD"
+  "AAAOAAAAAgAAAKADAAASAAAABQAAAAAAAAADAAsAAAAAAAMAAAATAAAAAwABABMAAAADAAYAEwAA"
+  "AAMACwAUAAAAAwACABUAAAADAAMAFQAAAAMABAAVAAAAAwAFABUAAAADAAcAFQAAAAMACAAVAAAA"
+  "AwAJABUAAAADAAoAFQAAAAQACwAAAAAAAwAAAAAAAAAEAAAAAAAAABEAAAAAAAAAtwQAAAAAAAAB"
+  "AAEAAQAAAFcEAAAEAAAAcBANAAAADgABAAEAAAAAAFwEAAABAAAADwAAAAIAAgAAAAAAYgQAAAEA"
+  "AAAQAAAAAQABAAAAAABoBAAAAQAAAA8AAAAAAAAAAAAAAG4EAAABAAAADgAAAAYABAAAAAAAcwQA"
+  "AAMAAACrAAIEEAAAAAgABgAAAAAAegQAAAQAAACrAAIEy2AQAAoACAAAAAAAggQAAAUAAACrAAIE"
+  "y2DLgBAAAAAMAAoAAAAAAIsEAAAGAAAAqwACBMtgy4DLoBAAAwACAAAAAACVBAAAAwAAAJAAAQIP"
+  "AAAABAADAAAAAACcBAAABAAAAJAAAQKwMA8ABQAEAAAAAACkBAAABQAAAJAAAQKwMLBADwAAAAYA"
+  "BQAAAAAArQQAAAYAAACQAAECsDCwQLBQDwABAAAAAAAAAAEAAAABAAAAAgAAAAEAAQADAAAAAQAB"
+  "AAEAAAAEAAAAAQABAAEAAQAFAAAAAQABAAEAAQABAAAAAQAAAAIAAAACAAAAAgACAAMAAAACAAIA"
+  "AgAAAAQAAAACAAIAAgACAAUAAAACAAIAAgACAAIABjxpbml0PgABQgACQkIAAUQAAkREAANEREQA"
+  "BEREREQABUREREREAAZEREREREQAAUkAAklJAANJSUkABElJSUkABUlJSUlJAAZJSUlJSUkAE0xT"
+  "dGF0aWNMZWFmTWV0aG9kczsAEkxqYXZhL2xhbmcvT2JqZWN0OwAWU3RhdGljTGVhZk1ldGhvZHMu"
+  "amF2YQABVgAIaWRlbnRpdHkAA25vcAADc3VtAAEABw4ABQEABw4AFwEABw4ACAEABw4AAwAHDgAa"
+  "AgAABw4AHQMAAAAHDgAgBAAAAAAHDgAjBQAAAAAABw4ACwIAAAcOAA4DAAAABw4AEQQAAAAABw4A"
+  "FAUAAAAAAAcOAAAADQAAgIAEgAQBCJgEAQisBAEIwAQBCNQEAQjoBAEIgAUBCJgFAQi0BQEI0AUB"
+  "COgFAQiABgEInAYAAAAMAAAAAAAAAAEAAAAAAAAAAQAAABYAAABwAAAAAgAAAAYAAADIAAAAAwAA"
+  "AAwAAADgAAAABQAAAA4AAABwAQAABgAAAAEAAADgAQAAASAAAA0AAAAAAgAAARAAAAsAAAA4AwAA"
+  "AiAAABYAAACuAwAAAyAAAA0AAABXBAAAACAAAAEAAAC3BAAAABAAAAEAAAD0BAAA";
+
 static inline DexFile* OpenDexFileBase64(const char* base64) {
   CHECK(base64 != NULL);
   size_t length;
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 6e6934f..6e61001 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -2,12 +2,15 @@
 
 #include "jni_internal.h"
 
+#include <cstdarg>
 #include <vector>
 #include <utility>
+#include <sys/mman.h>
 
 #include "class_linker.h"
 #include "jni.h"
 #include "logging.h"
+#include "object.h"
 #include "runtime.h"
 #include "scoped_ptr.h"
 #include "stringpiece.h"
@@ -53,6 +56,132 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedJniThreadState);
 };
 
+void CreateInvokeStub(Assembler* assembler, Method* method);
+
+bool EnsureInvokeStub(Method* method) {
+  if (method->GetInvokeStub() != NULL) {
+    return true;
+  }
+  // TODO: use signature to find a matching stub
+  // TODO: failed, acquire a lock on the stub table
+  Assembler assembler;
+  CreateInvokeStub(&assembler, method);
+  // TODO: store native_entry in the stub table
+  int prot = PROT_READ | PROT_WRITE | PROT_EXEC;
+  size_t length = assembler.CodeSize();
+  void* addr = mmap(NULL, length, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  if (addr == MAP_FAILED) {
+    PLOG(FATAL) << "mmap failed";
+  }
+  MemoryRegion region(addr, length);
+  assembler.FinalizeInstructions(region);
+  method->SetInvokeStub(reinterpret_cast<Method::InvokeStub*>(region.pointer()));
+  return true;
+}
+
+static byte* CreateArgArray(Method* method, va_list ap) {
+  size_t num_bytes = method->NumArgArrayBytes();
+  scoped_array<byte> arg_array(new byte[num_bytes]);
+  const StringPiece& shorty = method->GetShorty();
+  for (int i = 1, offset = 0; i < shorty.size() - 1; ++i) {
+    switch (shorty[i]) {
+      case 'Z':
+      case 'B':
+      case 'C':
+      case 'S':
+      case 'I':
+        *reinterpret_cast<int32_t*>(&arg_array[offset]) = va_arg(ap, jint);
+        offset += 4;
+        break;
+      case 'F':
+        *reinterpret_cast<float*>(&arg_array[offset]) = va_arg(ap, jdouble);
+        offset += 4;
+        break;
+      case 'L': {
+        // TODO: local reference
+        Object* obj = reinterpret_cast<Object*>(va_arg(ap, jobject));
+        *reinterpret_cast<Object**>(&arg_array[offset]) = obj;
+        offset += sizeof(Object*);
+        break;
+      }
+      case 'D':
+        *reinterpret_cast<double*>(&arg_array[offset]) = va_arg(ap, jdouble);
+        offset += 8;
+        break;
+      case 'J':
+        *reinterpret_cast<int64_t*>(&arg_array[offset]) = va_arg(ap, jlong);
+        offset += 8;
+        break;
+    }
+  }
+  return arg_array.release();
+}
+
+static byte* CreateArgArray(Method* method, jvalue* args) {
+  size_t num_bytes = method->NumArgArrayBytes();
+  scoped_array<byte> arg_array(new byte[num_bytes]);
+  const StringPiece& shorty = method->GetShorty();
+  for (int i = 1, offset = 0; i < shorty.size() - 1; ++i) {
+    switch (shorty[i]) {
+      case 'Z':
+      case 'B':
+      case 'C':
+      case 'S':
+      case 'I':
+        *reinterpret_cast<uint32_t*>(&arg_array[offset]) = args[i - 1].i;
+        offset += 4;
+        break;
+      case 'F':
+        *reinterpret_cast<float*>(&arg_array[offset]) = args[i - 1].f;
+        offset += 4;
+        break;
+      case 'L': {
+        Object* obj = reinterpret_cast<Object*>(args[i - 1].l);  // TODO: local reference
+        *reinterpret_cast<Object**>(&arg_array[offset]) = obj;
+        offset += sizeof(Object*);
+        break;
+      }
+      case 'D':
+        *reinterpret_cast<double*>(&arg_array[offset]) = args[i - 1].d;
+        offset += 8;
+        break;
+      case 'J':
+        *reinterpret_cast<uint64_t*>(&arg_array[offset]) = args[i - 1].j;
+        offset += 8;
+        break;
+    }
+  }
+  return arg_array.release();
+}
+
+JValue InvokeWithArgArray(JNIEnv* env, Object* obj, jmethodID method_id,
+                          byte* args) {
+  Method* method = reinterpret_cast<Method*>(method_id);  // TODO
+  // Call the invoke stub associated with the method
+  // Pass everything as arguments
+  const Method::InvokeStub* stub = method->GetInvokeStub();
+  CHECK(stub != NULL);
+  // TODO: get thread from env
+  Thread* thread = NULL;
+  JValue result;
+  (*stub)(method, obj, thread, args, &result);
+  return result;
+}
+
+JValue InvokeWithJValues(JNIEnv* env, Object* obj, jmethodID method_id,
+                         jvalue* args) {
+  Method* method = reinterpret_cast<Method*>(method_id);
+  scoped_array<byte> arg_array(CreateArgArray(method, args));
+  return InvokeWithArgArray(env, obj, method_id, arg_array.get());
+}
+
+JValue InvokeWithVarArgs(JNIEnv* env, Object* obj, jmethodID method_id,
+                         va_list args) {
+  Method* method = reinterpret_cast<Method*>(method_id);
+  scoped_array<byte> arg_array(CreateArgArray(method, args));
+  return InvokeWithArgArray(env, obj, method_id, arg_array.get());
+}
+
 jint GetVersion(JNIEnv* env) {
   ScopedJniThreadState ts(env);
   return JNI_VERSION_1_6;
@@ -773,210 +902,219 @@
 jmethodID GetStaticMethodID(JNIEnv* env,
     jclass clazz, const char* name, const char* sig) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return NULL;
+  // TODO: retrieve handle value for class
+  Class* klass = reinterpret_cast<Class*>(clazz);
+  // TODO: check that klass is initialized
+  Method* method = klass->FindDirectMethod(name, sig);
+  if (method == NULL || !method->IsStatic()) {
+    // TODO: throw NoSuchMethodError
+    return NULL;
+  }
+  // TODO: create a JNI weak global reference for method
+  bool success = EnsureInvokeStub(method);
+  if (!success) {
+    // TODO: throw OutOfMemoryException
+    return NULL;
+  }
+  return reinterpret_cast<jmethodID>(method);
 }
 
 jobject CallStaticObjectMethod(JNIEnv* env,
     jclass clazz, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return NULL;
+  va_list ap;
+  va_start(ap, methodID);
+  JValue result = InvokeWithVarArgs(env, NULL, methodID, ap);
+  jobject obj = reinterpret_cast<jobject>(result.l);  // TODO: local reference
+  return obj;
 }
 
 jobject CallStaticObjectMethodV(JNIEnv* env,
     jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return NULL;
+  JValue result = InvokeWithVarArgs(env, NULL, methodID, args);
+  jobject obj = reinterpret_cast<jobject>(result.l);  // TODO: local reference
+  return obj;
 }
 
 jobject CallStaticObjectMethodA(JNIEnv* env,
     jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return NULL;
+  JValue result = InvokeWithJValues(env, NULL, methodID, args);
+  jobject obj = reinterpret_cast<jobject>(result.l);  // TODO: local reference
+  return obj;
 }
 
 jboolean CallStaticBooleanMethod(JNIEnv* env,
     jclass clazz, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return JNI_FALSE;
+  va_list ap;
+  va_start(ap, methodID);
+  return InvokeWithVarArgs(env, NULL, methodID, ap).z;
 }
 
 jboolean CallStaticBooleanMethodV(JNIEnv* env,
     jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return JNI_FALSE;
+  return InvokeWithVarArgs(env, NULL, methodID, args).z;
 }
 
 jboolean CallStaticBooleanMethodA(JNIEnv* env,
     jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return JNI_FALSE;
+  return InvokeWithJValues(env, NULL, methodID, args).z;
 }
 
 jbyte CallStaticByteMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  va_list ap;
+  va_start(ap, methodID);
+  return InvokeWithVarArgs(env, NULL, methodID, ap).b;
 }
 
 jbyte CallStaticByteMethodV(JNIEnv* env,
     jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithVarArgs(env, NULL, methodID, args).b;
 }
 
 jbyte CallStaticByteMethodA(JNIEnv* env,
     jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithJValues(env, NULL, methodID, args).b;
 }
 
 jchar CallStaticCharMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  va_list ap;
+  va_start(ap, methodID);
+  return InvokeWithVarArgs(env, NULL, methodID, ap).c;
 }
 
 jchar CallStaticCharMethodV(JNIEnv* env,
     jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithVarArgs(env, NULL, methodID, args).c;
 }
 
 jchar CallStaticCharMethodA(JNIEnv* env,
     jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithJValues(env, NULL, methodID, args).c;
 }
 
-jshort CallStaticShortMethod(JNIEnv* env,
-    jclass clazz, jmethodID methodID, ...) {
+jshort CallStaticShortMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  va_list ap;
+  va_start(ap, methodID);
+  return InvokeWithVarArgs(env, NULL, methodID, ap).s;
 }
 
 jshort CallStaticShortMethodV(JNIEnv* env,
     jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithVarArgs(env, NULL, methodID, args).s;
 }
 
 jshort CallStaticShortMethodA(JNIEnv* env,
     jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithJValues(env, NULL, methodID, args).s;
 }
 
 jint CallStaticIntMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  va_list ap;
+  va_start(ap, methodID);
+  return InvokeWithVarArgs(env, NULL, methodID, ap).i;
 }
 
 jint CallStaticIntMethodV(JNIEnv* env,
     jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithVarArgs(env, NULL, methodID, args).i;
 }
 
 jint CallStaticIntMethodA(JNIEnv* env,
     jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithJValues(env, NULL, methodID, args).i;
 }
 
 jlong CallStaticLongMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  va_list ap;
+  va_start(ap, methodID);
+  return InvokeWithVarArgs(env, NULL, methodID, ap).j;
 }
 
 jlong CallStaticLongMethodV(JNIEnv* env,
     jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithVarArgs(env, NULL, methodID, args).j;
 }
 
 jlong CallStaticLongMethodA(JNIEnv* env,
     jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithJValues(env, NULL, methodID, args).j;
 }
 
-jfloat CallStaticFloatMethod(JNIEnv* env,
-    jclass clazz, jmethodID methodID, ...) {
+jfloat CallStaticFloatMethod(JNIEnv* env, jclass cls, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  va_list ap;
+  va_start(ap, methodID);
+  return InvokeWithVarArgs(env, NULL, methodID, ap).f;
 }
 
 jfloat CallStaticFloatMethodV(JNIEnv* env,
     jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithVarArgs(env, NULL, methodID, args).f;
 }
 
 jfloat CallStaticFloatMethodA(JNIEnv* env,
     jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithJValues(env, NULL, methodID, args).f;
 }
 
-jdouble CallStaticDoubleMethod(JNIEnv* env,
-    jclass clazz, jmethodID methodID, ...) {
+jdouble CallStaticDoubleMethod(JNIEnv* env, jclass cls, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  va_list ap;
+  va_start(ap, methodID);
+  return InvokeWithVarArgs(env, NULL, methodID, ap).d;
 }
 
 jdouble CallStaticDoubleMethodV(JNIEnv* env,
     jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithVarArgs(env, NULL, methodID, args).d;
 }
 
 jdouble CallStaticDoubleMethodA(JNIEnv* env,
     jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return InvokeWithJValues(env, NULL, methodID, args).d;
 }
 
 void CallStaticVoidMethod(JNIEnv* env, jclass cls, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
+  va_list ap;
+  va_start(ap, methodID);
+  InvokeWithVarArgs(env, NULL, methodID, ap);
 }
 
 void CallStaticVoidMethodV(JNIEnv* env,
     jclass cls, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
+  InvokeWithVarArgs(env, NULL, methodID, args);
 }
 
 void CallStaticVoidMethodA(JNIEnv* env,
-    jclass cls, jmethodID methodID, jvalue*  args) {
+    jclass cls, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(FATAL);
+  InvokeWithJValues(env, NULL, methodID, args);
 }
 
 jfieldID GetStaticFieldID(JNIEnv* env,
diff --git a/src/jni_internal_arm.cc b/src/jni_internal_arm.cc
new file mode 100644
index 0000000..b1ba248
--- /dev/null
+++ b/src/jni_internal_arm.cc
@@ -0,0 +1,115 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+// Author: cshapiro@google.com (Carl Shapiro)
+
+#include "jni_internal.h"
+
+#include <algorithm>
+
+#include "assembler.h"
+#include "object.h"
+
+#define __ assembler->
+
+namespace art {
+
+// Creates a function which invokes a managed method with an array of
+// arguments.
+//
+// At the time of call, the environment looks something like this:
+//
+// 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
+//
+// As the JNI call has already transitioned the thread into the
+// "running" state the remaining responsibilities of this routine are
+// to save the native register value and restore the managed thread
+// register and transfer arguments from the array into register and on
+// 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;
+
+  // 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;
+  }
+
+  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 all the register arguments into place.
+  if (method->IsStatic()) {
+    if (num_register_words > 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);
+    }
+  } else {
+    if (num_register_words > 0) {
+      __ LoadFromOffset(kLoadWord, R2, R3, 0);
+    }
+    if (num_register_words > 1) {
+      __ 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);
+    if (ch == 'D' || ch == 'J') {
+      __ StoreToOffset(kStoreWordPair, R0, IP, 0);
+    } else {
+      __ StoreToOffset(kStoreWord, R0, IP, 0);
+    }
+  }
+
+  __ AddConstant(SP, stack_parameters_size);
+  __ PopList(save | (1 << PC));
+}
+
+}  // namespace art
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index a7f6917..457bae8 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -1,8 +1,11 @@
 // Copyright 2011 Google Inc. All Rights Reserved.
 
-#include "common_test.h"
+#include "jni_internal.h"
 
-#include <stdio.h>
+#include <cmath>
+#include <sys/mman.h>
+
+#include "common_test.h"
 #include "gtest/gtest.h"
 
 namespace art {
@@ -54,4 +57,754 @@
   EXPECT_CLASS_NOT_FOUND("K");
 }
 
-}  // namespace art
+bool EnsureInvokeStub(Method* method);
+
+byte* AllocateCode(void* code, size_t length) {
+  int prot = PROT_READ | PROT_WRITE | PROT_EXEC;
+  void* addr = mmap(NULL, length, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  CHECK(addr != MAP_FAILED);
+  memcpy(addr, code, length);
+  __builtin___clear_cache(addr, (byte*)addr + length);
+  // Set the low-order bit so a BLX will switch to Thumb mode.
+  return reinterpret_cast<byte*>(reinterpret_cast<uintptr_t>(addr) | 1);
+}
+
+Method::InvokeStub* AllocateStub(Method* method,
+                                 byte* code,
+                                 size_t length) {
+  CHECK(method->GetInvokeStub() == NULL);
+  EnsureInvokeStub(method);
+  Method::InvokeStub* stub = method->GetInvokeStub();
+  CHECK(stub != NULL);
+  method->SetCode(AllocateCode(code, length));
+  CHECK(method->GetCode() != NULL);
+  return stub;
+}
+
+void FreeStub(Method* method, size_t length) {
+  void* addr = const_cast<void*>(method->GetCode());
+  munmap(addr, length);
+  method->SetCode(NULL);
+}
+
+#if defined(__arm__)
+TEST_F(JniInternalTest, StaticMainMethod) {
+  scoped_ptr<DexFile> dex(OpenDexFileBase64(kMainDex));
+
+  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
+  ASSERT_TRUE(class_loader != NULL);
+
+  Class* klass = class_linker_->FindClass("LMain;", class_loader);
+  ASSERT_TRUE(klass != NULL);
+
+  Method* method = klass->FindDirectMethod("main", "([Ljava/lang/String;)V");
+  ASSERT_TRUE(method != NULL);
+
+  byte main_LV_code[] = {
+    0x2d, 0xe9, 0x00, 0x40, 0x83, 0xb0, 0xcd, 0xf8, 0x00, 0x00,
+    0xcd, 0xf8, 0x14, 0x10, 0x03, 0xb0, 0xbd, 0xe8, 0x00, 0x80,
+  };
+
+  Method::InvokeStub* stub = AllocateStub(method,
+                                          main_LV_code,
+                                          sizeof(main_LV_code));
+
+  Object* arg = NULL;
+
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(&arg), NULL);
+
+  FreeStub(method, sizeof(main_LV_code));
+}
+
+TEST_F(JniInternalTest, StaticNopMethod) {
+  scoped_ptr<DexFile> dex(OpenDexFileBase64(kStaticLeafMethodsDex));
+
+  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
+  ASSERT_TRUE(class_loader != NULL);
+
+  Class* klass = class_linker_->FindClass("LStaticLeafMethods;", class_loader);
+  ASSERT_TRUE(klass != NULL);
+
+  Method* method = klass->FindDirectMethod("nop", "()V");
+  ASSERT_TRUE(method != NULL);
+
+  byte nop_V_code[] = {
+    0x2d, 0xe9, 0x00, 0x40, 0x83, 0xb0, 0xcd, 0xf8,
+    0x00, 0x00, 0x03, 0xb0, 0xbd, 0xe8, 0x00, 0x80,
+  };
+
+  Method::InvokeStub* stub = AllocateStub(method,
+                                          nop_V_code,
+                                          sizeof(nop_V_code));
+  ASSERT_TRUE(stub);
+
+  (*stub)(method, NULL, NULL, NULL, NULL);
+
+  FreeStub(method, sizeof(nop_V_code));
+}
+
+TEST_F(JniInternalTest, StaticIdentityByteMethod) {
+  scoped_ptr<DexFile> dex(OpenDexFileBase64(kStaticLeafMethodsDex));
+
+  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
+  ASSERT_TRUE(class_loader != NULL);
+
+  Class* klass = class_linker_->FindClass("LStaticLeafMethods;", class_loader);
+  ASSERT_TRUE(klass != NULL);
+
+  Method* method = klass->FindDirectMethod("identity", "(B)B");
+  ASSERT_TRUE(method != NULL);
+
+  byte identity_BB_code[] = {
+    0x2d, 0xe9, 0x00, 0x40, 0x83, 0xb0, 0xcd, 0xf8,
+    0x00, 0x00, 0xcd, 0xf8, 0x14, 0x10, 0x05, 0x98,
+    0x03, 0xb0, 0xbd, 0xe8, 0x00, 0x80, 0x00, 0x00,
+  };
+
+  Method::InvokeStub* stub = AllocateStub(method,
+                                          identity_BB_code,
+                                          sizeof(identity_BB_code));
+
+  int arg;
+  JValue result;
+
+  arg = 0;
+  result.b = -1;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(&arg), &result);
+  EXPECT_EQ(0, result.b);
+
+  arg = -1;
+  result.b = 0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(&arg), &result);
+  EXPECT_EQ(-1, result.b);
+
+  arg = SCHAR_MAX;
+  result.b = 0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(&arg), &result);
+  EXPECT_EQ(SCHAR_MAX, result.b);
+
+  arg = SCHAR_MIN;
+  result.b = 0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(&arg), &result);
+  EXPECT_EQ(SCHAR_MIN, result.b);
+
+  FreeStub(method, sizeof(identity_BB_code));
+}
+
+TEST_F(JniInternalTest, StaticIdentityIntMethod) {
+  scoped_ptr<DexFile> dex(OpenDexFileBase64(kStaticLeafMethodsDex));
+
+  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
+  ASSERT_TRUE(class_loader != NULL);
+
+  Class* klass = class_linker_->FindClass("LStaticLeafMethods;", class_loader);
+  ASSERT_TRUE(klass != NULL);
+
+  Method* method = klass->FindDirectMethod("identity", "(I)I");
+  ASSERT_TRUE(method != NULL);
+
+  byte identity_II_code[] = {
+    0x2d, 0xe9, 0x00, 0x40, 0x83, 0xb0, 0xcd, 0xf8,
+    0x00, 0x00, 0xcd, 0xf8, 0x14, 0x10, 0x05, 0x98,
+    0x03, 0xb0, 0xbd, 0xe8, 0x00, 0x80, 0x00, 0x00,
+  };
+
+  Method::InvokeStub* stub = AllocateStub(method,
+                                          identity_II_code,
+                                          sizeof(identity_II_code));
+
+  int arg;
+  JValue result;
+
+  arg = 0;
+  result.i = -1;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(&arg), &result);
+  EXPECT_EQ(0, result.i);
+
+  arg = -1;
+  result.i = 0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(&arg), &result);
+  EXPECT_EQ(-1, result.i);
+
+  arg = INT_MAX;
+  result.i = 0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(&arg), &result);
+  EXPECT_EQ(INT_MAX, result.i);
+
+  arg = INT_MIN;
+  result.i = 0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(&arg), &result);
+  EXPECT_EQ(INT_MIN, result.i);
+
+  FreeStub(method, sizeof(identity_II_code));
+}
+
+TEST_F(JniInternalTest, StaticIdentityDoubleMethod) {
+  scoped_ptr<DexFile> dex(OpenDexFileBase64(kStaticLeafMethodsDex));
+
+  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
+  ASSERT_TRUE(class_loader != NULL);
+
+  Class* klass = class_linker_->FindClass("LStaticLeafMethods;", class_loader);
+  ASSERT_TRUE(klass != NULL);
+
+  Method* method = klass->FindDirectMethod("identity", "(D)D");
+  ASSERT_TRUE(method != NULL);
+
+  byte identity_DD_code[] = {
+    0x2d, 0xe9, 0x00, 0x40, 0x83, 0xb0, 0xcd, 0xf8,
+    0x00, 0x00, 0xcd, 0xf8, 0x14, 0x10, 0xcd, 0xf8,
+    0x18, 0x20, 0x05, 0x98, 0x06, 0x99, 0x03, 0xb0,
+    0xbd, 0xe8, 0x00, 0x80,
+  };
+
+  Method::InvokeStub* stub = AllocateStub(method,
+                                          identity_DD_code,
+                                          sizeof(identity_DD_code));
+
+  double arg;
+  JValue result;
+
+  arg = 0.0;
+  result.d = -1.0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(&arg), &result);
+  EXPECT_EQ(0.0, result.d);
+
+  arg = -1.0;
+  result.d = 0.0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(&arg), &result);
+  EXPECT_EQ(-1.0, result.d);
+
+  arg = DBL_MAX;
+  result.d = 0.0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(&arg), &result);
+  EXPECT_EQ(DBL_MAX, result.d);
+
+  arg = DBL_MIN;
+  result.d = 0.0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(&arg), &result);
+  EXPECT_EQ(DBL_MIN, result.d);
+
+  FreeStub(method, sizeof(identity_DD_code));
+}
+
+TEST_F(JniInternalTest, StaticSumIntIntMethod) {
+  scoped_ptr<DexFile> dex(OpenDexFileBase64(kStaticLeafMethodsDex));
+
+  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
+  ASSERT_TRUE(class_loader != NULL);
+
+  Class* klass = class_linker_->FindClass("LStaticLeafMethods;", class_loader);
+  ASSERT_TRUE(klass != NULL);
+
+  Method* method = klass->FindDirectMethod("sum", "(II)I");
+  ASSERT_TRUE(method != NULL);
+
+  byte sum_III_code[] = {
+    0x2d, 0xe9, 0x00, 0x40, 0x83, 0xb0, 0xcd, 0xf8,
+    0x00, 0x00, 0xcd, 0xf8, 0x14, 0x10, 0xcd, 0xf8,
+    0x18, 0x20, 0x05, 0x98, 0x06, 0x99, 0x42, 0x18,
+    0xcd, 0xf8, 0x04, 0x20, 0x01, 0x98, 0x03, 0xb0,
+    0xbd, 0xe8, 0x00, 0x80,
+  };
+
+  Method::InvokeStub* stub = AllocateStub(method,
+                                          sum_III_code,
+                                          sizeof(sum_III_code));
+
+  int args[2];
+  JValue result;
+
+  args[0] = 0;
+  args[1] = 0;
+  result.i = -1;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(0, result.i);
+
+  args[0] = 1;
+  args[1] = 2;
+  result.i = 0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(3, result.i);
+
+  args[0] = -2;
+  args[1] = 5;
+  result.i = 0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(3, result.i);
+
+  args[0] = INT_MAX;
+  args[1] = INT_MIN;
+  result.i = 1234;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(-1, result.i);
+
+  args[0] = INT_MAX;
+  args[1] = INT_MAX;
+  result.i = INT_MIN;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(-2, result.i);
+
+  FreeStub(method, sizeof(sum_III_code));
+}
+
+TEST_F(JniInternalTest, StaticSumIntIntIntMethod) {
+  scoped_ptr<DexFile> dex(OpenDexFileBase64(kStaticLeafMethodsDex));
+
+  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
+  ASSERT_TRUE(class_loader != NULL);
+
+  Class* klass = class_linker_->FindClass("LStaticLeafMethods;", class_loader);
+  ASSERT_TRUE(klass != NULL);
+
+  Method* method = klass->FindDirectMethod("sum", "(III)I");
+  ASSERT_TRUE(method != NULL);
+
+  byte sum_IIII_code[] = {
+    0x2d, 0xe9, 0x00, 0x40, 0x83, 0xb0, 0xcd, 0xf8,
+    0x00, 0x00, 0xcd, 0xf8, 0x14, 0x10, 0xcd, 0xf8,
+    0x18, 0x20, 0xcd, 0xf8, 0x1c, 0x30, 0x05, 0x98,
+    0x06, 0x99, 0x42, 0x18, 0xcd, 0xf8, 0x04, 0x20,
+    0x01, 0x9b, 0xdd, 0xf8, 0x1c, 0xc0, 0x13, 0xeb,
+    0x0c, 0x03, 0xcd, 0xf8, 0x04, 0x30, 0x01, 0x98,
+    0x03, 0xb0, 0xbd, 0xe8, 0x00, 0x80, 0x00, 0x00,
+  };
+
+  Method::InvokeStub* stub = AllocateStub(method,
+                                          sum_IIII_code,
+                                          sizeof(sum_IIII_code));
+
+  int args[3];
+  JValue result;
+
+  args[0] = 0;
+  args[1] = 0;
+  args[2] = 0;
+  result.i = -1;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(0, result.i);
+
+  args[0] = 1;
+  args[1] = 2;
+  args[2] = 3;
+  result.i = 0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(6, result.i);
+
+  args[0] = -1;
+  args[1] = 2;
+  args[2] = -3;
+  result.i = 0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(-2, result.i);
+
+  args[0] = INT_MAX;
+  args[1] = INT_MIN;
+  args[2] = INT_MAX;
+  result.i = 1234;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(2147483646, result.i);
+
+  args[0] = INT_MAX;
+  args[1] = INT_MAX;
+  args[2] = INT_MAX;
+  result.i = INT_MIN;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(2147483645, result.i);
+
+  FreeStub(method, sizeof(sum_IIII_code));
+}
+
+TEST_F(JniInternalTest, StaticSumIntIntIntIntMethod) {
+  scoped_ptr<DexFile> dex(OpenDexFileBase64(kStaticLeafMethodsDex));
+
+  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
+  ASSERT_TRUE(class_loader != NULL);
+
+  Class* klass = class_linker_->FindClass("LStaticLeafMethods;", class_loader);
+  ASSERT_TRUE(klass != NULL);
+
+  Method* method = klass->FindDirectMethod("sum", "(IIII)I");
+  ASSERT_TRUE(method != NULL);
+
+  byte sum_IIIII_code[] = {
+    0x2d, 0xe9, 0x00, 0x40, 0x83, 0xb0, 0xcd, 0xf8,
+    0x00, 0x00, 0xcd, 0xf8, 0x14, 0x10, 0xcd, 0xf8,
+    0x18, 0x20, 0xcd, 0xf8, 0x1c, 0x30, 0x05, 0x98,
+    0x06, 0x99, 0x42, 0x18, 0xcd, 0xf8, 0x04, 0x20,
+    0x01, 0x9b, 0xdd, 0xf8, 0x1c, 0xc0, 0x13, 0xeb,
+    0x0c, 0x03, 0xcd, 0xf8, 0x04, 0x30, 0x01, 0x98,
+    0x08, 0x99, 0x40, 0x18, 0xcd, 0xf8, 0x04, 0x00,
+    0x01, 0x98, 0x03, 0xb0, 0xbd, 0xe8, 0x00, 0x80,
+  };
+
+  Method::InvokeStub* stub = AllocateStub(method,
+                                          sum_IIIII_code,
+                                          sizeof(sum_IIIII_code));
+
+  int args[4];
+  JValue result;
+
+  args[0] = 0;
+  args[1] = 0;
+  args[2] = 0;
+  args[3] = 0;
+  result.i = -1;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(0, result.i);
+
+  args[0] = 1;
+  args[1] = 2;
+  args[2] = 3;
+  args[3] = 4;
+  result.i = 0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(10, result.i);
+
+  args[0] = -1;
+  args[1] = 2;
+  args[2] = -3;
+  args[3] = 4;
+  result.i = 0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(2, result.i);
+
+  args[0] = INT_MAX;
+  args[1] = INT_MIN;
+  args[2] = INT_MAX;
+  args[3] = INT_MIN;
+  result.i = 1234;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(-2, result.i);
+
+  args[0] = INT_MAX;
+  args[1] = INT_MAX;
+  args[2] = INT_MAX;
+  args[3] = INT_MAX;
+  result.i = INT_MIN;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(-4, result.i);
+
+  FreeStub(method, sizeof(sum_IIIII_code));
+}
+
+TEST_F(JniInternalTest, StaticSumIntIntIntIntIntMethod) {
+  scoped_ptr<DexFile> dex(OpenDexFileBase64(kStaticLeafMethodsDex));
+
+  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
+  ASSERT_TRUE(class_loader != NULL);
+
+  Class* klass = class_linker_->FindClass("LStaticLeafMethods;", class_loader);
+  ASSERT_TRUE(klass != NULL);
+
+  Method* method = klass->FindDirectMethod("sum", "(IIIII)I");
+  ASSERT_TRUE(method != NULL);
+
+  byte sum_IIIIII_code[] = {
+    0x2d, 0xe9, 0x00, 0x40, 0x83, 0xb0, 0xcd, 0xf8,
+    0x00, 0x00, 0xcd, 0xf8, 0x14, 0x10, 0xcd, 0xf8,
+    0x18, 0x20, 0xcd, 0xf8, 0x1c, 0x30, 0x05, 0x98,
+    0x06, 0x99, 0x42, 0x18, 0xcd, 0xf8, 0x04, 0x20,
+    0x01, 0x9b, 0xdd, 0xf8, 0x1c, 0xc0, 0x13, 0xeb,
+    0x0c, 0x03, 0xcd, 0xf8, 0x04, 0x30, 0x01, 0x98,
+    0x08, 0x99, 0x40, 0x18, 0xcd, 0xf8, 0x04, 0x00,
+    0x01, 0x9a, 0x09, 0x9b, 0xd2, 0x18, 0xcd, 0xf8,
+    0x04, 0x20, 0x01, 0x98, 0x03, 0xb0, 0xbd, 0xe8,
+    0x00, 0x80, 0x00, 0x00,
+  };
+
+  Method::InvokeStub* stub = AllocateStub(method,
+                                          sum_IIIIII_code,
+                                          sizeof(sum_IIIIII_code));
+
+  int args[5];
+  JValue result;
+
+  args[0] = 0;
+  args[1] = 0;
+  args[2] = 0;
+  args[3] = 0;
+  args[4] = 0;
+  result.i = -1.0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(0, result.i);
+
+  args[0] = 1;
+  args[1] = 2;
+  args[2] = 3;
+  args[3] = 4;
+  args[4] = 5;
+  result.i = 0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(15, result.i);
+
+  args[0] = -1;
+  args[1] = 2;
+  args[2] = -3;
+  args[3] = 4;
+  args[4] = -5;
+  result.i = 0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(-3, result.i);
+
+  args[0] = INT_MAX;
+  args[1] = INT_MIN;
+  args[2] = INT_MAX;
+  args[3] = INT_MIN;
+  args[4] = INT_MAX;
+  result.i = 1234;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(2147483645, result.i);
+
+  args[0] = INT_MAX;
+  args[1] = INT_MAX;
+  args[2] = INT_MAX;
+  args[3] = INT_MAX;
+  args[4] = INT_MAX;
+  result.i = INT_MIN;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(2147483643, result.i);
+
+  FreeStub(method, sizeof(sum_IIIIII_code));
+}
+
+TEST_F(JniInternalTest, StaticSumDoubleDoubleMethod) {
+  scoped_ptr<DexFile> dex(OpenDexFileBase64(kStaticLeafMethodsDex));
+
+  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
+  ASSERT_TRUE(class_loader != NULL);
+
+  Class* klass = class_linker_->FindClass("LStaticLeafMethods;", class_loader);
+  ASSERT_TRUE(klass != NULL);
+
+  Method* method = klass->FindDirectMethod("sum", "(DD)D");
+  ASSERT_TRUE(method != NULL);
+
+  byte sum_DDD_code[] = {
+    0x2d, 0xe9, 0x00, 0x40, 0x87, 0xb0, 0xcd, 0xf8,
+    0x00, 0x00, 0xcd, 0xf8, 0x24, 0x10, 0xcd, 0xf8,
+    0x28, 0x20, 0xcd, 0xf8, 0x2c, 0x30, 0x9d, 0xed,
+    0x09, 0x0b, 0x9d, 0xed, 0x0b, 0x1b, 0x30, 0xee,
+    0x01, 0x2b, 0x8d, 0xed, 0x04, 0x2b, 0x04, 0x98,
+    0x05, 0x99, 0x07, 0xb0, 0xbd, 0xe8, 0x00, 0x80,
+  };
+
+  Method::InvokeStub* stub = AllocateStub(method,
+                                          sum_DDD_code,
+                                          sizeof(sum_DDD_code));
+
+  double args[2];
+  JValue result;
+
+  args[0] = 0.0;
+  args[1] = 0.0;
+  result.d = -1.0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(0.0, result.d);
+
+  args[0] = 1.0;
+  args[1] = 2.0;
+  result.d = 0.0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(3.0, result.d);
+
+  args[0] = 1.0;
+  args[1] = -2.0;
+  result.d = 0.0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(-1.0, result.d);
+
+  args[0] = DBL_MAX;
+  args[1] = DBL_MIN;
+  result.d = 0.0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(1.7976931348623157e308, result.d);
+
+  args[0] = DBL_MAX;
+  args[1] = DBL_MAX;
+  result.d = 0.0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(INFINITY, result.d);
+
+  FreeStub(method, sizeof(sum_DDD_code));
+}
+
+TEST_F(JniInternalTest, StaticSumDoubleDoubleDoubleMethod) {
+  scoped_ptr<DexFile> dex(OpenDexFileBase64(kStaticLeafMethodsDex));
+
+  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
+  ASSERT_TRUE(class_loader != NULL);
+
+  Class* klass = class_linker_->FindClass("LStaticLeafMethods;", class_loader);
+  ASSERT_TRUE(klass != NULL);
+
+  Method* method = klass->FindDirectMethod("sum", "(DDD)D");
+  ASSERT_TRUE(method != NULL);
+
+  byte sum_DDDD_code[] = {
+    0x2d, 0xe9, 0x00, 0x40, 0x87, 0xb0, 0xcd, 0xf8,
+    0x00, 0x00, 0xcd, 0xf8, 0x24, 0x10, 0xcd, 0xf8,
+    0x28, 0x20, 0xcd, 0xf8, 0x2c, 0x30, 0x9d, 0xed,
+    0x09, 0x0b, 0x9d, 0xed, 0x0b, 0x1b, 0x30, 0xee,
+    0x01, 0x2b, 0x8d, 0xed, 0x04, 0x2b, 0x9d, 0xed,
+    0x04, 0x3b, 0x9d, 0xed, 0x0d, 0x4b, 0x33, 0xee,
+    0x04, 0x3b, 0x8d, 0xed, 0x04, 0x3b, 0x04, 0x98,
+    0x05, 0x99, 0x07, 0xb0, 0xbd, 0xe8, 0x00, 0x80,
+  };
+
+  Method::InvokeStub* stub = AllocateStub(method,
+                                          sum_DDDD_code,
+                                          sizeof(sum_DDDD_code));
+
+  double args[3];
+  JValue result;
+
+  args[0] = 0.0;
+  args[1] = 0.0;
+  args[2] = 0.0;
+  result.d = -1.0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(0.0, result.d);
+
+  args[0] = 1.0;
+  args[1] = 2.0;
+  args[2] = 3.0;
+  result.d = 0.0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(6.0, result.d);
+
+  args[0] = 1.0;
+  args[1] = -2.0;
+  args[2] = 3.0;
+  result.d = 0.0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(2.0, result.d);
+
+  FreeStub(method, sizeof(sum_DDDD_code));
+}
+
+TEST_F(JniInternalTest, StaticSumDoubleDoubleDoubleDoubleMethod) {
+  scoped_ptr<DexFile> dex(OpenDexFileBase64(kStaticLeafMethodsDex));
+
+  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
+  ASSERT_TRUE(class_loader != NULL);
+
+  Class* klass = class_linker_->FindClass("LStaticLeafMethods;", class_loader);
+  ASSERT_TRUE(klass != NULL);
+
+  Method* method = klass->FindDirectMethod("sum", "(DDDD)D");
+  ASSERT_TRUE(method != NULL);
+
+  byte sum_DDDDD_code[] = {
+    0x2d, 0xe9, 0x00, 0x40, 0x87, 0xb0, 0xcd, 0xf8,
+    0x00, 0x00, 0xcd, 0xf8, 0x24, 0x10, 0xcd, 0xf8,
+    0x28, 0x20, 0xcd, 0xf8, 0x2c, 0x30, 0x9d, 0xed,
+    0x09, 0x0b, 0x9d, 0xed, 0x0b, 0x1b, 0x30, 0xee,
+    0x01, 0x2b, 0x8d, 0xed, 0x04, 0x2b, 0x9d, 0xed,
+    0x04, 0x3b, 0x9d, 0xed, 0x0d, 0x4b, 0x33, 0xee,
+    0x04, 0x3b, 0x8d, 0xed, 0x04, 0x3b, 0x9d, 0xed,
+    0x04, 0x5b, 0x9d, 0xed, 0x0f, 0x6b, 0x35, 0xee,
+    0x06, 0x5b, 0x8d, 0xed, 0x04, 0x5b, 0x04, 0x98,
+    0x05, 0x99, 0x07, 0xb0, 0xbd, 0xe8, 0x00, 0x80,
+  };
+
+  Method::InvokeStub* stub = AllocateStub(method,
+                                          sum_DDDDD_code,
+                                          sizeof(sum_DDDDD_code));
+
+  double args[4];
+  JValue result;
+
+  args[0] = 0.0;
+  args[1] = 0.0;
+  args[2] = 0.0;
+  args[3] = 0.0;
+  result.d = -1.0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(0.0, result.d);
+
+  args[0] = 1.0;
+  args[1] = 2.0;
+  args[2] = 3.0;
+  args[3] = 4.0;
+  result.d = 0.0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(10.0, result.d);
+
+  args[0] = 1.0;
+  args[1] = -2.0;
+  args[2] = 3.0;
+  args[3] = -4.0;
+  result.d = 0.0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(-2.0, result.d);
+
+  FreeStub(method, sizeof(sum_DDDDD_code));
+}
+
+TEST_F(JniInternalTest, StaticSumDoubleDoubleDoubleDoubleDoubleMethod) {
+  scoped_ptr<DexFile> dex(OpenDexFileBase64(kStaticLeafMethodsDex));
+
+  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
+  ASSERT_TRUE(class_loader != NULL);
+
+  Class* klass = class_linker_->FindClass("LStaticLeafMethods;", class_loader);
+  ASSERT_TRUE(klass != NULL);
+
+  Method* method = klass->FindDirectMethod("sum", "(DDDDD)D");
+  ASSERT_TRUE(method != NULL);
+
+  byte sum_DDDDDD_code[] = {
+    0x2d, 0xe9, 0x00, 0x40, 0x87, 0xb0, 0xcd, 0xf8,
+    0x00, 0x00, 0xcd, 0xf8, 0x24, 0x10, 0xcd, 0xf8,
+    0x28, 0x20, 0xcd, 0xf8, 0x2c, 0x30, 0x9d, 0xed,
+    0x09, 0x0b, 0x9d, 0xed, 0x0b, 0x1b, 0x30, 0xee,
+    0x01, 0x2b, 0x8d, 0xed, 0x04, 0x2b, 0x9d, 0xed,
+    0x04, 0x3b, 0x9d, 0xed, 0x0d, 0x4b, 0x33, 0xee,
+    0x04, 0x3b, 0x8d, 0xed, 0x04, 0x3b, 0x9d, 0xed,
+    0x04, 0x5b, 0x9d, 0xed, 0x0f, 0x6b, 0x35, 0xee,
+    0x06, 0x5b, 0x8d, 0xed, 0x04, 0x5b, 0x9d, 0xed,
+    0x04, 0x7b, 0x9d, 0xed, 0x11, 0x0b, 0x37, 0xee,
+    0x00, 0x7b, 0x8d, 0xed, 0x04, 0x7b, 0x04, 0x98,
+    0x05, 0x99, 0x07, 0xb0, 0xbd, 0xe8, 0x00, 0x80,
+  };
+
+  Method::InvokeStub* stub = AllocateStub(method,
+                                          sum_DDDDDD_code,
+                                          sizeof(sum_DDDDDD_code));
+
+  double args[5];
+  JValue result;
+
+  args[0] = 0.0;
+  args[1] = 0.0;
+  args[2] = 0.0;
+  args[3] = 0.0;
+  args[4] = 0.0;
+  result.d = -1.0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(0.0, result.d);
+
+  args[0] = 1.0;
+  args[1] = 2.0;
+  args[2] = 3.0;
+  args[3] = 4.0;
+  args[4] = 5.0;
+  result.d = 0.0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(15.0, result.d);
+
+  args[0] = 1.0;
+  args[1] = -2.0;
+  args[2] = 3.0;
+  args[3] = -4.0;
+  args[4] = 5.0;
+  result.d = 0.0;
+  (*stub)(method, NULL, NULL, reinterpret_cast<byte*>(args), &result);
+  EXPECT_EQ(3.0, result.d);
+
+  FreeStub(method, sizeof(sum_DDDDDD_code));
+}
+#endif  // __arm__
+
+}
diff --git a/src/jni_internal_x86.cc b/src/jni_internal_x86.cc
new file mode 100644
index 0000000..17c7b24
--- /dev/null
+++ b/src/jni_internal_x86.cc
@@ -0,0 +1,15 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+// Author: cshapiro@google.com (Carl Shapiro)
+
+#include "jni_internal.h"
+
+#include "assembler.h"
+#include "object.h"
+
+namespace art {
+
+void CreateInvokeStub(Assembler* assembler, Method* method) {
+  UNIMPLEMENTED(FATAL);
+}
+
+}  // namespace art
diff --git a/src/object.cc b/src/object.cc
index 7ea30a5..34b0bb9 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -288,6 +288,24 @@
   return num_registers;
 }
 
+size_t Method::NumArgArrayBytes() {
+  const StringPiece& shorty = GetShorty();
+  size_t num_bytes = 0;
+  for (int i = 1; i < shorty.size(); ++i) {
+    char ch = shorty[i];
+    if (ch == 'D' || ch == 'J') {
+      num_bytes += 8;
+    } if (ch == 'L') {
+      // Argument is a reference or an array.  The shorty descriptor
+      // does not distinguish between these types.
+      num_bytes += sizeof(Object*);
+    } else {
+      num_bytes += 4;
+    }
+  }
+  return num_bytes;
+}
+
 // The number of reference arguments to this method including implicit this
 // pointer
 size_t Method::NumReferenceArgs() const {
diff --git a/src/object.h b/src/object.h
index dfcc626..927729c 100644
--- a/src/object.h
+++ b/src/object.h
@@ -334,6 +334,13 @@
 
 class Method : public AccessibleObject {
  public:
+  // An function that invokes a method with an array of its arguments.
+  typedef void InvokeStub(Method* method,
+                          Object* obj,
+                          Thread* thread,
+                          byte* args,
+                          JValue* result);
+
   // Returns the method name, e.g. "<init>" or "eatLunch"
   const String* GetName() const {
     return name_;
@@ -394,6 +401,10 @@
   // Number of argument registers required by the prototype.
   uint32_t NumArgRegisters();
 
+  // Number of argument bytes required for densely packing the
+  // arguments into an array of arguments.
+  size_t NumArgArrayBytes();
+
  public:  // TODO: private
   // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
   // the class we are a part of
@@ -409,6 +420,10 @@
   uint32_t java_generic_types_are_initialized_;
   uint32_t java_slot_;
 
+  const StringPiece& GetShorty() const {
+    return shorty_;
+  }
+
   bool IsReturnAReference() const {
     return (shorty_[0] == 'L') || (shorty_[0] == '[');
   }
@@ -452,10 +467,10 @@
   }
 
   // The number of reference arguments to this method including implicit this
-  // pointer
+  // pointer.
   size_t NumReferenceArgs() const;
 
-  // The number of long or double arguments
+  // The number of long or double arguments.
   size_t NumLongOrDoubleArgs() const;
 
   // The number of reference arguments to this method before the given
@@ -474,12 +489,16 @@
   // Size in bytes of the return value
   size_t ReturnSize() const;
 
+  const void* GetCode() const {
+    return code_;
+  }
+
   void SetCode(const void* code) {
     code_ = code;
   }
 
-  const void* GetCode() const {
-    return code_;
+  static size_t GetCodeOffset() {
+    return OFFSETOF_MEMBER(Method, code_);
   }
 
   void RegisterNative(const void* native_method) {
@@ -490,6 +509,18 @@
     return MemberOffset(OFFSETOF_MEMBER(Method, native_method_));
   }
 
+  InvokeStub* GetInvokeStub() const {
+    return invoke_stub_;
+  }
+
+  void SetInvokeStub(const InvokeStub* invoke_stub) {
+    invoke_stub_ = invoke_stub;
+  }
+
+  static size_t GetInvokeStubOffset() {
+    return OFFSETOF_MEMBER(Method, invoke_stub_);
+  }
+
   bool HasSameNameAndDescriptor(const Method* that) const;
 
  public:  // TODO: private/const
@@ -539,6 +570,9 @@
   // Any native method registered with this method
   const void* native_method_;
 
+  // Native invocation stub entry point.
+  const InvokeStub* invoke_stub_;
+
   DISALLOW_IMPLICIT_CONSTRUCTORS(Method);
 };