Move JNIHelp's implementation to C++.
Ouch.
Change-Id: I81c0457a95549f1bef7cc8d9ab23d6ca4475cdb5
diff --git a/Android.mk b/Android.mk
index 088de10..3b0b6dc 100644
--- a/Android.mk
+++ b/Android.mk
@@ -20,13 +20,13 @@
#
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/JNIHelp.c b/JNIHelp.c
deleted file mode 100644
index d3e5ffb..0000000
--- a/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/JNIHelp.cpp b/JNIHelp.cpp
new file mode 100644
index 0000000..79a4042
--- /dev/null
+++ b/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/Register.c b/Register.cpp
similarity index 92%
rename from Register.c
rename to Register.cpp
index 5d7d690..b6b1b1f 100644
--- a/Register.c
+++ b/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 @@
/*
* 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/include/nativehelper/JNIHelp.h b/include/nativehelper/JNIHelp.h
index 1b5ff0c..ff9cf86 100644
--- a/include/nativehelper/JNIHelp.h
+++ b/include/nativehelper/JNIHelp.h
@@ -37,17 +37,21 @@
/*
* 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 @@
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 @@
* 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