diff options
Diffstat (limited to 'src/jni_internal.cc')
| -rw-r--r-- | src/jni_internal.cc | 504 |
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) { } |