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);
};