Merge "Finish off the new JNI implementation." into dalvik-dev
diff --git a/src/indirect_reference_table.cc b/src/indirect_reference_table.cc
index 2ea2434..76d5379 100644
--- a/src/indirect_reference_table.cc
+++ b/src/indirect_reference_table.cc
@@ -318,7 +318,7 @@
void IndirectReferenceTable::Dump() const {
LOG(WARNING) << kind_ << " table dump:";
- std::vector<Object*> entries(table_, table_ + Capacity());
+ std::vector<const Object*> entries(table_, table_ + Capacity());
ReferenceTable::Dump(entries);
}
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index d11f2a7..a95ff81 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -165,6 +165,10 @@
return self_;
}
+ JavaVMExt* Vm() {
+ return env_->vm;
+ }
+
private:
static Thread* ThreadForEnv(JNIEnv* env) {
// TODO: need replacement for gDvmJni.
@@ -240,7 +244,7 @@
if (obj == NULL) {
return NULL;
}
- JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+ JavaVMExt* vm = ts.Vm();
IndirectReferenceTable& weak_globals = vm->weak_globals;
MutexLock mu(vm->weak_globals_lock);
IndirectRef ref = weak_globals.Add(IRT_FIRST_SEGMENT, obj);
@@ -384,7 +388,7 @@
return InvokeWithArgArray(ts, receiver, method, arg_array.get());
}
-static Method* FindVirtualMethod(Object* receiver, Method* method) {
+Method* FindVirtualMethod(Object* receiver, Method* method) {
return receiver->GetClass()->GetMethodByVtableIndex(method->GetVtableIndex());
}
@@ -490,6 +494,18 @@
return reinterpret_cast<jfieldID>(fid);
}
+void PinPrimitiveArray(ScopedJniThreadState& ts, const Array* array) {
+ JavaVMExt* vm = ts.Vm();
+ MutexLock mu(vm->pins_lock);
+ vm->pin_table.Add(array);
+}
+
+void UnpinPrimitiveArray(ScopedJniThreadState& ts, const Array* array) {
+ JavaVMExt* vm = ts.Vm();
+ MutexLock mu(vm->pins_lock);
+ vm->pin_table.Remove(array);
+}
+
template<typename JniT, typename ArtT>
JniT NewPrimitiveArray(ScopedJniThreadState& ts, jsize length) {
CHECK_GE(length, 0); // TODO: ReportJniError
@@ -497,6 +513,24 @@
return AddLocalReference<JniT>(ts, result);
}
+template <typename ArrayT, typename CArrayT, typename ArtArrayT>
+CArrayT GetPrimitiveArray(ScopedJniThreadState& ts, ArrayT java_array, jboolean* is_copy) {
+ ArtArrayT* array = Decode<ArtArrayT*>(ts, java_array);
+ PinPrimitiveArray(ts, array);
+ if (is_copy != NULL) {
+ *is_copy = JNI_FALSE;
+ }
+ return array->GetData();
+}
+
+template <typename ArrayT>
+void ReleasePrimitiveArray(ScopedJniThreadState& ts, ArrayT java_array, jint mode) {
+ if (mode != JNI_COMMIT) {
+ Array* array = Decode<Array*>(ts, java_array);
+ UnpinPrimitiveArray(ts, array);
+ }
+}
+
void ThrowAIOOBE(ScopedJniThreadState& ts, Array* array, jsize start, jsize length, const char* identifier) {
std::string type(PrettyType(array));
ts.Self()->ThrowNewException("Ljava/lang/ArrayIndexOutOfBoundsException;",
@@ -509,7 +543,7 @@
}
template <typename JavaArrayT, typename JavaT, typename ArrayT>
-static void GetPrimitiveArrayRegion(ScopedJniThreadState& ts, JavaArrayT java_array, jsize start, jsize length, JavaT* buf) {
+void GetPrimitiveArrayRegion(ScopedJniThreadState& ts, JavaArrayT java_array, jsize start, jsize length, JavaT* buf) {
ArrayT* array = Decode<ArrayT*>(ts, java_array);
if (start < 0 || length < 0 || start + length > array->GetLength()) {
ThrowAIOOBE(ts, array, start, length, "src");
@@ -520,7 +554,7 @@
}
template <typename JavaArrayT, typename JavaT, typename ArrayT>
-static void SetPrimitiveArrayRegion(ScopedJniThreadState& ts, JavaArrayT java_array, jsize start, jsize length, const JavaT* buf) {
+void SetPrimitiveArrayRegion(ScopedJniThreadState& ts, JavaArrayT java_array, jsize start, jsize length, const JavaT* buf) {
ArrayT* array = Decode<ArrayT*>(ts, java_array);
if (start < 0 || length < 0 || start + length > array->GetLength()) {
ThrowAIOOBE(ts, array, start, length, "dst");
@@ -530,17 +564,45 @@
}
}
-static jclass InitDirectByteBufferClass(JNIEnv* env) {
+jclass InitDirectByteBufferClass(JNIEnv* env) {
ScopedLocalRef<jclass> buffer_class(env, env->FindClass("java/nio/ReadWriteDirectByteBuffer"));
CHECK(buffer_class.get() != NULL);
return reinterpret_cast<jclass>(env->NewGlobalRef(buffer_class.get()));
}
-static jclass GetDirectByteBufferClass(JNIEnv* env) {
+jclass GetDirectByteBufferClass(JNIEnv* env) {
static jclass buffer_class = InitDirectByteBufferClass(env);
return buffer_class;
}
+jint JII_AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args, bool as_daemon) {
+ if (vm == NULL || p_env == NULL) {
+ return JNI_ERR;
+ }
+
+ JavaVMAttachArgs* in_args = static_cast<JavaVMAttachArgs*>(thr_args);
+ JavaVMAttachArgs args;
+ if (thr_args == NULL) {
+ // Allow the v1.1 calling convention.
+ args.version = JNI_VERSION_1_2;
+ args.name = NULL;
+ args.group = NULL; // TODO: get "main" thread group
+ } else {
+ args.version = in_args->version;
+ args.name = in_args->name;
+ if (in_args->group != NULL) {
+ UNIMPLEMENTED(WARNING) << "thr_args->group != NULL";
+ args.group = NULL; // TODO: decode in_args->group
+ } else {
+ args.group = NULL; // TODO: get "main" thread group
+ }
+ }
+ CHECK_GE(args.version, JNI_VERSION_1_2);
+
+ Runtime* runtime = reinterpret_cast<JavaVMExt*>(vm)->runtime;
+ return runtime->AttachCurrentThread(args.name, p_env, as_daemon) ? JNI_OK : JNI_ERR;
+}
+
} // namespace
class JNI {
@@ -612,7 +674,7 @@
static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass clazz) {
ScopedJniThreadState ts(env);
- CHECK_NE(static_cast<jclass>(NULL), clazz);
+ CHECK_NE(static_cast<jclass>(NULL), clazz); // TODO: ReportJniError
if (jobj == NULL) {
// NB. JNI is different from regular Java instanceof in this respect
return JNI_TRUE;
@@ -744,7 +806,7 @@
return NULL;
}
- JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+ JavaVMExt* vm = ts.Vm();
IndirectReferenceTable& globals = vm->globals;
MutexLock mu(vm->globals_lock);
IndirectRef ref = globals.Add(IRT_FIRST_SEGMENT, Decode<Object*>(ts, obj));
@@ -757,7 +819,7 @@
return;
}
- JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+ JavaVMExt* vm = ts.Vm();
IndirectReferenceTable& globals = vm->globals;
MutexLock mu(vm->globals_lock);
@@ -778,7 +840,7 @@
return;
}
- JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+ JavaVMExt* vm = ts.Vm();
IndirectReferenceTable& weak_globals = vm->weak_globals;
MutexLock mu(vm->weak_globals_lock);
@@ -1737,37 +1799,56 @@
}
}
- static const jchar* GetStringChars(JNIEnv* env, jstring str, jboolean* isCopy) {
+ static const jchar* GetStringChars(JNIEnv* env, jstring java_string, jboolean* is_copy) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
- return NULL;
+ String* s = Decode<String*>(ts, java_string);
+ const CharArray* chars = s->GetCharArray();
+ PinPrimitiveArray(ts, chars);
+ if (is_copy != NULL) {
+ *is_copy = JNI_FALSE;
+ }
+ return chars->GetData() + s->GetOffset();
}
- static void ReleaseStringChars(JNIEnv* env, jstring str, const jchar* chars) {
+ static void ReleaseStringChars(JNIEnv* env, jstring java_string, const jchar* chars) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
+ UnpinPrimitiveArray(ts, Decode<String*>(ts, java_string)->GetCharArray());
}
- static const char* GetStringUTFChars(JNIEnv* env, jstring str, jboolean* isCopy) {
+ static const jchar* GetStringCritical(JNIEnv* env, jstring java_string, jboolean* is_copy) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
- return NULL;
+ return GetStringChars(env, java_string, is_copy);
}
- static void ReleaseStringUTFChars(JNIEnv* env, jstring str, const char* chars) {
+ static void ReleaseStringCritical(JNIEnv* env, jstring java_string, const jchar* chars) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
+ return ReleaseStringChars(env, java_string, chars);
}
- static const jchar* GetStringCritical(JNIEnv* env, jstring s, jboolean* isCopy) {
+ static const char* GetStringUTFChars(JNIEnv* env, jstring java_string, jboolean* is_copy) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
- return NULL;
+ if (java_string == NULL) {
+ return NULL;
+ }
+ if (is_copy != NULL) {
+ *is_copy = JNI_TRUE;
+ }
+ String* s = Decode<String*>(ts, java_string);
+ size_t byte_count = s->GetUtfLength();
+ char* bytes = new char[byte_count + 1];
+ if (bytes == NULL) {
+ UNIMPLEMENTED(WARNING) << "need to Throw OOME";
+ return NULL;
+ }
+ const uint16_t* chars = s->GetCharArray()->GetData() + s->GetOffset();
+ ConvertUtf16ToModifiedUtf8(bytes, chars, s->GetLength());
+ bytes[byte_count] = '\0';
+ return bytes;
}
- static void ReleaseStringCritical(JNIEnv* env, jstring s, const jchar* cstr) {
+ static void ReleaseStringUTFChars(JNIEnv* env, jstring, const char* chars) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
+ delete[] chars;
}
static jsize GetArrayLength(JNIEnv* env, jarray java_array) {
@@ -1838,15 +1919,20 @@
descriptor += element_class->GetDescriptor()->ToModifiedUtf8();
// Find the class.
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- // TODO: need to get the appropriate ClassLoader.
- Class* array_class = class_linker->FindClass(descriptor, NULL);
- if (array_class == NULL) {
+ ScopedLocalRef<jclass> java_array_class(env, FindClass(env, descriptor.c_str()));
+ if (java_array_class.get() == NULL) {
return NULL;
}
+ // Allocate and initialize if necessary.
+ Class* array_class = Decode<Class*>(ts, java_array_class.get());
ObjectArray<Object>* result = ObjectArray<Object>::Alloc(array_class, length);
- CHECK(initial_element == NULL); // TODO: support initial_element
+ if (initial_element != NULL) {
+ Object* initial_object = Decode<Object*>(ts, initial_element);
+ for (jsize i = 0; i < length; ++i) {
+ result->Set(i, initial_object);
+ }
+ }
return AddLocalReference<jobjectArray>(ts, result);
}
@@ -1855,115 +1941,94 @@
return NewPrimitiveArray<jshortArray, ShortArray>(ts, length);
}
- static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray array, jboolean* isCopy) {
+ static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray array, jboolean* is_copy) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
- return NULL;
+ return GetPrimitiveArray<jarray, jbyte*, ByteArray>(ts, array, is_copy);
}
- static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array, void* carray, jint mode) {
+ static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array, void* data, jint mode) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
+ ReleasePrimitiveArray(ts, array, mode);
}
- static jboolean* GetBooleanArrayElements(JNIEnv* env,
- jbooleanArray array, jboolean* isCopy) {
+ static jboolean* GetBooleanArrayElements(JNIEnv* env, jbooleanArray array, jboolean* is_copy) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
- return NULL;
+ return GetPrimitiveArray<jbooleanArray, jboolean*, BooleanArray>(ts, array, is_copy);
}
- static jbyte* GetByteArrayElements(JNIEnv* env, jbyteArray array, jboolean* isCopy) {
+ static jbyte* GetByteArrayElements(JNIEnv* env, jbyteArray array, jboolean* is_copy) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
- return NULL;
+ return GetPrimitiveArray<jbyteArray, jbyte*, ByteArray>(ts, array, is_copy);
}
- static jchar* GetCharArrayElements(JNIEnv* env, jcharArray array, jboolean* isCopy) {
+ static jchar* GetCharArrayElements(JNIEnv* env, jcharArray array, jboolean* is_copy) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
- return NULL;
+ return GetPrimitiveArray<jcharArray, jchar*, CharArray>(ts, array, is_copy);
}
- static jshort* GetShortArrayElements(JNIEnv* env,
- jshortArray array, jboolean* isCopy) {
+ static jdouble* GetDoubleArrayElements(JNIEnv* env, jdoubleArray array, jboolean* is_copy) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
- return NULL;
+ return GetPrimitiveArray<jdoubleArray, jdouble*, DoubleArray>(ts, array, is_copy);
}
- static jint* GetIntArrayElements(JNIEnv* env, jintArray array, jboolean* isCopy) {
+ static jfloat* GetFloatArrayElements(JNIEnv* env, jfloatArray array, jboolean* is_copy) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
- return NULL;
+ return GetPrimitiveArray<jfloatArray, jfloat*, FloatArray>(ts, array, is_copy);
}
- static jlong* GetLongArrayElements(JNIEnv* env, jlongArray array, jboolean* isCopy) {
+ static jint* GetIntArrayElements(JNIEnv* env, jintArray array, jboolean* is_copy) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
- return NULL;
+ return GetPrimitiveArray<jintArray, jint*, IntArray>(ts, array, is_copy);
}
- static jfloat* GetFloatArrayElements(JNIEnv* env,
- jfloatArray array, jboolean* isCopy) {
+ static jlong* GetLongArrayElements(JNIEnv* env, jlongArray array, jboolean* is_copy) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
- return NULL;
+ return GetPrimitiveArray<jlongArray, jlong*, LongArray>(ts, array, is_copy);
}
- static jdouble* GetDoubleArrayElements(JNIEnv* env,
- jdoubleArray array, jboolean* isCopy) {
+ static jshort* GetShortArrayElements(JNIEnv* env, jshortArray array, jboolean* is_copy) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
- return NULL;
+ return GetPrimitiveArray<jshortArray, jshort*, ShortArray>(ts, array, is_copy);
}
- static void ReleaseBooleanArrayElements(JNIEnv* env,
- jbooleanArray array, jboolean* elems, jint mode) {
+ static void ReleaseBooleanArrayElements(JNIEnv* env, jbooleanArray array, jboolean* data, jint mode) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
+ ReleasePrimitiveArray(ts, array, mode);
}
- static void ReleaseByteArrayElements(JNIEnv* env,
- jbyteArray array, jbyte* elems, jint mode) {
+ static void ReleaseByteArrayElements(JNIEnv* env, jbyteArray array, jbyte* data, jint mode) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
+ ReleasePrimitiveArray(ts, array, mode);
}
- static void ReleaseCharArrayElements(JNIEnv* env,
- jcharArray array, jchar* elems, jint mode) {
+ static void ReleaseCharArrayElements(JNIEnv* env, jcharArray array, jchar* data, jint mode) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
+ ReleasePrimitiveArray(ts, array, mode);
}
- static void ReleaseShortArrayElements(JNIEnv* env,
- jshortArray array, jshort* elems, jint mode) {
+ static void ReleaseDoubleArrayElements(JNIEnv* env, jdoubleArray array, jdouble* data, jint mode) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
+ ReleasePrimitiveArray(ts, array, mode);
}
- static void ReleaseIntArrayElements(JNIEnv* env,
- jintArray array, jint* elems, jint mode) {
+ static void ReleaseFloatArrayElements(JNIEnv* env, jfloatArray array, jfloat* data, jint mode) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
+ ReleasePrimitiveArray(ts, array, mode);
}
- static void ReleaseLongArrayElements(JNIEnv* env,
- jlongArray array, jlong* elems, jint mode) {
+ static void ReleaseIntArrayElements(JNIEnv* env, jintArray array, jint* data, jint mode) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
+ ReleasePrimitiveArray(ts, array, mode);
}
- static void ReleaseFloatArrayElements(JNIEnv* env,
- jfloatArray array, jfloat* elems, jint mode) {
+ static void ReleaseLongArrayElements(JNIEnv* env, jlongArray array, jlong* data, jint mode) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
+ ReleasePrimitiveArray(ts, array, mode);
}
- static void ReleaseDoubleArrayElements(JNIEnv* env,
- jdoubleArray array, jdouble* elems, jint mode) {
+ static void ReleaseShortArrayElements(JNIEnv* env, jshortArray array, jshort* data, jint mode) {
ScopedJniThreadState ts(env);
- UNIMPLEMENTED(FATAL);
+ ReleasePrimitiveArray(ts, array, mode);
}
static void GetBooleanArrayRegion(JNIEnv* env, jbooleanArray array, jsize start, jsize length, jboolean* buf) {
@@ -2050,8 +2115,6 @@
ScopedJniThreadState ts(env);
Class* c = Decode<Class*>(ts, java_class);
- JavaVMExt* vm = Runtime::Current()->GetJavaVM();
-
for (int i = 0; i < method_count; i++) {
const char* name = methods[i].name;
const char* sig = methods[i].signature;
@@ -2081,7 +2144,7 @@
return JNI_ERR;
}
- if (vm->verbose_jni) {
+ if (ts.Vm()->verbose_jni) {
LOG(INFO) << "[Registering JNI native method "
<< PrettyMethod(m, true) << "]";
}
@@ -2095,8 +2158,7 @@
ScopedJniThreadState ts(env);
Class* c = Decode<Class*>(ts, java_class);
- JavaVMExt* vm = Runtime::Current()->GetJavaVM();
- if (vm->verbose_jni) {
+ if (ts.Vm()->verbose_jni) {
LOG(INFO) << "[Unregistering JNI native methods for "
<< PrettyDescriptor(c->GetDescriptor()) << "]";
}
@@ -2144,8 +2206,8 @@
ScopedJniThreadState ts(env);
// The address may not be NULL, and the capacity must be > 0.
- CHECK(address != NULL);
- CHECK_GT(capacity, 0);
+ CHECK(address != NULL); // TODO: ReportJniError
+ CHECK_GT(capacity, 0); // TODO: ReportJniError
jclass buffer_class = GetDirectByteBufferClass(env);
jmethodID mid = env->GetMethodID(buffer_class, "<init>", "(II)V");
@@ -2178,7 +2240,7 @@
static jobjectRefType GetObjectRefType(JNIEnv* env, jobject java_object) {
ScopedJniThreadState ts(env);
- CHECK(java_object != NULL);
+ CHECK(java_object != NULL); // TODO: ReportJniError
// Do we definitely know what kind of reference this is?
IndirectRef ref = reinterpret_cast<IndirectRef>(java_object);
@@ -2452,9 +2514,10 @@
static const size_t kLocalsInitial = 64; // Arbitrary.
static const size_t kLocalsMax = 512; // Arbitrary sanity check.
-JNIEnvExt::JNIEnvExt(Thread* self, bool check_jni)
+JNIEnvExt::JNIEnvExt(Thread* self, JavaVMExt* vm)
: self(self),
- check_jni(check_jni),
+ vm(vm),
+ check_jni(vm->check_jni),
critical(false),
monitors("monitors", kMonitorsInitial, kMonitorsMax),
locals(kLocalsInitial, kLocalsMax, kLocal) {
@@ -2514,43 +2577,11 @@
}
static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
- if (vm == NULL || p_env == NULL) {
- return JNI_ERR;
- }
- JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
- Runtime* runtime = raw_vm->runtime;
- const char* name = NULL;
- if (thr_args != NULL) {
- // TODO: check version
- name = static_cast<JavaVMAttachArgs*>(thr_args)->name;
- // TODO: thread group
- }
- bool success = runtime->AttachCurrentThread(name, p_env);
- if (!success) {
- return JNI_ERR;
- } else {
- return JNI_OK;
- }
+ return JII_AttachCurrentThread(vm, p_env, thr_args, false);
}
static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
- if (vm == NULL || p_env == NULL) {
- return JNI_ERR;
- }
- JavaVMExt* raw_vm = reinterpret_cast<JavaVMExt*>(vm);
- Runtime* runtime = raw_vm->runtime;
- const char* name = NULL;
- if (thr_args != NULL) {
- // TODO: check version
- name = static_cast<JavaVMAttachArgs*>(thr_args)->name;
- // TODO: thread group
- }
- bool success = runtime->AttachCurrentThreadAsDaemon(name, p_env);
- if (!success) {
- return JNI_ERR;
- } else {
- return JNI_OK;
- }
+ return JII_AttachCurrentThread(vm, p_env, thr_args, true);
}
static jint DetachCurrentThread(JavaVM* vm) {
@@ -2605,6 +2636,7 @@
: runtime(runtime),
check_jni(check_jni),
verbose_jni(verbose_jni),
+ pins_lock(Mutex::Create("JNI pin table lock")),
pin_table("pin table", kPinTableInitialSize, kPinTableMaxSize),
globals_lock(Mutex::Create("JNI global reference table lock")),
globals(kGlobalsInitial, kGlobalsMax, kGlobal),
@@ -2614,39 +2646,30 @@
}
JavaVMExt::~JavaVMExt() {
+ delete pins_lock;
delete globals_lock;
delete weak_globals_lock;
}
/*
- * 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, ClassLoader* class_loader, char** detail) {
- *detail = NULL;
+bool JavaVMExt::LoadNativeLibrary(const std::string& path, ClassLoader* class_loader, std::string& detail) {
+ detail.clear();
// See if we've already loaded this library. If we have, and the class loader
// matches, return successfully without doing anything.
+ // TODO: for better results we should canonicalize the pathname (or even compare
+ // inodes). This implementation is fine if everybody is using System.loadLibrary.
SharedLibrary* library = libraries[path];
if (library != NULL) {
if (library->GetClassLoader() != class_loader) {
- LOG(WARNING) << "Shared library \"" << path << "\" already opened by "
- << "ClassLoader " << library->GetClassLoader() << "; "
- << "can't open in " << class_loader;
- *detail = strdup("already opened by different ClassLoader");
+ // The library will be associated with class_loader. The JNI
+ // spec says we can't load the same library into more than one
+ // class loader.
+ StringAppendF(&detail, "Shared library \"%s\" already opened by "
+ "ClassLoader %p; can't open in ClassLoader %p",
+ path.c_str(), library->GetClassLoader(), class_loader);
+ LOG(WARNING) << detail;
return false;
}
if (verbose_jni) {
@@ -2654,7 +2677,8 @@
<< "ClassLoader " << class_loader << "]";
}
if (!library->CheckOnLoadResult(this)) {
- *detail = strdup("JNI_OnLoad failed before");
+ StringAppendF(&detail, "JNI_OnLoad failed on a previous attempt "
+ "to load \"%s\"", path.c_str());
return false;
}
return true;
@@ -2698,7 +2722,7 @@
}
if (handle == NULL) {
- *detail = strdup(dlerror());
+ detail = dlerror();
return false;
}
diff --git a/src/jni_internal.h b/src/jni_internal.h
index 6d1ff96..2b3529c 100644
--- a/src/jni_internal.h
+++ b/src/jni_internal.h
@@ -24,24 +24,13 @@
JavaVMExt(Runtime* runtime, bool check_jni, bool verbose_jni);
~JavaVMExt();
- /*
- * 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.
+ /**
+ * Loads the given shared library. 'path' is an absolute pathname.
*
- * TODO: for better results we should canonicalize 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, returns false and 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.
+ * Returns 'true' on success. On failure, sets 'detail' to a
+ * human-readable description of the error.
*/
- bool LoadNativeLibrary(const std::string& path, ClassLoader* class_loader, char** detail);
+ bool LoadNativeLibrary(const std::string& path, ClassLoader* class_loader, std::string& detail);
Runtime* runtime;
@@ -49,6 +38,7 @@
bool verbose_jni;
// Used to hold references to pinned primitive arrays.
+ Mutex* pins_lock;
ReferenceTable pin_table;
// JNI global references.
@@ -63,9 +53,10 @@
};
struct JNIEnvExt : public JNIEnv {
- JNIEnvExt(Thread* self, bool check_jni);
+ JNIEnvExt(Thread* self, JavaVMExt* vm);
Thread* self;
+ JavaVMExt* vm;
bool check_jni;
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index 0964b62..ddc0408 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -279,7 +279,7 @@
env_->UnregisterNatives(jlobject);
}
-#define EXPECT_PRIMITIVE_ARRAY(new_fn, get_region_fn, set_region_fn, scalar_type, expected_class_descriptor) \
+#define EXPECT_PRIMITIVE_ARRAY(new_fn, get_region_fn, set_region_fn, get_elements_fn, release_elements_fn, scalar_type, expected_class_descriptor) \
jsize size = 4; \
/* Allocate an array and check it has the right type and length. */ \
scalar_type ## Array a = env_->new_fn(size); \
@@ -317,31 +317,40 @@
EXPECT_TRUE(memcmp(src_buf, dst_buf, sizeof(src_buf)) == 0) << "fixed copy not equal"; \
/* Copy back the whole array. */ \
env_->get_region_fn(a, 0, size, dst_buf); \
- EXPECT_TRUE(memcmp(src_buf, dst_buf, sizeof(src_buf)) == 0) << "full copy not equal"
+ EXPECT_TRUE(memcmp(src_buf, dst_buf, sizeof(src_buf)) == 0) << "full copy not equal"; \
+ /* GetPrimitiveArrayCritical */ \
+ void* v = env_->GetPrimitiveArrayCritical(a, NULL); \
+ EXPECT_TRUE(memcmp(src_buf, v, sizeof(src_buf)) == 0) << "GetPrimitiveArrayCritical not equal"; \
+ env_->ReleasePrimitiveArrayCritical(a, v, 0); \
+ /* GetXArrayElements */ \
+ scalar_type* xs = env_->get_elements_fn(a, NULL); \
+ EXPECT_TRUE(memcmp(src_buf, xs, sizeof(src_buf)) == 0) << # get_elements_fn " not equal"; \
+ env_->release_elements_fn(a, xs, 0); \
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(v), reinterpret_cast<uintptr_t>(xs))
TEST_F(JniInternalTest, BooleanArrays) {
- EXPECT_PRIMITIVE_ARRAY(NewBooleanArray, GetBooleanArrayRegion, SetBooleanArrayRegion, jboolean, "[Z");
+ EXPECT_PRIMITIVE_ARRAY(NewBooleanArray, GetBooleanArrayRegion, SetBooleanArrayRegion, GetBooleanArrayElements, ReleaseBooleanArrayElements, jboolean, "[Z");
}
TEST_F(JniInternalTest, ByteArrays) {
- EXPECT_PRIMITIVE_ARRAY(NewByteArray, GetByteArrayRegion, SetByteArrayRegion, jbyte, "[B");
+ EXPECT_PRIMITIVE_ARRAY(NewByteArray, GetByteArrayRegion, SetByteArrayRegion, GetByteArrayElements, ReleaseByteArrayElements, jbyte, "[B");
}
TEST_F(JniInternalTest, CharArrays) {
- EXPECT_PRIMITIVE_ARRAY(NewCharArray, GetCharArrayRegion, SetCharArrayRegion, jchar, "[C");
+ EXPECT_PRIMITIVE_ARRAY(NewCharArray, GetCharArrayRegion, SetCharArrayRegion, GetCharArrayElements, ReleaseCharArrayElements, jchar, "[C");
}
TEST_F(JniInternalTest, DoubleArrays) {
- EXPECT_PRIMITIVE_ARRAY(NewDoubleArray, GetDoubleArrayRegion, SetDoubleArrayRegion, jdouble, "[D");
+ EXPECT_PRIMITIVE_ARRAY(NewDoubleArray, GetDoubleArrayRegion, SetDoubleArrayRegion, GetDoubleArrayElements, ReleaseDoubleArrayElements, jdouble, "[D");
}
TEST_F(JniInternalTest, FloatArrays) {
- EXPECT_PRIMITIVE_ARRAY(NewFloatArray, GetFloatArrayRegion, SetFloatArrayRegion, jfloat, "[F");
+ EXPECT_PRIMITIVE_ARRAY(NewFloatArray, GetFloatArrayRegion, SetFloatArrayRegion, GetFloatArrayElements, ReleaseFloatArrayElements, jfloat, "[F");
}
TEST_F(JniInternalTest, IntArrays) {
- EXPECT_PRIMITIVE_ARRAY(NewIntArray, GetIntArrayRegion, SetIntArrayRegion, jint, "[I");
+ EXPECT_PRIMITIVE_ARRAY(NewIntArray, GetIntArrayRegion, SetIntArrayRegion, GetIntArrayElements, ReleaseIntArrayElements, jint, "[I");
}
TEST_F(JniInternalTest, LongArrays) {
- EXPECT_PRIMITIVE_ARRAY(NewLongArray, GetLongArrayRegion, SetLongArrayRegion, jlong, "[J");
+ EXPECT_PRIMITIVE_ARRAY(NewLongArray, GetLongArrayRegion, SetLongArrayRegion, GetLongArrayElements, ReleaseLongArrayElements, jlong, "[J");
}
TEST_F(JniInternalTest, ShortArrays) {
- EXPECT_PRIMITIVE_ARRAY(NewShortArray, GetShortArrayRegion, SetShortArrayRegion, jshort, "[S");
+ EXPECT_PRIMITIVE_ARRAY(NewShortArray, GetShortArrayRegion, SetShortArrayRegion, GetShortArrayElements, ReleaseShortArrayElements, jshort, "[S");
}
TEST_F(JniInternalTest, NewObjectArray) {
@@ -365,6 +374,15 @@
EXPECT_TRUE(a != NULL);
EXPECT_TRUE(env_->IsInstanceOf(a, array_class));
EXPECT_EQ(1, env_->GetArrayLength(a));
+ EXPECT_TRUE(env_->IsSameObject(env_->GetObjectArrayElement(a, 0), NULL));
+
+ jstring s = env_->NewStringUTF("poop");
+ a = env_->NewObjectArray(2, element_class, s);
+ EXPECT_TRUE(a != NULL);
+ EXPECT_TRUE(env_->IsInstanceOf(a, array_class));
+ EXPECT_EQ(2, env_->GetArrayLength(a));
+ EXPECT_TRUE(env_->IsSameObject(env_->GetObjectArrayElement(a, 0), s));
+ EXPECT_TRUE(env_->IsSameObject(env_->GetObjectArrayElement(a, 1), s));
}
TEST_F(JniInternalTest, GetArrayLength) {
@@ -495,6 +513,71 @@
EXPECT_EQ('x', bytes[3]);
}
+TEST_F(JniInternalTest, GetStringUTFChars_ReleaseStringUTFChars) {
+ EXPECT_TRUE(env_->GetStringUTFChars(NULL, NULL) == NULL);
+
+ jstring s = env_->NewStringUTF("hello");
+ ASSERT_TRUE(s != NULL);
+
+ const char* utf = env_->GetStringUTFChars(s, NULL);
+ EXPECT_STREQ("hello", utf);
+ env_->ReleaseStringUTFChars(s, utf);
+
+ jboolean is_copy = JNI_FALSE;
+ utf = env_->GetStringUTFChars(s, &is_copy);
+ EXPECT_EQ(JNI_TRUE, is_copy);
+ EXPECT_STREQ("hello", utf);
+ env_->ReleaseStringUTFChars(s, utf);
+}
+
+TEST_F(JniInternalTest, GetStringChars_ReleaseStringChars) {
+ jstring s = env_->NewStringUTF("hello");
+ ASSERT_TRUE(s != NULL);
+
+ jchar expected[] = { 'h', 'e', 'l', 'l', 'o' };
+ const jchar* chars = env_->GetStringChars(s, NULL);
+ EXPECT_EQ(expected[0], chars[0]);
+ EXPECT_EQ(expected[1], chars[1]);
+ EXPECT_EQ(expected[2], chars[2]);
+ EXPECT_EQ(expected[3], chars[3]);
+ EXPECT_EQ(expected[4], chars[4]);
+ env_->ReleaseStringChars(s, chars);
+
+ jboolean is_copy = JNI_FALSE;
+ chars = env_->GetStringChars(s, &is_copy);
+ EXPECT_EQ(JNI_FALSE, is_copy);
+ EXPECT_EQ(expected[0], chars[0]);
+ EXPECT_EQ(expected[1], chars[1]);
+ EXPECT_EQ(expected[2], chars[2]);
+ EXPECT_EQ(expected[3], chars[3]);
+ EXPECT_EQ(expected[4], chars[4]);
+ env_->ReleaseStringChars(s, chars);
+}
+
+TEST_F(JniInternalTest, GetStringCritical_ReleaseStringCritical) {
+ jstring s = env_->NewStringUTF("hello");
+ ASSERT_TRUE(s != NULL);
+
+ jchar expected[] = { 'h', 'e', 'l', 'l', 'o' };
+ const jchar* chars = env_->GetStringCritical(s, NULL);
+ EXPECT_EQ(expected[0], chars[0]);
+ EXPECT_EQ(expected[1], chars[1]);
+ EXPECT_EQ(expected[2], chars[2]);
+ EXPECT_EQ(expected[3], chars[3]);
+ EXPECT_EQ(expected[4], chars[4]);
+ env_->ReleaseStringCritical(s, chars);
+
+ jboolean is_copy = JNI_FALSE;
+ chars = env_->GetStringCritical(s, &is_copy);
+ EXPECT_EQ(JNI_FALSE, is_copy);
+ EXPECT_EQ(expected[0], chars[0]);
+ EXPECT_EQ(expected[1], chars[1]);
+ EXPECT_EQ(expected[2], chars[2]);
+ EXPECT_EQ(expected[3], chars[3]);
+ EXPECT_EQ(expected[4], chars[4]);
+ env_->ReleaseStringCritical(s, chars);
+}
+
TEST_F(JniInternalTest, GetObjectArrayElement_SetObjectArrayElement) {
jclass c = env_->FindClass("[Ljava/lang/Object;");
ASSERT_TRUE(c != NULL);
diff --git a/src/reference_table.cc b/src/reference_table.cc
index 0fc038a..b5c988a 100644
--- a/src/reference_table.cc
+++ b/src/reference_table.cc
@@ -29,7 +29,7 @@
entries_.reserve(initial_size);
}
-void ReferenceTable::Add(Object* obj) {
+void ReferenceTable::Add(const Object* obj) {
DCHECK(obj != NULL);
if (entries_.size() == max_size_) {
LOG(FATAL) << "ReferenceTable '" << name_ << "' "
@@ -38,7 +38,7 @@
entries_.push_back(obj);
}
-void ReferenceTable::Remove(Object* obj) {
+void ReferenceTable::Remove(const Object* obj) {
// We iterate backwards on the assumption that references are LIFO.
for (int i = entries_.size() - 1; i >= 0; --i) {
if (entries_[i] == obj) {
@@ -58,7 +58,7 @@
}
struct ObjectComparator {
- bool operator()(Object* obj1, Object* obj2){
+ bool operator()(const Object* obj1, const Object* obj2){
// Ensure null references and cleared jweaks appear at the end.
if (obj1 == NULL) {
return true;
@@ -132,7 +132,7 @@
Dump(entries_);
}
-void ReferenceTable::Dump(const std::vector<Object*>& entries) {
+void ReferenceTable::Dump(const std::vector<const Object*>& entries) {
if (entries.empty()) {
LOG(WARNING) << " (empty)";
return;
@@ -192,7 +192,7 @@
}
// Make a copy of the table and sort it.
- std::vector<Object*> sorted_entries(entries.begin(), entries.end());
+ std::vector<const Object*> sorted_entries(entries.begin(), entries.end());
std::sort(sorted_entries.begin(), sorted_entries.end(), ObjectComparator());
// Remove any uninteresting stuff from the list. The sort moved them all to the end.
@@ -211,8 +211,8 @@
size_t equiv = 0;
size_t identical = 0;
for (size_t idx = 1; idx < count; idx++) {
- Object* prev = sorted_entries[idx-1];
- Object* current = sorted_entries[idx];
+ const Object* prev = sorted_entries[idx-1];
+ const Object* current = sorted_entries[idx];
size_t elems = GetElementCount(prev);
if (current == prev) {
// Same reference, added more than once.
diff --git a/src/reference_table.h b/src/reference_table.h
index 2e4d954..15a2e11 100644
--- a/src/reference_table.h
+++ b/src/reference_table.h
@@ -34,20 +34,20 @@
public:
ReferenceTable(const char* name, size_t initial_size, size_t max_size);
- void Add(Object* obj);
+ void Add(const Object* obj);
- void Remove(Object* obj);
+ void Remove(const Object* obj);
size_t Size() const;
void Dump() const;
private:
- static void Dump(const std::vector<Object*>& entries);
+ static void Dump(const std::vector<const Object*>& entries);
friend class IndirectReferenceTable; // For Dump.
std::string name_;
- std::vector<Object*> entries_;
+ std::vector<const Object*> entries_;
size_t max_size_;
};
diff --git a/src/runtime.cc b/src/runtime.cc
index b009e22..ac0b90b 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -155,8 +155,8 @@
void LoadJniLibrary(JavaVMExt* vm, const char* name) {
// TODO: OS_SHARED_LIB_FORMAT_STR
std::string mapped_name(StringPrintf("lib%s.so", name));
- char* reason = NULL;
- if (!vm->LoadNativeLibrary(mapped_name, NULL, &reason)) {
+ std::string reason;
+ if (!vm->LoadNativeLibrary(mapped_name, NULL, reason)) {
LOG(FATAL) << "LoadNativeLibrary failed for \"" << mapped_name << "\": "
<< reason;
}
@@ -356,12 +356,10 @@
return true;
}
-bool Runtime::AttachCurrentThread(const char* name, JNIEnv** penv) {
- return Thread::Attach(instance_) != NULL;
-}
-
-bool Runtime::AttachCurrentThreadAsDaemon(const char* name, JNIEnv** penv) {
- // TODO: do something different for daemon threads.
+bool Runtime::AttachCurrentThread(const char* name, JNIEnv** penv, bool as_daemon) {
+ if (as_daemon) {
+ UNIMPLEMENTED(WARNING) << "TODO: do something different for daemon threads";
+ }
return Thread::Attach(instance_) != NULL;
}
diff --git a/src/runtime.h b/src/runtime.h
index 9879023..49657c2 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -71,8 +71,7 @@
static void Abort(const char* file, int line);
// Attaches the current native thread to the runtime.
- bool AttachCurrentThread(const char* name, JNIEnv** jni_env);
- bool AttachCurrentThreadAsDaemon(const char* name, JNIEnv** jni_env);
+ bool AttachCurrentThread(const char* name, JNIEnv** jni_env, bool as_daemon);
// Detaches the current native thread from the runtime.
bool DetachCurrentThread();
diff --git a/src/thread.cc b/src/thread.cc
index 477b149..0878b50 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -175,10 +175,7 @@
PLOG(FATAL) << "pthread_setspecific failed";
}
- JavaVMExt* vm = runtime->GetJavaVM();
- CHECK(vm != NULL);
- bool check_jni = vm->check_jni;
- thread->jni_env_ = new JNIEnvExt(thread, check_jni);
+ thread->jni_env_ = new JNIEnvExt(thread, runtime->GetJavaVM());
return thread;
}