summaryrefslogtreecommitdiff
path: root/src/jni_internal.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/jni_internal.cc')
-rw-r--r--src/jni_internal.cc504
1 files changed, 427 insertions, 77 deletions
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index ef3bf57770..599b0fe68f 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -3,9 +3,10 @@
#include "jni_internal.h"
#include <cstdarg>
-#include <vector>
-#include <utility>
+#include <dlfcn.h>
#include <sys/mman.h>
+#include <utility>
+#include <vector>
#include "class_linker.h"
#include "jni.h"
@@ -18,6 +19,235 @@
namespace art {
+enum JNI_OnLoadState {
+ kPending = 0, /* initial state, must be zero */
+ kFailed,
+ kOkay,
+};
+
+struct SharedLibrary {
+ SharedLibrary() : jni_on_load_lock("JNI_OnLoad") {
+ }
+
+ // Path to library "/system/lib/libjni.so".
+ std::string path;
+
+ // The void* returned by dlopen(3).
+ void* handle;
+
+ // The ClassLoader this library is associated with.
+ Object* class_loader;
+
+ // Guards remaining items.
+ Mutex jni_on_load_lock;
+ // Wait for JNI_OnLoad in other thread.
+ pthread_cond_t jni_on_load_cond;
+ // Recursive invocation guard.
+ uint32_t jni_on_load_tid;
+ // Result of earlier JNI_OnLoad call.
+ JNI_OnLoadState jni_on_load_result;
+};
+
+/*
+ * Check the result of an earlier call to JNI_OnLoad on this library. If
+ * the call has not yet finished in another thread, wait for it.
+ */
+bool CheckOnLoadResult(JavaVMExt* vm, SharedLibrary* library) {
+ Thread* self = Thread::Current();
+ if (library->jni_on_load_tid == self->GetId()) {
+ // Check this so we don't end up waiting for ourselves. We need
+ // to return "true" so the caller can continue.
+ LOG(INFO) << *self << " recursive attempt to load library "
+ << "\"" << library->path << "\"";
+ return true;
+ }
+
+ UNIMPLEMENTED(ERROR) << "need to pthread_cond_wait!";
+ // MutexLock mu(&library->jni_on_load_lock);
+ while (library->jni_on_load_result == kPending) {
+ if (vm->verbose_jni) {
+ LOG(INFO) << "[" << *self << " waiting for \"" << library->path << "\" "
+ << "JNI_OnLoad...]";
+ }
+ Thread::State old_state = self->GetState();
+ self->SetState(Thread::kWaiting); // TODO: VMWAIT
+ // pthread_cond_wait(&library->jni_on_load_cond, &library->jni_on_load_lock);
+ self->SetState(old_state);
+ }
+
+ bool okay = (library->jni_on_load_result == kOkay);
+ if (vm->verbose_jni) {
+ LOG(INFO) << "[Earlier JNI_OnLoad for \"" << library->path << "\" "
+ << (okay ? "succeeded" : "failed") << "]";
+ }
+ return okay;
+}
+
+typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
+
+/*
+ * Load native code from the specified absolute pathname. Per the spec,
+ * if we've already loaded a library with the specified pathname, we
+ * return without doing anything.
+ *
+ * TODO? for better results we should absolutify the pathname. For fully
+ * correct results we should stat to get the inode and compare that. The
+ * existing implementation is fine so long as everybody is using
+ * System.loadLibrary.
+ *
+ * The library will be associated with the specified class loader. The JNI
+ * spec says we can't load the same library into more than one class loader.
+ *
+ * Returns "true" on success. On failure, sets *detail to a
+ * human-readable description of the error or NULL if no detail is
+ * available; ownership of the string is transferred to the caller.
+ */
+bool JavaVMExt::LoadNativeLibrary(const std::string& path, Object* class_loader, char** detail) {
+ *detail = NULL;
+
+ // See if we've already loaded this library. If we have, and the class loader
+ // matches, return successfully without doing anything.
+ SharedLibrary* library = libraries[path];
+ if (library != NULL) {
+ if (library->class_loader != class_loader) {
+ LOG(WARNING) << "Shared library \"" << path << "\" already opened by "
+ << "ClassLoader " << library->class_loader << "; "
+ << "can't open in " << class_loader;
+ *detail = strdup("already opened by different ClassLoader");
+ return false;
+ }
+ if (verbose_jni) {
+ LOG(INFO) << "[Shared library \"" << path << "\" already loaded in "
+ << "ClassLoader " << class_loader << "]";
+ }
+ if (!CheckOnLoadResult(this, library)) {
+ *detail = strdup("JNI_OnLoad failed before");
+ return false;
+ }
+ return true;
+ }
+
+ // Open the shared library. Because we're using a full path, the system
+ // doesn't have to search through LD_LIBRARY_PATH. (It may do so to
+ // resolve this library's dependencies though.)
+
+ // Failures here are expected when java.library.path has several entries
+ // and we have to hunt for the lib.
+
+ // The current version of the dynamic linker prints detailed information
+ // about dlopen() failures. Some things to check if the message is
+ // cryptic:
+ // - make sure the library exists on the device
+ // - verify that the right path is being opened (the debug log message
+ // above can help with that)
+ // - check to see if the library is valid (e.g. not zero bytes long)
+ // - check config/prelink-linux-arm.map to ensure that the library
+ // is listed and is not being overrun by the previous entry (if
+ // loading suddenly stops working on a prelinked library, this is
+ // a good one to check)
+ // - write a trivial app that calls sleep() then dlopen(), attach
+ // to it with "strace -p <pid>" while it sleeps, and watch for
+ // attempts to open nonexistent dependent shared libs
+
+ // TODO: automate some of these checks!
+
+ // This can execute slowly for a large library on a busy system, so we
+ // want to switch from RUNNING to VMWAIT while it executes. This allows
+ // the GC to ignore us.
+ Thread* self = Thread::Current();
+ Thread::State old_state = self->GetState();
+ self->SetState(Thread::kWaiting); // TODO: VMWAIT
+ void* handle = dlopen(path.c_str(), RTLD_LAZY);
+ self->SetState(old_state);
+
+ if (verbose_jni) {
+ LOG(INFO) << "[Call to dlopen(\"" << path << "\") returned " << handle << "]";
+ }
+
+ if (handle == NULL) {
+ *detail = strdup(dlerror());
+ return false;
+ }
+
+ // Create a new entry.
+ library = new SharedLibrary;
+ library->path = path;
+ library->handle = handle;
+ library->class_loader = class_loader;
+ UNIMPLEMENTED(ERROR) << "missing pthread_cond_init";
+ // pthread_cond_init(&library->onLoadCond, NULL);
+ library->jni_on_load_tid = self->GetId();
+
+ libraries[path] = library;
+
+// if (pNewEntry != pActualEntry) {
+// LOG(INFO) << "WOW: we lost a race to add a shared library (\"" << path << "\" ClassLoader=" << class_loader <<")";
+// freeSharedLibEntry(pNewEntry);
+// return CheckOnLoadResult(this, pActualEntry);
+// } else
+ {
+ if (verbose_jni) {
+ LOG(INFO) << "[Added shared library \"" << path << "\" for ClassLoader " << class_loader << "]";
+ }
+
+ bool result = true;
+ void* sym = dlsym(handle, "JNI_OnLoad");
+ if (sym == NULL) {
+ if (verbose_jni) {
+ LOG(INFO) << "[No JNI_OnLoad found in \"" << path << "\"]";
+ }
+ } else {
+ // Call JNI_OnLoad. We have to override the current class
+ // loader, which will always be "null" since the stuff at the
+ // top of the stack is around Runtime.loadLibrary(). (See
+ // the comments in the JNI FindClass function.)
+ UNIMPLEMENTED(WARNING) << "need to override current class loader";
+ JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
+ //Object* prevOverride = self->classLoaderOverride;
+ //self->classLoaderOverride = classLoader;
+
+ old_state = self->GetState();
+ self->SetState(Thread::kNative);
+ if (verbose_jni) {
+ LOG(INFO) << "[Calling JNI_OnLoad in \"" << path << "\"]";
+ }
+ int version = (*jni_on_load)(reinterpret_cast<JavaVM*>(this), NULL);
+ self->SetState(old_state);
+
+ UNIMPLEMENTED(WARNING) << "need to restore current class loader";
+ //self->classLoaderOverride = prevOverride;
+
+ if (version != JNI_VERSION_1_2 &&
+ version != JNI_VERSION_1_4 &&
+ version != JNI_VERSION_1_6) {
+ LOG(WARNING) << "JNI_OnLoad in \"" << path << "\" returned "
+ << "bad version: " << version;
+ // It's unwise to call dlclose() here, but we can mark it
+ // as bad and ensure that future load attempts will fail.
+ // We don't know how far JNI_OnLoad got, so there could
+ // be some partially-initialized stuff accessible through
+ // newly-registered native method calls. We could try to
+ // unregister them, but that doesn't seem worthwhile.
+ result = false;
+ } else {
+ if (verbose_jni) {
+ LOG(INFO) << "[Returned " << (result ? "successfully" : "failure")
+ << " from JNI_OnLoad in \"" << path << "\"]";
+ }
+ }
+ }
+
+ library->jni_on_load_result = result ? kOkay : kFailed;
+ library->jni_on_load_tid = 0;
+
+ // Broadcast a wakeup to anybody sleeping on the condition variable.
+ UNIMPLEMENTED(ERROR) << "missing pthread_cond_broadcast";
+ // MutexLock mu(&library->jni_on_load_lock);
+ // pthread_cond_broadcast(&library->jni_on_load_cond);
+ return result;
+ }
+}
+
// Entry/exit processing for all JNI calls.
//
// This performs the necessary thread state switching, lets us amortize the
@@ -25,7 +255,8 @@ namespace art {
// that are using a JNIEnv on the wrong thread.
class ScopedJniThreadState {
public:
- explicit ScopedJniThreadState(JNIEnv* env) {
+ explicit ScopedJniThreadState(JNIEnv* env)
+ : env_(reinterpret_cast<JNIEnvExt*>(env)) {
self_ = ThreadForEnv(env);
self_->SetState(Thread::kRunnable);
}
@@ -34,6 +265,10 @@ class ScopedJniThreadState {
self_->SetState(Thread::kNative);
}
+ JNIEnvExt* Env() {
+ return env_;
+ }
+
Thread* Self() {
return self_;
}
@@ -52,14 +287,114 @@ class ScopedJniThreadState {
return self;
}
+ JNIEnvExt* env_;
Thread* self_;
DISALLOW_COPY_AND_ASSIGN(ScopedJniThreadState);
};
+/*
+ * Add a local reference for an object to the current stack frame. When
+ * the native function returns, the reference will be discarded.
+ *
+ * We need to allow the same reference to be added multiple times.
+ *
+ * This will be called on otherwise unreferenced objects. We cannot do
+ * GC allocations here, and it's best if we don't grab a mutex.
+ *
+ * Returns the local reference (currently just the same pointer that was
+ * passed in), or NULL on failure.
+ */
template<typename T>
T AddLocalReference(ScopedJniThreadState& ts, Object* obj) {
- UNIMPLEMENTED(WARNING);
- return reinterpret_cast<T>(obj);
+ if (obj == NULL) {
+ return NULL;
+ }
+
+ IndirectReferenceTable& locals = ts.Env()->locals;
+
+ uint32_t cookie = IRT_FIRST_SEGMENT; // TODO
+ IndirectRef ref = locals.Add(cookie, obj);
+ if (ref == NULL) {
+ // TODO: just change Add's DCHECK to CHECK and lose this?
+ locals.Dump();
+ LOG(FATAL) << "Failed adding to JNI local reference table "
+ << "(has " << locals.Capacity() << " entries)";
+ // TODO: dvmDumpThread(dvmThreadSelf(), false);
+ }
+
+#if 0 // TODO: fix this to understand PushLocalFrame, so we can turn it on.
+ if (ts.Env()->check_jni) {
+ size_t entry_count = locals.Capacity();
+ if (entry_count > 16) {
+ std::string class_name(PrettyDescriptor(obj->GetClass()->GetDescriptor()));
+ LOG(WARNING) << "Warning: more than 16 JNI local references: "
+ << entry_count << " (most recent was a " << class_name << ")";
+ locals.Dump();
+ // TODO: dvmDumpThread(dvmThreadSelf(), false);
+ // dvmAbort();
+ }
+ }
+#endif
+
+ if (false /*gDvmJni.workAroundAppJniBugs*/) { // TODO
+ // Hand out direct pointers to support broken old apps.
+ return reinterpret_cast<T>(obj);
+ }
+
+ return reinterpret_cast<T>(ref);
+}
+
+template<typename T>
+T Decode(ScopedJniThreadState& ts, jobject obj) {
+ if (obj == NULL) {
+ return NULL;
+ }
+
+ IndirectRef ref = reinterpret_cast<IndirectRef>(obj);
+ IndirectRefKind kind = GetIndirectRefKind(ref);
+ Object* result;
+ switch (kind) {
+ case kLocal:
+ {
+ IndirectReferenceTable& locals = ts.Env()->locals;
+ result = locals.Get(ref);
+ break;
+ }
+ case kGlobal:
+ {
+ JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+ IndirectReferenceTable& globals = vm->globals;
+ MutexLock mu(&vm->globals_lock);
+ result = globals.Get(ref);
+ break;
+ }
+ case kWeakGlobal:
+ {
+ JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+ IndirectReferenceTable& weak_globals = vm->weak_globals;
+ MutexLock mu(&vm->weak_globals_lock);
+ result = weak_globals.Get(ref);
+ if (result == kClearedJniWeakGlobal) {
+ // This is a special case where it's okay to return NULL.
+ return NULL;
+ }
+ break;
+ }
+ case kInvalid:
+ default:
+ if (false /*gDvmJni.workAroundAppJniBugs*/) { // TODO
+ // Assume an invalid local reference is actually a direct pointer.
+ return reinterpret_cast<T>(obj);
+ }
+ LOG(FATAL) << "Invalid indirect reference " << obj;
+ return reinterpret_cast<T>(kInvalidIndirectRefObject);
+ }
+
+ if (result == NULL) {
+ LOG(FATAL) << "JNI ERROR (app bug): use of deleted " << kind << ": "
+ << obj;
+ }
+ return reinterpret_cast<T>(result);
}
void CreateInvokeStub(Assembler* assembler, Method* method);
@@ -85,7 +420,7 @@ bool EnsureInvokeStub(Method* method) {
return true;
}
-byte* CreateArgArray(Method* method, va_list ap) {
+byte* CreateArgArray(ScopedJniThreadState& ts, 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();
@@ -104,8 +439,7 @@ byte* CreateArgArray(Method* method, va_list ap) {
offset += 4;
break;
case 'L': {
- // TODO: DecodeReference
- Object* obj = reinterpret_cast<Object*>(va_arg(ap, jobject));
+ Object* obj = Decode<Object*>(ts, va_arg(ap, jobject));
*reinterpret_cast<Object**>(&arg_array[offset]) = obj;
offset += sizeof(Object*);
break;
@@ -123,7 +457,7 @@ byte* CreateArgArray(Method* method, va_list ap) {
return arg_array.release();
}
-byte* CreateArgArray(Method* method, jvalue* args) {
+byte* CreateArgArray(ScopedJniThreadState& ts, Method* method, jvalue* args) {
size_t num_bytes = method->NumArgArrayBytes();
scoped_array<byte> arg_array(new byte[num_bytes]);
const StringPiece& shorty = method->GetShorty();
@@ -142,8 +476,7 @@ byte* CreateArgArray(Method* method, jvalue* args) {
offset += 4;
break;
case 'L': {
- // TODO: DecodeReference
- Object* obj = reinterpret_cast<Object*>(args[i - 1].l);
+ Object* obj = Decode<Object*>(ts, args[i - 1].l);
*reinterpret_cast<Object**>(&arg_array[offset]) = obj;
offset += sizeof(Object*);
break;
@@ -161,8 +494,8 @@ byte* CreateArgArray(Method* method, jvalue* args) {
return arg_array.release();
}
-JValue InvokeWithArgArray(Thread* self, Object* obj, jmethodID method_id,
- byte* args) {
+JValue InvokeWithArgArray(ScopedJniThreadState& ts,
+ Object* obj, jmethodID method_id, byte* args) {
// TODO: DecodeReference
Method* method = reinterpret_cast<Method*>(method_id);
// Call the invoke stub associated with the method
@@ -170,22 +503,22 @@ JValue InvokeWithArgArray(Thread* self, Object* obj, jmethodID method_id,
const Method::InvokeStub* stub = method->GetInvokeStub();
CHECK(stub != NULL);
JValue result;
- (*stub)(method, obj, self, args, &result);
+ (*stub)(method, obj, ts.Self(), args, &result);
return result;
}
-JValue InvokeWithJValues(Thread* self, Object* obj, jmethodID method_id,
- jvalue* args) {
+JValue InvokeWithJValues(ScopedJniThreadState& ts,
+ Object* obj, jmethodID method_id, jvalue* args) {
Method* method = reinterpret_cast<Method*>(method_id);
- scoped_array<byte> arg_array(CreateArgArray(method, args));
- return InvokeWithArgArray(self, obj, method_id, arg_array.get());
+ scoped_array<byte> arg_array(CreateArgArray(ts, method, args));
+ return InvokeWithArgArray(ts, obj, method_id, arg_array.get());
}
-JValue InvokeWithVarArgs(Thread* self, Object* obj, jmethodID method_id,
- va_list args) {
+JValue InvokeWithVarArgs(ScopedJniThreadState& ts,
+ 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(self, obj, method_id, arg_array.get());
+ scoped_array<byte> arg_array(CreateArgArray(ts, method, args));
+ return InvokeWithArgArray(ts, obj, method_id, arg_array.get());
}
jint GetVersion(JNIEnv* env) {
@@ -320,14 +653,14 @@ void FatalError(JNIEnv* env, const char* msg) {
jint PushLocalFrame(JNIEnv* env, jint cap) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
- return 0;
+ UNIMPLEMENTED(WARNING) << "ignoring PushLocalFrame(" << cap << ")";
+ return JNI_OK;
}
jobject PopLocalFrame(JNIEnv* env, jobject res) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
- return NULL;
+ UNIMPLEMENTED(WARNING) << "ignoring PopLocalFrame " << res;
+ return res;
}
jobject NewGlobalRef(JNIEnv* env, jobject lobj) {
@@ -343,7 +676,23 @@ void DeleteGlobalRef(JNIEnv* env, jobject gref) {
void DeleteLocalRef(JNIEnv* env, jobject obj) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(WARNING);
+
+ if (obj == NULL) {
+ return;
+ }
+
+ IndirectReferenceTable& locals = ts.Env()->locals;
+
+ uint32_t cookie = IRT_FIRST_SEGMENT; // TODO
+ if (!locals.Remove(cookie, obj)) {
+ // Attempting to delete a local reference that is not in the
+ // topmost local reference frame is a no-op. DeleteLocalRef returns
+ // void and doesn't throw any exceptions, but we should probably
+ // complain about it so the user will notice that things aren't
+ // going quite the way they expect.
+ LOG(WARNING) << "JNI WARNING: DeleteLocalRef(" << obj << ") "
+ << "failed to find entry";
+ }
}
jboolean IsSameObject(JNIEnv* env, jobject obj1, jobject obj2) {
@@ -403,10 +752,8 @@ jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass clazz) {
// NB. JNI is different from regular Java instanceof in this respect
return JNI_TRUE;
} else {
- // TODO: retrieve handle value for object
- Object* obj = reinterpret_cast<Object*>(jobj);
- // TODO: retrieve handle value for class
- Class* klass = reinterpret_cast<Class*>(clazz);
+ Object* obj = Decode<Object*>(ts, jobj);
+ Class* klass = Decode<Class*>(ts, clazz);
return Object::InstanceOf(obj, klass) ? JNI_TRUE : JNI_FALSE;
}
}
@@ -414,8 +761,7 @@ jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass clazz) {
jmethodID GetMethodID(JNIEnv* env,
jclass clazz, const char* name, const char* sig) {
ScopedJniThreadState ts(env);
- // TODO: retrieve handle value for class
- Class* klass = reinterpret_cast<Class*>(clazz);
+ Class* klass = Decode<Class*>(ts, clazz);
if (!klass->IsInitialized()) {
// TODO: initialize the class
}
@@ -965,8 +1311,7 @@ void SetDoubleField(JNIEnv* env, jobject obj, jfieldID fieldID, jdouble val) {
jmethodID GetStaticMethodID(JNIEnv* env,
jclass clazz, const char* name, const char* sig) {
ScopedJniThreadState ts(env);
- // TODO: DecodeReference
- Class* klass = reinterpret_cast<Class*>(clazz);
+ Class* klass = Decode<Class*>(ts, clazz);
if (!klass->IsInitialized()) {
// TODO: initialize the class
}
@@ -1005,21 +1350,21 @@ jobject CallStaticObjectMethod(JNIEnv* env,
ScopedJniThreadState ts(env);
va_list ap;
va_start(ap, methodID);
- JValue result = InvokeWithVarArgs(ts.Self(), NULL, methodID, ap);
+ JValue result = InvokeWithVarArgs(ts, NULL, methodID, ap);
return AddLocalReference<jobject>(ts, result.l);
}
jobject CallStaticObjectMethodV(JNIEnv* env,
jclass clazz, jmethodID methodID, va_list args) {
ScopedJniThreadState ts(env);
- JValue result = InvokeWithVarArgs(ts.Self(), NULL, methodID, args);
+ JValue result = InvokeWithVarArgs(ts, NULL, methodID, args);
return AddLocalReference<jobject>(ts, result.l);
}
jobject CallStaticObjectMethodA(JNIEnv* env,
jclass clazz, jmethodID methodID, jvalue* args) {
ScopedJniThreadState ts(env);
- JValue result = InvokeWithJValues(ts.Self(), NULL, methodID, args);
+ JValue result = InvokeWithJValues(ts, NULL, methodID, args);
return AddLocalReference<jobject>(ts, result.l);
}
@@ -1028,171 +1373,171 @@ jboolean CallStaticBooleanMethod(JNIEnv* env,
ScopedJniThreadState ts(env);
va_list ap;
va_start(ap, methodID);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).z;
+ return InvokeWithVarArgs(ts, NULL, methodID, ap).z;
}
jboolean CallStaticBooleanMethodV(JNIEnv* env,
jclass clazz, jmethodID methodID, va_list args) {
ScopedJniThreadState ts(env);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).z;
+ return InvokeWithVarArgs(ts, NULL, methodID, args).z;
}
jboolean CallStaticBooleanMethodA(JNIEnv* env,
jclass clazz, jmethodID methodID, jvalue* args) {
ScopedJniThreadState ts(env);
- return InvokeWithJValues(ts.Self(), NULL, methodID, args).z;
+ return InvokeWithJValues(ts, NULL, methodID, args).z;
}
jbyte CallStaticByteMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
ScopedJniThreadState ts(env);
va_list ap;
va_start(ap, methodID);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).b;
+ return InvokeWithVarArgs(ts, NULL, methodID, ap).b;
}
jbyte CallStaticByteMethodV(JNIEnv* env,
jclass clazz, jmethodID methodID, va_list args) {
ScopedJniThreadState ts(env);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).b;
+ return InvokeWithVarArgs(ts, NULL, methodID, args).b;
}
jbyte CallStaticByteMethodA(JNIEnv* env,
jclass clazz, jmethodID methodID, jvalue* args) {
ScopedJniThreadState ts(env);
- return InvokeWithJValues(ts.Self(), NULL, methodID, args).b;
+ return InvokeWithJValues(ts, NULL, methodID, args).b;
}
jchar CallStaticCharMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
ScopedJniThreadState ts(env);
va_list ap;
va_start(ap, methodID);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).c;
+ return InvokeWithVarArgs(ts, NULL, methodID, ap).c;
}
jchar CallStaticCharMethodV(JNIEnv* env,
jclass clazz, jmethodID methodID, va_list args) {
ScopedJniThreadState ts(env);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).c;
+ return InvokeWithVarArgs(ts, NULL, methodID, args).c;
}
jchar CallStaticCharMethodA(JNIEnv* env,
jclass clazz, jmethodID methodID, jvalue* args) {
ScopedJniThreadState ts(env);
- return InvokeWithJValues(ts.Self(), NULL, methodID, args).c;
+ return InvokeWithJValues(ts, NULL, methodID, args).c;
}
jshort CallStaticShortMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
ScopedJniThreadState ts(env);
va_list ap;
va_start(ap, methodID);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).s;
+ return InvokeWithVarArgs(ts, NULL, methodID, ap).s;
}
jshort CallStaticShortMethodV(JNIEnv* env,
jclass clazz, jmethodID methodID, va_list args) {
ScopedJniThreadState ts(env);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).s;
+ return InvokeWithVarArgs(ts, NULL, methodID, args).s;
}
jshort CallStaticShortMethodA(JNIEnv* env,
jclass clazz, jmethodID methodID, jvalue* args) {
ScopedJniThreadState ts(env);
- return InvokeWithJValues(ts.Self(), NULL, methodID, args).s;
+ return InvokeWithJValues(ts, NULL, methodID, args).s;
}
jint CallStaticIntMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
ScopedJniThreadState ts(env);
va_list ap;
va_start(ap, methodID);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).i;
+ return InvokeWithVarArgs(ts, NULL, methodID, ap).i;
}
jint CallStaticIntMethodV(JNIEnv* env,
jclass clazz, jmethodID methodID, va_list args) {
ScopedJniThreadState ts(env);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).i;
+ return InvokeWithVarArgs(ts, NULL, methodID, args).i;
}
jint CallStaticIntMethodA(JNIEnv* env,
jclass clazz, jmethodID methodID, jvalue* args) {
ScopedJniThreadState ts(env);
- return InvokeWithJValues(ts.Self(), NULL, methodID, args).i;
+ return InvokeWithJValues(ts, NULL, methodID, args).i;
}
jlong CallStaticLongMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
ScopedJniThreadState ts(env);
va_list ap;
va_start(ap, methodID);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).j;
+ return InvokeWithVarArgs(ts, NULL, methodID, ap).j;
}
jlong CallStaticLongMethodV(JNIEnv* env,
jclass clazz, jmethodID methodID, va_list args) {
ScopedJniThreadState ts(env);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).j;
+ return InvokeWithVarArgs(ts, NULL, methodID, args).j;
}
jlong CallStaticLongMethodA(JNIEnv* env,
jclass clazz, jmethodID methodID, jvalue* args) {
ScopedJniThreadState ts(env);
- return InvokeWithJValues(ts.Self(), NULL, methodID, args).j;
+ return InvokeWithJValues(ts, NULL, methodID, args).j;
}
jfloat CallStaticFloatMethod(JNIEnv* env, jclass cls, jmethodID methodID, ...) {
ScopedJniThreadState ts(env);
va_list ap;
va_start(ap, methodID);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).f;
+ return InvokeWithVarArgs(ts, NULL, methodID, ap).f;
}
jfloat CallStaticFloatMethodV(JNIEnv* env,
jclass clazz, jmethodID methodID, va_list args) {
ScopedJniThreadState ts(env);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).f;
+ return InvokeWithVarArgs(ts, NULL, methodID, args).f;
}
jfloat CallStaticFloatMethodA(JNIEnv* env,
jclass clazz, jmethodID methodID, jvalue* args) {
ScopedJniThreadState ts(env);
- return InvokeWithJValues(ts.Self(), NULL, methodID, args).f;
+ return InvokeWithJValues(ts, NULL, methodID, args).f;
}
jdouble CallStaticDoubleMethod(JNIEnv* env, jclass cls, jmethodID methodID, ...) {
ScopedJniThreadState ts(env);
va_list ap;
va_start(ap, methodID);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).d;
+ return InvokeWithVarArgs(ts, NULL, methodID, ap).d;
}
jdouble CallStaticDoubleMethodV(JNIEnv* env,
jclass clazz, jmethodID methodID, va_list args) {
ScopedJniThreadState ts(env);
- return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).d;
+ return InvokeWithVarArgs(ts, NULL, methodID, args).d;
}
jdouble CallStaticDoubleMethodA(JNIEnv* env,
jclass clazz, jmethodID methodID, jvalue* args) {
ScopedJniThreadState ts(env);
- return InvokeWithJValues(ts.Self(), NULL, methodID, args).d;
+ return InvokeWithJValues(ts, NULL, methodID, args).d;
}
void CallStaticVoidMethod(JNIEnv* env, jclass cls, jmethodID methodID, ...) {
ScopedJniThreadState ts(env);
va_list ap;
va_start(ap, methodID);
- InvokeWithVarArgs(ts.Self(), NULL, methodID, ap);
+ InvokeWithVarArgs(ts, NULL, methodID, ap);
}
void CallStaticVoidMethodV(JNIEnv* env,
jclass cls, jmethodID methodID, va_list args) {
ScopedJniThreadState ts(env);
- InvokeWithVarArgs(ts.Self(), NULL, methodID, args);
+ InvokeWithVarArgs(ts, NULL, methodID, args);
}
void CallStaticVoidMethodA(JNIEnv* env,
jclass cls, jmethodID methodID, jvalue* args) {
ScopedJniThreadState ts(env);
- InvokeWithJValues(ts.Self(), NULL, methodID, args);
+ InvokeWithJValues(ts, NULL, methodID, args);
}
jfieldID GetStaticFieldID(JNIEnv* env,
@@ -1375,9 +1720,8 @@ jobject GetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index) {
void SetObjectArrayElement(JNIEnv* env,
jobjectArray java_array, jsize index, jobject java_value) {
ScopedJniThreadState ts(env);
- // TODO: DecodeReference
- ObjectArray<Object>* array = reinterpret_cast<ObjectArray<Object>*>(java_array);
- Object* value = reinterpret_cast<Object*>(java_value);
+ ObjectArray<Object>* array = Decode<ObjectArray<Object>*>(ts, java_array);
+ Object* value = Decode<Object*>(ts, java_value);
array->Set(index, value);
}
@@ -1428,8 +1772,7 @@ jobjectArray NewObjectArray(JNIEnv* env, jsize length, jclass element_jclass, jo
CHECK_GE(length, 0); // TODO: ReportJniError
// Compute the array class corresponding to the given element class.
- // TODO: DecodeReference
- Class* element_class = reinterpret_cast<Class*>(element_jclass);
+ Class* element_class = Decode<Class*>(ts, element_jclass);
std::string descriptor;
descriptor += "[";
descriptor += element_class->GetDescriptor().ToString();
@@ -1651,11 +1994,16 @@ void SetDoubleArrayRegion(JNIEnv* env,
jint RegisterNatives(JNIEnv* env,
jclass clazz, const JNINativeMethod* methods, jint nMethods) {
ScopedJniThreadState ts(env);
- // TODO: retrieve handle value for class
- Class* klass = reinterpret_cast<Class*>(clazz);
+ Class* klass = Decode<Class*>(ts, clazz);
for(int i = 0; i < nMethods; i++) {
const char* name = methods[i].name;
const char* sig = methods[i].signature;
+
+ if (*sig == '!') {
+ // TODO: fast jni. it's too noisy to log all these.
+ ++sig;
+ }
+
Method* method = klass->FindDirectMethod(name, sig);
if (method == NULL) {
method = klass->FindVirtualMethod(name, sig);
@@ -1704,7 +2052,7 @@ jint GetJavaVM(JNIEnv* env, JavaVM** vm) {
ScopedJniThreadState ts(env);
Runtime* runtime = Runtime::Current();
if (runtime != NULL) {
- *vm = runtime->GetJavaVM();
+ *vm = reinterpret_cast<JavaVM*>(runtime->GetJavaVM());
} else {
*vm = NULL;
}
@@ -2058,7 +2406,7 @@ extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, void** p_env, void* vm_args) {
return JNI_ERR;
} else {
*p_env = reinterpret_cast<JNIEnv*>(Thread::Current()->GetJniEnv());
- *p_vm = runtime->GetJavaVM();
+ *p_vm = reinterpret_cast<JavaVM*>(runtime->GetJavaVM());
return JNI_OK;
}
}
@@ -2069,7 +2417,7 @@ extern "C" jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize, jsize* vm_count) {
*vm_count = 0;
} else {
*vm_count = 1;
- vms[0] = runtime->GetJavaVM();
+ vms[0] = reinterpret_cast<JavaVM*>(runtime->GetJavaVM());
}
return JNI_OK;
}
@@ -2085,7 +2433,6 @@ jint DestroyJavaVM(JavaVM* vm) {
} else {
JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
delete raw_vm->runtime;
- raw_vm->runtime = NULL;
return JNI_OK;
}
}
@@ -2177,12 +2524,15 @@ static const size_t kGlobalsMax = 51200; // Arbitrary sanity check.
static const size_t kWeakGlobalsInitial = 16; // Arbitrary.
static const size_t kWeakGlobalsMax = 51200; // Arbitrary sanity check.
-JavaVMExt::JavaVMExt(Runtime* runtime, bool check_jni)
+JavaVMExt::JavaVMExt(Runtime* runtime, bool check_jni, bool verbose_jni)
: fns(&gInvokeInterface),
runtime(runtime),
check_jni(check_jni),
+ verbose_jni(verbose_jni),
pin_table("pin table", kPinTableInitialSize, kPinTableMaxSize),
+ globals_lock("JNI global reference table"),
globals(kGlobalsInitial, kGlobalsMax, kGlobal),
+ weak_globals_lock("JNI weak global reference table"),
weak_globals(kWeakGlobalsInitial, kWeakGlobalsMax, kWeakGlobal) {
}