Implement upcalls with compiled JNI callback bridges.
Change-Id: Ib475a5957a3e2596a812df1314fbc73a96f01725
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,