diff options
| author | Elliott Hughes <enh@google.com> | 2011-04-22 19:22:54 -0700 |
|---|---|---|
| committer | Elliott Hughes <enh@google.com> | 2011-04-25 10:36:06 -0700 |
| commit | f06a4d168052054ee9f5ffa847011d944be2e570 (patch) | |
| tree | e0c8c6b70daac9922a1cf2a8ecf08d18fd79fbd0 /libnativehelper | |
| parent | 0ed5b9bf184b97b0d3fa2ef5a9f3dacd3e186e46 (diff) | |
| download | android_dalvik-f06a4d168052054ee9f5ffa847011d944be2e570.tar.gz android_dalvik-f06a4d168052054ee9f5ffa847011d944be2e570.tar.bz2 android_dalvik-f06a4d168052054ee9f5ffa847011d944be2e570.zip | |
Move JNIHelp's implementation to C++.
Ouch.
Change-Id: I81c0457a95549f1bef7cc8d9ab23d6ca4475cdb5
Diffstat (limited to 'libnativehelper')
| -rw-r--r-- | libnativehelper/Android.mk | 6 | ||||
| -rw-r--r-- | libnativehelper/JNIHelp.c | 369 | ||||
| -rw-r--r-- | libnativehelper/JNIHelp.cpp | 386 | ||||
| -rw-r--r-- | libnativehelper/Register.cpp (renamed from libnativehelper/Register.c) | 7 | ||||
| -rw-r--r-- | libnativehelper/include/nativehelper/JNIHelp.h | 64 |
5 files changed, 419 insertions, 413 deletions
diff --git a/libnativehelper/Android.mk b/libnativehelper/Android.mk index 088de1018..3b0b6dcac 100644 --- a/libnativehelper/Android.mk +++ b/libnativehelper/Android.mk @@ -20,13 +20,13 @@ LOCAL_PATH := $(call my-dir) # src_files := \ - JNIHelp.c \ - Register.c + JNIHelp.cpp \ + Register.cpp c_includes := \ $(JNI_H_INCLUDE) -# Any shared/static libs required by libjavacore +# Any shared/static libs required by libcore # need to be mentioned here as well. # TODO: fix this requirement diff --git a/libnativehelper/JNIHelp.c b/libnativehelper/JNIHelp.c deleted file mode 100644 index d3e5ffb38..000000000 --- a/libnativehelper/JNIHelp.c +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * JNI helper functions. - */ -#define LOG_TAG "JNIHelp" -#include "JNIHelp.h" -#include "utils/Log.h" - -#include <stdlib.h> -#include <string.h> -#include <assert.h> - -/* - * Register native JNI-callable methods. - * - * "className" looks like "java/lang/String". - */ -int jniRegisterNativeMethods(JNIEnv* env, const char* className, - const JNINativeMethod* gMethods, int numMethods) -{ - jclass clazz; - - LOGV("Registering %s natives\n", className); - clazz = (*env)->FindClass(env, className); - if (clazz == NULL) { - LOGE("Native registration unable to find class '%s', aborting\n", - className); - abort(); - } - - if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) { - LOGE("RegisterNatives failed for '%s', aborting\n", className); - abort(); - } - - (*env)->DeleteLocalRef(env, clazz); - return 0; -} - -/* - * Get a human-readable summary of an exception object. The buffer will - * be populated with the "binary" class name and, if present, the - * exception message. - */ -static char* getExceptionSummary(JNIEnv* env, jthrowable exception) { - char* result = NULL; - - /* get the name of the exception's class */ - jclass exceptionClazz = (*env)->GetObjectClass(env, exception); // can't fail - jclass classClazz = (*env)->GetObjectClass(env, exceptionClazz); // java.lang.Class, can't fail - jmethodID classGetNameMethod = (*env)->GetMethodID( - env, classClazz, "getName", "()Ljava/lang/String;"); - jstring classNameStr = (*env)->CallObjectMethod(env, exceptionClazz, classGetNameMethod); - if (classNameStr != NULL) { - /* get printable string */ - const char* classNameChars = (*env)->GetStringUTFChars(env, classNameStr, NULL); - if (classNameChars != NULL) { - /* if the exception has a message string, get that */ - jmethodID throwableGetMessageMethod = (*env)->GetMethodID( - env, exceptionClazz, "getMessage", "()Ljava/lang/String;"); - jstring messageStr = (*env)->CallObjectMethod( - env, exception, throwableGetMessageMethod); - - if (messageStr == NULL) { - result = strdup(classNameChars); - } else { - const char* messageChars = (*env)->GetStringUTFChars(env, messageStr, NULL); - if (messageChars != NULL) { - asprintf(&result, "%s: %s", classNameChars, messageChars); - (*env)->ReleaseStringUTFChars(env, messageStr, messageChars); - } else { - (*env)->ExceptionClear(env); // clear OOM - asprintf(&result, "%s: <error getting message>", classNameChars); - } - (*env)->DeleteLocalRef(env, messageStr); - } - - (*env)->ReleaseStringUTFChars(env, classNameStr, classNameChars); - } - (*env)->DeleteLocalRef(env, classNameStr); - } - (*env)->DeleteLocalRef(env, classClazz); - (*env)->DeleteLocalRef(env, exceptionClazz); - - if (result == NULL) { - (*env)->ExceptionClear(env); - result = strdup("<error getting class name>"); - } - - return result; -} - -/* - * Formats an exception as a string with its stack trace. - */ -static char* printStackTrace(JNIEnv* env, jthrowable exception) { - char* result = NULL; - - jclass stringWriterClazz = (*env)->FindClass(env, "java/io/StringWriter"); - if (stringWriterClazz != NULL) { - jmethodID stringWriterCtor = (*env)->GetMethodID(env, stringWriterClazz, "<init>", "()V"); - jmethodID stringWriterToStringMethod = (*env)->GetMethodID(env, stringWriterClazz, - "toString", "()Ljava/lang/String;"); - - jclass printWriterClazz = (*env)->FindClass(env, "java/io/PrintWriter"); - if (printWriterClazz != NULL) { - jmethodID printWriterCtor = (*env)->GetMethodID(env, printWriterClazz, - "<init>", "(Ljava/io/Writer;)V"); - - jobject stringWriterObj = (*env)->NewObject(env, stringWriterClazz, stringWriterCtor); - if (stringWriterObj != NULL) { - jobject printWriterObj = (*env)->NewObject(env, printWriterClazz, printWriterCtor, - stringWriterObj); - if (printWriterObj != NULL) { - jclass exceptionClazz = (*env)->GetObjectClass(env, exception); // can't fail - jmethodID printStackTraceMethod = (*env)->GetMethodID( - env, exceptionClazz, "printStackTrace", "(Ljava/io/PrintWriter;)V"); - - (*env)->CallVoidMethod(env, exception, printStackTraceMethod, printWriterObj); - if (! (*env)->ExceptionCheck(env)) { - jstring messageStr = (*env)->CallObjectMethod( - env, stringWriterObj, stringWriterToStringMethod); - if (messageStr != NULL) { - const char* utfChars = (*env)->GetStringUTFChars(env, messageStr, NULL); - if (utfChars != NULL) { - result = strdup(utfChars); - (*env)->ReleaseStringUTFChars(env, messageStr, utfChars); - } - (*env)->DeleteLocalRef(env, messageStr); - } - } - (*env)->DeleteLocalRef(env, exceptionClazz); - (*env)->DeleteLocalRef(env, printWriterObj); - } - (*env)->DeleteLocalRef(env, stringWriterObj); - } - (*env)->DeleteLocalRef(env, printWriterClazz); - } - (*env)->DeleteLocalRef(env, stringWriterClazz); - } - - if (result == NULL) { - (*env)->ExceptionClear(env); - result = getExceptionSummary(env, exception); - } - - return result; -} - -/* - * Throw an exception with the specified class and an optional message. - * - * If an exception is currently pending, we log a warning message and - * clear it. - * - * Returns 0 if the specified exception was successfully thrown. (Some - * sort of exception will always be pending when this returns.) - */ -int jniThrowException(JNIEnv* env, const char* className, const char* msg) { - jclass exceptionClass; - - if ((*env)->ExceptionCheck(env)) { - /* TODO: consider creating the new exception with this as "cause" */ - jthrowable exception = (*env)->ExceptionOccurred(env); - (*env)->ExceptionClear(env); - - if (exception != NULL) { - char* text = getExceptionSummary(env, exception); - LOGW("Discarding pending exception (%s) to throw %s", text, className); - free(text); - (*env)->DeleteLocalRef(env, exception); - } - } - - exceptionClass = (*env)->FindClass(env, className); - if (exceptionClass == NULL) { - LOGE("Unable to find exception class %s\n", className); - /* ClassNotFoundException now pending */ - return -1; - } - - int result = 0; - if ((*env)->ThrowNew(env, exceptionClass, msg) != JNI_OK) { - LOGE("Failed throwing '%s' '%s'\n", className, msg); - /* an exception, most likely OOM, will now be pending */ - result = -1; - } - - (*env)->DeleteLocalRef(env, exceptionClass); - return result; -} - -int jniThrowExceptionFmt(JNIEnv* env, const char* className, const char* fmt, va_list args) { - char msgBuf[512]; - vsnprintf(msgBuf, sizeof(msgBuf), fmt, args); - return jniThrowException(env, className, msgBuf); -} - -/* - * Throw a java.lang.NullPointerException, with an optional message. - */ -int jniThrowNullPointerException(JNIEnv* env, const char* msg) { - return jniThrowException(env, "java/lang/NullPointerException", msg); -} - -/* - * Throw a java.lang.RuntimeException, with an optional message. - */ -int jniThrowRuntimeException(JNIEnv* env, const char* msg) { - return jniThrowException(env, "java/lang/RuntimeException", msg); -} - -/* - * Throw a java.io.IOException, generating the message from errno. - */ -int jniThrowIOException(JNIEnv* env, int errnum) { - char buffer[80]; - const char* message = jniStrError(errnum, buffer, sizeof(buffer)); - return jniThrowException(env, "java/io/IOException", message); -} - -/* - * Log an exception. - * If exception is NULL, logs the current exception in the JNI environment, if any. - */ -void jniLogException(JNIEnv* env, int priority, const char* tag, jthrowable exception) { - int currentException = 0; - if (exception == NULL) { - exception = (*env)->ExceptionOccurred(env); - if (exception == NULL) { - return; - } - - (*env)->ExceptionClear(env); - currentException = 1; - } - - char* buffer = printStackTrace(env, exception); - __android_log_write(priority, tag, buffer); - free(buffer); - - if (currentException) { - (*env)->Throw(env, exception); // rethrow - (*env)->DeleteLocalRef(env, exception); - } -} - -const char* jniStrError(int errnum, char* buf, size_t buflen) { - // note: glibc has a nonstandard strerror_r that returns char* rather - // than POSIX's int. - // char *strerror_r(int errnum, char *buf, size_t n); - char* ret = (char*) strerror_r(errnum, buf, buflen); - if (((int)ret) == 0) { - //POSIX strerror_r, success - return buf; - } else if (((int)ret) == -1) { - //POSIX strerror_r, failure - // (Strictly, POSIX only guarantees a value other than 0. The safest - // way to implement this function is to use C++ and overload on the - // type of strerror_r to accurately distinguish GNU from POSIX. But - // realistic implementations will always return -1.) - snprintf(buf, buflen, "errno %d", errnum); - return buf; - } else { - //glibc strerror_r returning a string - return ret; - } -} - -static struct CachedFields { - jclass fileDescriptorClass; - jmethodID fileDescriptorCtor; - jfieldID descriptorField; -} gCachedFields; - -int registerJniHelp(JNIEnv* env) { - gCachedFields.fileDescriptorClass = - (*env)->NewGlobalRef(env, (*env)->FindClass(env, "java/io/FileDescriptor")); - if (gCachedFields.fileDescriptorClass == NULL) { - return -1; - } - - gCachedFields.fileDescriptorCtor = - (*env)->GetMethodID(env, gCachedFields.fileDescriptorClass, "<init>", "()V"); - if (gCachedFields.fileDescriptorCtor == NULL) { - return -1; - } - - gCachedFields.descriptorField = - (*env)->GetFieldID(env, gCachedFields.fileDescriptorClass, "descriptor", "I"); - if (gCachedFields.descriptorField == NULL) { - return -1; - } - - return 0; -} - -/* - * Create a java.io.FileDescriptor given an integer fd - */ -jobject jniCreateFileDescriptor(JNIEnv* env, int fd) { - jobject fileDescriptor = (*env)->NewObject(env, - gCachedFields.fileDescriptorClass, gCachedFields.fileDescriptorCtor); - jniSetFileDescriptorOfFD(env, fileDescriptor, fd); - return fileDescriptor; -} - -/* - * Get an int file descriptor from a java.io.FileDescriptor - */ -int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) { - return (*env)->GetIntField(env, fileDescriptor, gCachedFields.descriptorField); -} - -/* - * Set the descriptor of a java.io.FileDescriptor - */ -void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor, int value) { - (*env)->SetIntField(env, fileDescriptor, gCachedFields.descriptorField, value); -} - -/* - * DO NOT USE THIS FUNCTION - * - * Get a pointer to the elements of a non-movable array. - * - * The semantics are similar to GetDirectBufferAddress. Specifically, the VM - * guarantees that the array will not move, and the caller must ensure that - * it does not continue to use the pointer after the object is collected. - * - * We currently use an illegal sequence that trips up CheckJNI when - * the "forcecopy" mode is enabled. We pass in a magic value to work - * around the problem. - * - * Returns NULL if the array is movable. - */ -jbyte* jniGetNonMovableArrayElements(JNIEnv* env, jarray arrayObj) { -#define kNoCopyMagic 0xd5aab57f /* also in CheckJni.c */ - - /* - * Normally the "isCopy" parameter is for a return value only, so the - * non-CheckJNI VM will ignore whatever we pass in. - */ - uint32_t noCopy = kNoCopyMagic; - jbyte *addr = (*env)->GetByteArrayElements(env, arrayObj, - (jboolean*)&noCopy); - - /* - * The non-CheckJNI implementation only cares about the array object, - * so we can replace the element pointer with the magic value. - */ - (*env)->ReleaseByteArrayElements(env, arrayObj, (jbyte*) kNoCopyMagic, 0); - return addr; -} diff --git a/libnativehelper/JNIHelp.cpp b/libnativehelper/JNIHelp.cpp new file mode 100644 index 000000000..79a4042f5 --- /dev/null +++ b/libnativehelper/JNIHelp.cpp @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "JNIHelp" + +#include "JNIHelp.h" +#include "utils/Log.h" + +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +/** + * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.) + */ +template<typename T> +class scoped_local_ref { +public: + scoped_local_ref(C_JNIEnv* env, T localRef = NULL) + : mEnv(env), mLocalRef(localRef) + { + } + + ~scoped_local_ref() { + reset(); + } + + void reset(T localRef = NULL) { + if (mLocalRef != NULL) { + (*mEnv)->DeleteLocalRef(reinterpret_cast<JNIEnv*>(mEnv), mLocalRef); + mLocalRef = localRef; + } + } + + T get() const { + return mLocalRef; + } + +private: + C_JNIEnv* mEnv; + T mLocalRef; + + // Disallow copy and assignment. + scoped_local_ref(const scoped_local_ref&); + void operator=(const scoped_local_ref&); +}; + +static jclass findClass(C_JNIEnv* env, const char* className) { + JNIEnv* e = reinterpret_cast<JNIEnv*>(env); + return (*env)->FindClass(e, className); +} + +extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, + const JNINativeMethod* gMethods, int numMethods) +{ + JNIEnv* e = reinterpret_cast<JNIEnv*>(env); + + LOGV("Registering %s natives", className); + + scoped_local_ref<jclass> c(env, findClass(env, className)); + if (c.get() == NULL) { + LOGE("Native registration unable to find class '%s', aborting", className); + abort(); + } + + if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) { + LOGE("RegisterNatives failed for '%s', aborting", className); + abort(); + } + + return 0; +} + +/* + * Returns a human-readable summary of an exception object. The buffer will + * be populated with the "binary" class name and, if present, the + * exception message. + */ +static char* getExceptionSummary0(C_JNIEnv* env, jthrowable exception) { + JNIEnv* e = reinterpret_cast<JNIEnv*>(env); + + /* get the name of the exception's class */ + scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail + scoped_local_ref<jclass> classClass(env, + (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail + jmethodID classGetNameMethod = + (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;"); + scoped_local_ref<jstring> classNameStr(env, + (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod)); + if (classNameStr.get() == NULL) { + return NULL; + } + + /* get printable string */ + const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL); + if (classNameChars == NULL) { + return NULL; + } + + /* if the exception has a detail message, get that */ + jmethodID getMessage = + (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;"); + scoped_local_ref<jstring> messageStr(env, + (jstring) (*env)->CallObjectMethod(e, exception, getMessage)); + if (messageStr.get() == NULL) { + return strdup(classNameChars); + } + + char* result = NULL; + const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL); + if (messageChars != NULL) { + asprintf(&result, "%s: %s", classNameChars, messageChars); + (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars); + } else { + (*env)->ExceptionClear(e); // clear OOM + asprintf(&result, "%s: <error getting message>", classNameChars); + } + + (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars); + return result; +} + +static char* getExceptionSummary(C_JNIEnv* env, jthrowable exception) { + JNIEnv* e = reinterpret_cast<JNIEnv*>(env); + char* result = getExceptionSummary0(env, exception); + if (result == NULL) { + (*env)->ExceptionClear(e); + result = strdup("<error getting class name>"); + } + return result; +} + +/* + * Returns an exception (with stack trace) as a string. + */ +static char* getStackTrace(C_JNIEnv* env, jthrowable exception) { + JNIEnv* e = reinterpret_cast<JNIEnv*>(env); + + scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter")); + if (stringWriterClass.get() == NULL) { + return NULL; + } + + jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V"); + jmethodID stringWriterToStringMethod = + (*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;"); + + scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter")); + if (printWriterClass.get() == NULL) { + return NULL; + } + + jmethodID printWriterCtor = + (*env)->GetMethodID(e, printWriterClass.get(), "<init>", "(Ljava/io/Writer;)V"); + + scoped_local_ref<jobject> stringWriter(env, + (*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor)); + if (stringWriter.get() == NULL) { + return NULL; + } + + jobject printWriter = + (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get()); + if (printWriter == NULL) { + return NULL; + } + + scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail + jmethodID printStackTraceMethod = + (*env)->GetMethodID(e, exceptionClass.get(), "printStackTrace", "(Ljava/io/PrintWriter;)V"); + (*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter); + + if ((*env)->ExceptionCheck(e)) { + return NULL; + } + + scoped_local_ref<jstring> messageStr(env, + (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod)); + if (messageStr.get() == NULL) { + return NULL; + } + + const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL); + if (utfChars == NULL) { + return NULL; + } + + char* result = strdup(utfChars); + (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars); + return result; +} + +extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) { + JNIEnv* e = reinterpret_cast<JNIEnv*>(env); + + if ((*env)->ExceptionCheck(e)) { + /* TODO: consider creating the new exception with this as "cause" */ + scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e)); + (*env)->ExceptionClear(e); + + if (exception.get() != NULL) { + char* text = getExceptionSummary(env, exception.get()); + LOGW("Discarding pending exception (%s) to throw %s", text, className); + free(text); + } + } + + scoped_local_ref<jclass> exceptionClass(env, findClass(env, className)); + if (exceptionClass.get() == NULL) { + LOGE("Unable to find exception class %s", className); + /* ClassNotFoundException now pending */ + return -1; + } + + if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) { + LOGE("Failed throwing '%s' '%s'", className, msg); + /* an exception, most likely OOM, will now be pending */ + return -1; + } + + return 0; +} + +int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) { + char msgBuf[512]; + vsnprintf(msgBuf, sizeof(msgBuf), fmt, args); + return jniThrowException(env, className, msgBuf); +} + +int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) { + return jniThrowException(env, "java/lang/NullPointerException", msg); +} + +int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) { + return jniThrowException(env, "java/lang/RuntimeException", msg); +} + +int jniThrowIOException(C_JNIEnv* env, int errnum) { + char buffer[80]; + const char* message = jniStrError(errnum, buffer, sizeof(buffer)); + return jniThrowException(env, "java/io/IOException", message); +} + +void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) { + JNIEnv* e = reinterpret_cast<JNIEnv*>(env); + + scoped_local_ref<jthrowable> currentException(env); + if (exception == NULL) { + exception = (*env)->ExceptionOccurred(e); + if (exception == NULL) { + return; + } + + (*env)->ExceptionClear(e); + currentException.reset(exception); + } + + char* buffer = getStackTrace(env, exception); + if (buffer == NULL) { + (*env)->ExceptionClear(e); + buffer = getExceptionSummary(env, exception); + } + + __android_log_write(priority, tag, buffer); + free(buffer); + + if (currentException.get() != NULL) { + (*env)->Throw(e, exception); // rethrow + } +} + +const char* jniStrError(int errnum, char* buf, size_t buflen) { + // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int. + // char *strerror_r(int errnum, char *buf, size_t n); + char* ret = (char*) strerror_r(errnum, buf, buflen); + if (((int)ret) == 0) { + // POSIX strerror_r, success + return buf; + } else if (((int)ret) == -1) { + // POSIX strerror_r, failure + // (Strictly, POSIX only guarantees a value other than 0. The safest + // way to implement this function is to use C++ and overload on the + // type of strerror_r to accurately distinguish GNU from POSIX. But + // realistic implementations will always return -1.) + snprintf(buf, buflen, "errno %d", errnum); + return buf; + } else { + // glibc strerror_r returning a string + return ret; + } +} + +static struct CachedFields { + jclass fileDescriptorClass; + jmethodID fileDescriptorCtor; + jfieldID descriptorField; +} gCachedFields; + +int registerJniHelp(JNIEnv* env) { + gCachedFields.fileDescriptorClass = + reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass("java/io/FileDescriptor"))); + if (gCachedFields.fileDescriptorClass == NULL) { + return -1; + } + + gCachedFields.fileDescriptorCtor = + env->GetMethodID(gCachedFields.fileDescriptorClass, "<init>", "()V"); + if (gCachedFields.fileDescriptorCtor == NULL) { + return -1; + } + + gCachedFields.descriptorField = + env->GetFieldID(gCachedFields.fileDescriptorClass, "descriptor", "I"); + if (gCachedFields.descriptorField == NULL) { + return -1; + } + + return 0; +} + +jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) { + JNIEnv* e = reinterpret_cast<JNIEnv*>(env); + jobject fileDescriptor = (*env)->NewObject(e, + gCachedFields.fileDescriptorClass, gCachedFields.fileDescriptorCtor); + jniSetFileDescriptorOfFD(env, fileDescriptor, fd); + return fileDescriptor; +} + +int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) { + JNIEnv* e = reinterpret_cast<JNIEnv*>(env); + return (*env)->GetIntField(e, fileDescriptor, gCachedFields.descriptorField); +} + +void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) { + JNIEnv* e = reinterpret_cast<JNIEnv*>(env); + (*env)->SetIntField(e, fileDescriptor, gCachedFields.descriptorField, value); +} + +/* + * DO NOT USE THIS FUNCTION + * + * Get a pointer to the elements of a non-movable array. + * + * The semantics are similar to GetDirectBufferAddress. Specifically, the VM + * guarantees that the array will not move, and the caller must ensure that + * it does not continue to use the pointer after the object is collected. + * + * We currently use an illegal sequence that trips up CheckJNI when + * the "forcecopy" mode is enabled. We pass in a magic value to work + * around the problem. + * + * Returns NULL if the array is movable. + */ +#define kNoCopyMagic 0xd5aab57f /* also in CheckJni.c */ +extern "C" jbyte* jniGetNonMovableArrayElements(C_JNIEnv* env, jarray arrayObj) { + JNIEnv* e = reinterpret_cast<JNIEnv*>(env); + + jbyteArray byteArray = reinterpret_cast<jbyteArray>(arrayObj); + + /* + * Normally the "isCopy" parameter is for a return value only, so the + * non-CheckJNI VM will ignore whatever we pass in. + */ + uint32_t noCopy = kNoCopyMagic; + jbyte* result = (*env)->GetByteArrayElements(e, byteArray, reinterpret_cast<jboolean*>(&noCopy)); + + /* + * The non-CheckJNI implementation only cares about the array object, + * so we can replace the element pointer with the magic value. + */ + (*env)->ReleaseByteArrayElements(e, byteArray, reinterpret_cast<jbyte*>(kNoCopyMagic), 0); + return result; +} diff --git a/libnativehelper/Register.c b/libnativehelper/Register.cpp index 5d7d690ee..b6b1b1fe4 100644 --- a/libnativehelper/Register.c +++ b/libnativehelper/Register.cpp @@ -14,10 +14,6 @@ * limitations under the License. */ -/* - * JNI helper functions. - */ - #include "jni.h" extern int registerCoreLibrariesJni(JNIEnv* env); @@ -26,8 +22,7 @@ extern int registerJniHelp(JNIEnv* env); /* * Register all methods for system classes. */ -int jniRegisterSystemMethods(JNIEnv* env) -{ +int jniRegisterSystemMethods(JNIEnv* env) { // JniHelp depends on core library classes such as java.io.FileDescriptor. return registerCoreLibrariesJni(env) != -1 && registerJniHelp(env) != -1; } diff --git a/libnativehelper/include/nativehelper/JNIHelp.h b/libnativehelper/include/nativehelper/JNIHelp.h index 1b5ff0c2e..ff9cf862d 100644 --- a/libnativehelper/include/nativehelper/JNIHelp.h +++ b/libnativehelper/include/nativehelper/JNIHelp.h @@ -37,17 +37,21 @@ extern "C" { /* * Register one or more native methods with a particular class. + * "className" looks like "java/lang/String". */ -int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, - const JNINativeMethod* gMethods, int numMethods); +int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods); /* * Throw an exception with the specified class and an optional message. + * * The "className" argument will be passed directly to FindClass, which * takes strings with slashes (e.g. "java/lang/Object"). * + * If an exception is currently pending, we log a warning message and + * clear it. + * * Returns 0 on success, nonzero if something failed (e.g. the exception - * class couldn't be found). + * class couldn't be found, so *an* exception will still be pending). * * Currently aborts the VM if it can't throw the exception. */ @@ -77,17 +81,17 @@ int jniThrowIOException(C_JNIEnv* env, int errnum); const char* jniStrError(int errnum, char* buf, size_t buflen); /* - * Create a java.io.FileDescriptor given an integer fd + * Returns a new java.io.FileDescriptor for the given int fd. */ jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd); /* - * Get an int file descriptor from a java.io.FileDescriptor + * Returns the int fd from a java.io.FileDescriptor. */ int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor); /* - * Set an int file descriptor to a java.io.FileDescriptor + * Sets the int fd in a java.io.FileDescriptor. */ void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value); @@ -107,62 +111,52 @@ void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable ex * inlines these, even on non-optimized builds. */ #if defined(__cplusplus) -inline int jniRegisterNativeMethods(JNIEnv* env, const char* className, - const JNINativeMethod* gMethods, int numMethods) -{ - return jniRegisterNativeMethods(&env->functions, className, gMethods, - numMethods); +inline int jniRegisterNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods) { + return jniRegisterNativeMethods(&env->functions, className, gMethods, numMethods); } -inline int jniThrowException(JNIEnv* env, const char* className, - const char* msg) -{ + +inline int jniThrowException(JNIEnv* env, const char* className, const char* msg) { return jniThrowException(&env->functions, className, msg); } -extern "C" int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, - const char* fmt, va_list args); +extern "C" int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args); /* * Equivalent to jniThrowException but with a printf-like format string and * variable-length argument list. This is only available in C++. */ -inline int jniThrowExceptionFmt(JNIEnv* env, const char* className, - const char* fmt, ...) -{ +inline int jniThrowExceptionFmt(JNIEnv* env, const char* className, const char* fmt, ...) { va_list args; va_start(args, fmt); return jniThrowExceptionFmt(&env->functions, className, fmt, args); va_end(args); } -inline int jniThrowNullPointerException(JNIEnv* env, const char* msg) -{ +inline int jniThrowNullPointerException(JNIEnv* env, const char* msg) { return jniThrowNullPointerException(&env->functions, msg); } -inline int jniThrowRuntimeException(JNIEnv* env, const char* msg) -{ + +inline int jniThrowRuntimeException(JNIEnv* env, const char* msg) { return jniThrowRuntimeException(&env->functions, msg); } -inline int jniThrowIOException(JNIEnv* env, int errnum) -{ + +inline int jniThrowIOException(JNIEnv* env, int errnum) { return jniThrowIOException(&env->functions, errnum); } -inline jobject jniCreateFileDescriptor(JNIEnv* env, int fd) -{ + +inline jobject jniCreateFileDescriptor(JNIEnv* env, int fd) { return jniCreateFileDescriptor(&env->functions, fd); } -inline int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) -{ + +inline int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) { return jniGetFDFromFileDescriptor(&env->functions, fileDescriptor); } -inline void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor, - int value) -{ + +inline void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor, int value) { jniSetFileDescriptorOfFD(&env->functions, fileDescriptor, value); } -inline void jniLogException(JNIEnv* env, int priority, const char* tag, - jthrowable exception = NULL) -{ + +inline void jniLogException(JNIEnv* env, int priority, const char* tag, jthrowable exception = NULL) { jniLogException(&env->functions, priority, tag, exception); } #endif |
