ALog-priv.h0100644 0000000 0000000 00000003614 13756723631 011577 0ustar000000000 0000000 /* * Copyright 2013 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. */ #ifndef NATIVEHELPER_ALOGPRIV_H_ #define NATIVEHELPER_ALOGPRIV_H_ #include #ifndef LOG_NDEBUG #ifdef NDEBUG #define LOG_NDEBUG 1 #else #define LOG_NDEBUG 0 #endif #endif /* * Basic log message macros intended to emulate the behavior of log/log.h * in system core. This should be dependent only on ndk exposed logging * functionality. */ #ifndef ALOG #define ALOG(priority, tag, fmt...) \ __android_log_print(ANDROID_##priority, tag, fmt) #endif #ifndef ALOGV #if LOG_NDEBUG #define ALOGV(...) ((void)0) #else #define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) #endif #endif #ifndef ALOGD #define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) #endif #ifndef ALOGI #define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) #endif #ifndef ALOGW #define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) #endif #ifndef ALOGE #define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) #endif /* * Log a fatal error if cond is true. The condition test is inverted from * assert(3) semantics. The test and message are not stripped from release * builds */ #ifndef ALOG_ALWAYS_FATAL_IF #define ALOG_ALWAYS_FATAL_IF(cond, ...) \ if (cond) __android_log_assert(#cond, LOG_TAG, __VA_ARGS__) #endif #endif // NATIVEHELPER_ALOGPRIV_H_ Android.bp0100644 0000000 0000000 00000005177 13756723631 011537 0ustar000000000 0000000 // Copyright (C) 2009 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. cc_library_headers { name: "jni_headers", host_supported: true, export_include_dirs: ["include_jni"], vendor_available: true, target: { windows: { enabled: true, }, }, } cc_library_headers { name: "libnativehelper_header_only", host_supported: true, export_include_dirs: ["header_only_include"], target: { windows: { enabled: true, }, }, } cc_library_headers { name: "jni_platform_headers", host_supported: true, export_include_dirs: ["platform_include"], target: { windows: { enabled: true, }, }, } cc_library { name: "libnativehelper", host_supported: true, srcs: [ "JNIHelp.cpp", "JniConstants.cpp", "JniInvocation.cpp", "toStringArray.cpp", ], shared_libs: [ "libbase", "liblog", ], cflags: [ "-Werror", "-fvisibility=protected", ], export_include_dirs: [ "include", "header_only_include", "platform_include" ], stubs: { symbol_file: "libnativehelper.map.txt", versions: ["1"], }, target: { windows: { enabled: true, }, }, } // // NDK-only build for the target (device), using libc++. // - Relies only on NDK exposed functionality. // - This doesn't include JniInvocation. // cc_library_shared { name: "libnativehelper_compat_libc++", export_include_dirs: [ "header_only_include", "include", ], cflags: ["-Werror"], include_dirs: [ "libnativehelper/header_only_include", "libnativehelper/platform_include", ], srcs: [ "JNIHelp.cpp", "JniConstants.cpp", "toStringArray.cpp", ], shared_libs: [ "liblog", ], sdk_version: "19", stl: "c++_static", } ndk_headers { name: "ndk_jni.h", from: "include_jni", to: "", srcs: ["include_jni/jni.h"], license: "NOTICE", } // // Tests. // subdirs = ["tests"] JNIHelp.cpp0100644 0000000 0000000 00000034750 13756723631 011570 0ustar000000000 0000000 /* * 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 "nativehelper/JNIHelp.h" #include "ALog-priv.h" #include #include "JniConstants.h" #include "nativehelper/ScopedLocalRef.h" /** * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.) */ template class scoped_local_ref { public: explicit 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(mEnv), mLocalRef); mLocalRef = localRef; } } T get() const { return mLocalRef; } private: C_JNIEnv* const mEnv; T mLocalRef; DISALLOW_COPY_AND_ASSIGN(scoped_local_ref); }; static jclass findClass(C_JNIEnv* env, const char* className) { JNIEnv* e = reinterpret_cast(env); return (*env)->FindClass(e, className); } MODULE_API int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods) { JNIEnv* e = reinterpret_cast(env); ALOGV("Registering %s's %d native methods...", className, numMethods); scoped_local_ref c(env, findClass(env, className)); ALOG_ALWAYS_FATAL_IF(c.get() == NULL, "Native registration unable to find class '%s'; aborting...", className); int result = e->RegisterNatives(c.get(), gMethods, numMethods); ALOG_ALWAYS_FATAL_IF(result < 0, "RegisterNatives failed for '%s'; aborting...", className); 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 bool getExceptionSummary(C_JNIEnv* env, jthrowable exception, std::string& result) { JNIEnv* e = reinterpret_cast(env); /* get the name of the exception's class */ scoped_local_ref exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail scoped_local_ref 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 classNameStr(env, (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod)); if (classNameStr.get() == NULL) { (*env)->ExceptionClear(e); result = ""; return false; } const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL); if (classNameChars == NULL) { (*env)->ExceptionClear(e); result = ""; return false; } result += classNameChars; (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars); /* if the exception has a detail message, get that */ jmethodID getMessage = (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;"); scoped_local_ref messageStr(env, (jstring) (*env)->CallObjectMethod(e, exception, getMessage)); if (messageStr.get() == NULL) { return true; } result += ": "; const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL); if (messageChars != NULL) { result += messageChars; (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars); } else { result += ""; (*env)->ExceptionClear(e); // clear OOM } return true; } /* * Returns an exception (with stack trace) as a string. */ static bool getStackTrace(C_JNIEnv* env, jthrowable exception, std::string& result) { JNIEnv* e = reinterpret_cast(env); scoped_local_ref stringWriterClass(env, findClass(env, "java/io/StringWriter")); if (stringWriterClass.get() == NULL) { return false; } jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "", "()V"); jmethodID stringWriterToStringMethod = (*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;"); scoped_local_ref printWriterClass(env, findClass(env, "java/io/PrintWriter")); if (printWriterClass.get() == NULL) { return false; } jmethodID printWriterCtor = (*env)->GetMethodID(e, printWriterClass.get(), "", "(Ljava/io/Writer;)V"); scoped_local_ref stringWriter(env, (*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor)); if (stringWriter.get() == NULL) { return false; } scoped_local_ref printWriter(env, (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get())); if (printWriter.get() == NULL) { return false; } scoped_local_ref 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.get()); if ((*env)->ExceptionCheck(e)) { return false; } scoped_local_ref messageStr(env, (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod)); if (messageStr.get() == NULL) { return false; } const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL); if (utfChars == NULL) { return false; } result = utfChars; (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars); return true; } MODULE_API int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) { JNIEnv* e = reinterpret_cast(env); if ((*env)->ExceptionCheck(e)) { /* TODO: consider creating the new exception with this as "cause" */ scoped_local_ref exception(env, (*env)->ExceptionOccurred(e)); (*env)->ExceptionClear(e); if (exception.get() != NULL) { std::string text; getExceptionSummary(env, exception.get(), text); ALOGW("Discarding pending exception (%s) to throw %s", text.c_str(), className); } } scoped_local_ref exceptionClass(env, findClass(env, className)); if (exceptionClass.get() == NULL) { ALOGE("Unable to find exception class %s", className); /* ClassNotFoundException now pending */ return -1; } if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) { ALOGE("Failed throwing '%s' '%s'", className, msg); /* an exception, most likely OOM, will now be pending */ return -1; } return 0; } MODULE_API 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); } MODULE_API int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) { return jniThrowException(env, "java/lang/NullPointerException", msg); } MODULE_API int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) { return jniThrowException(env, "java/lang/RuntimeException", msg); } MODULE_API 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); } static std::string jniGetStackTrace(C_JNIEnv* env, jthrowable exception) { JNIEnv* e = reinterpret_cast(env); scoped_local_ref currentException(env, (*env)->ExceptionOccurred(e)); if (exception == NULL) { exception = currentException.get(); if (exception == NULL) { return ""; } } if (currentException.get() != NULL) { (*env)->ExceptionClear(e); } std::string trace; if (!getStackTrace(env, exception, trace)) { (*env)->ExceptionClear(e); getExceptionSummary(env, exception, trace); } if (currentException.get() != NULL) { (*env)->Throw(e, currentException.get()); // rethrow } return trace; } MODULE_API void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) { std::string trace(jniGetStackTrace(env, exception)); __android_log_write(priority, tag, trace.c_str()); } // 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); // // Some versions of bionic support the glibc style call. Since the set of defines that determine // which version is used is byzantine in its complexity we will just use this C++ template hack to // select the correct jniStrError implementation based on the libc being used. namespace impl { using GNUStrError = char* (*)(int,char*,size_t); using POSIXStrError = int (*)(int,char*,size_t); inline const char* realJniStrError(GNUStrError func, int errnum, char* buf, size_t buflen) { return func(errnum, buf, buflen); } inline const char* realJniStrError(POSIXStrError func, int errnum, char* buf, size_t buflen) { int rc = func(errnum, buf, buflen); if (rc != 0) { // (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.) snprintf(buf, buflen, "errno %d", errnum); } return buf; } } // namespace impl MODULE_API const char* jniStrError(int errnum, char* buf, size_t buflen) { #ifdef _WIN32 strerror_s(buf, buflen, errnum); return buf; #else // The magic of C++ overloading selects the correct implementation based on the declared type of // strerror_r. The inline will ensure that we don't have any indirect calls. return impl::realJniStrError(strerror_r, errnum, buf, buflen); #endif } MODULE_API jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) { JNIEnv* e = reinterpret_cast(env); jobject fileDescriptor = e->NewObject(JniConstants::GetFileDescriptorClass(e), JniConstants::GetFileDescriptorInitMethod(e)); // NOTE: NewObject ensures that an OutOfMemoryError will be seen by the Java // caller if the alloc fails, so we just return nullptr when that happens. if (fileDescriptor != nullptr) { jniSetFileDescriptorOfFD(env, fileDescriptor, fd); } return fileDescriptor; } MODULE_API int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) { JNIEnv* e = reinterpret_cast(env); if (fileDescriptor != nullptr) { return e->GetIntField(fileDescriptor, JniConstants::GetFileDescriptorDescriptorField(e)); } else { return -1; } } MODULE_API void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) { JNIEnv* e = reinterpret_cast(env); if (fileDescriptor == nullptr) { jniThrowNullPointerException(e, "null FileDescriptor"); } else { e->SetIntField(fileDescriptor, JniConstants::GetFileDescriptorDescriptorField(e), value); } } MODULE_API jlong jniGetOwnerIdFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) { JNIEnv* e = reinterpret_cast(env); return e->GetLongField(fileDescriptor, JniConstants::GetFileDescriptorOwnerIdField(e)); } MODULE_API jarray jniGetNioBufferBaseArray(C_JNIEnv* env, jobject nioBuffer) { JNIEnv* e = reinterpret_cast(env); jclass nioAccessClass = JniConstants::GetNioAccessClass(e); jmethodID getBaseArrayMethod = JniConstants::GetNioAccessGetBaseArrayMethod(e); jobject object = e->CallStaticObjectMethod(nioAccessClass, getBaseArrayMethod, nioBuffer); return static_cast(object); } MODULE_API int jniGetNioBufferBaseArrayOffset(C_JNIEnv* env, jobject nioBuffer) { JNIEnv* e = reinterpret_cast(env); jclass nioAccessClass = JniConstants::GetNioAccessClass(e); jmethodID getBaseArrayOffsetMethod = JniConstants::GetNioAccessGetBaseArrayOffsetMethod(e); return e->CallStaticIntMethod(nioAccessClass, getBaseArrayOffsetMethod, nioBuffer); } MODULE_API jlong jniGetNioBufferPointer(C_JNIEnv* env, jobject nioBuffer) { JNIEnv* e = reinterpret_cast(env); jlong baseAddress = e->GetLongField(nioBuffer, JniConstants::GetNioBufferAddressField(e)); if (baseAddress != 0) { const int position = e->GetIntField(nioBuffer, JniConstants::GetNioBufferPositionField(e)); const int shift = e->GetIntField(nioBuffer, JniConstants::GetNioBufferElementSizeShiftField(e)); baseAddress += position << shift; } return baseAddress; } MODULE_API jlong jniGetNioBufferFields(C_JNIEnv* env, jobject nioBuffer, jint* position, jint* limit, jint* elementSizeShift) { JNIEnv* e = reinterpret_cast(env); *position = e->GetIntField(nioBuffer, JniConstants::GetNioBufferPositionField(e)); *limit = e->GetIntField(nioBuffer, JniConstants::GetNioBufferLimitField(e)); *elementSizeShift = e->GetIntField(nioBuffer, JniConstants::GetNioBufferElementSizeShiftField(e)); return e->GetLongField(nioBuffer, JniConstants::GetNioBufferAddressField(e)); } MODULE_API jobject jniGetReferent(C_JNIEnv* env, jobject ref) { JNIEnv* e = reinterpret_cast(env); return e->CallObjectMethod(ref, JniConstants::GetReferenceGetMethod(e)); } MODULE_API jstring jniCreateString(C_JNIEnv* env, const jchar* unicodeChars, jsize len) { JNIEnv* e = reinterpret_cast(env); return e->NewString(unicodeChars, len); } MODULE_API void jniUninitializeConstants() { JniConstants::Uninitialize(); } JniConstants.cpp0100644 0000000 0000000 00000027433 13756723631 012754 0ustar000000000 0000000 /* * Copyright (C) 2010 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 "JniConstants" #include "ALog-priv.h" #include "JniConstants.h" #include #include #include #include "nativehelper/ScopedLocalRef.h" namespace { // Mutex protecting the initialization of cached class references. std::mutex g_class_refs_mutex; // Atomic boolean flag for double locked checking that class references are // initialized before use. std::atomic g_class_refs_initialized(false); // Cached global references to class instances. // // These are GC heap references that are initialized under the protection of // |g_class_refs_mutex| as they should only be initialized once to avoid losing a // global reference. Initialization happens lazily when an accessor tries to // retrieve one of these classes. jclass g_file_descriptor_class = nullptr; // java.io.FileDescriptor jclass g_nio_access_class = nullptr; // java.nio.Access jclass g_nio_buffer_class = nullptr; // java.nio.Buffer jclass g_reference_class = nullptr; // java.lang.ref.Reference jclass g_string_class = nullptr; // java.lang.String // Cached field and method ids. // // These are non-GC heap values. They are initialized lazily and racily. We // avoid holding a mutex here because the JNI API supports concurrent calls to // Get{Field,Method}ID and also because finding an id may recursively call into // Get{Field,Method}ID. // // The recursion issue occurs here for the fields in the FileDescriptor class // since retrieving a field id requires the class to be initialized. Class // initialization leads to the initialization of static fields. The // FileDescriptor class has static fields that are FileDescriptor instances. The // initialization of these static FileDescriptor fields follows a convoluted // path that that leads to a call to jniGetFDFromFileDescriptor() which then // needs to call GetFieldID() which is in the call stack. If thread-safety were // desirable here, a recursive mutex would be required. // // These field and method ids have default values of nullptr. They are reset // back to nullptr in JniConstants::Uninitialize(), along with the class // references, when a new runtime instance is created via JNI_CreateJavaVM(). The // reset happens before the new runtime instance is returned to the caller and // under the protection of the |g_class_refs_mutex|. jfieldID g_file_descriptor_descriptor_field = nullptr; // java.io.FileDescriptor.descriptor jfieldID g_file_descriptor_owner_id_field = nullptr; // java.io.FileDescriptor.ownerId jmethodID g_file_descriptor_init_method = nullptr; // void java.io.FileDescriptor.() jmethodID g_nio_access_get_base_array_method = nullptr; // Object java.nio.NIOAccess.getBaseArray() jmethodID g_nio_access_get_base_array_offset_method = nullptr; // Object java.nio.NIOAccess.getBaseArray() jfieldID g_nio_buffer_address_field = nullptr; // long java.nio.Buffer.address jfieldID g_nio_buffer_element_size_shift_field = nullptr; // int java.nio.Buffer._elementSizeShift jfieldID g_nio_buffer_limit_field = nullptr; // int java.nio.Buffer.limit jfieldID g_nio_buffer_position_field = nullptr; // int java.nio.Buffer.position jmethodID g_nio_buffer_array_method = nullptr; // Object java.nio.Buffer.array() jmethodID g_nio_buffer_array_offset_method = nullptr; // int java.nio.Buffer.arrayOffset() jmethodID g_reference_get_method = nullptr; // Object java.lang.ref.Reference.get() jclass FindClass(JNIEnv* env, const char* name) { ScopedLocalRef klass(env, env->FindClass(name)); ALOG_ALWAYS_FATAL_IF(klass.get() == nullptr, "failed to find class '%s'", name); return reinterpret_cast(env->NewGlobalRef(klass.get())); } jfieldID FindField(JNIEnv* env, jclass klass, const char* name, const char* desc) { jfieldID result = env->GetFieldID(klass, name, desc); ALOG_ALWAYS_FATAL_IF(result == nullptr, "failed to find field '%s:%s'", name, desc); return result; } jmethodID FindMethod(JNIEnv* env, jclass klass, const char* name, const char* signature) { jmethodID result = env->GetMethodID(klass, name, signature); ALOG_ALWAYS_FATAL_IF(result == nullptr, "failed to find method '%s%s'", name, signature); return result; } jmethodID FindStaticMethod(JNIEnv* env, jclass klass, const char* name, const char* signature) { jmethodID result = env->GetStaticMethodID(klass, name, signature); ALOG_ALWAYS_FATAL_IF(result == nullptr, "failed to find static method '%s%s'", name, signature); return result; } } // namespace jclass JniConstants::GetFileDescriptorClass(JNIEnv* env) { EnsureClassReferencesInitialized(env); return g_file_descriptor_class; } jclass JniConstants::GetNioAccessClass(JNIEnv* env) { EnsureClassReferencesInitialized(env); return g_nio_access_class; } jclass JniConstants::GetNioBufferClass(JNIEnv* env) { EnsureClassReferencesInitialized(env); return g_nio_buffer_class; } jclass JniConstants::GetReferenceClass(JNIEnv* env) { EnsureClassReferencesInitialized(env); return g_reference_class; } jclass JniConstants::GetStringClass(JNIEnv* env) { EnsureClassReferencesInitialized(env); return g_string_class; } jfieldID JniConstants::GetFileDescriptorDescriptorField(JNIEnv* env) { if (g_file_descriptor_descriptor_field == nullptr) { jclass klass = GetFileDescriptorClass(env); g_file_descriptor_descriptor_field = FindField(env, klass, "descriptor", "I"); } return g_file_descriptor_descriptor_field; } jfieldID JniConstants::GetFileDescriptorOwnerIdField(JNIEnv* env) { if (g_file_descriptor_owner_id_field == nullptr) { jclass klass = GetFileDescriptorClass(env); g_file_descriptor_owner_id_field = FindField(env, klass, "ownerId", "J"); } return g_file_descriptor_owner_id_field; } jmethodID JniConstants::GetFileDescriptorInitMethod(JNIEnv* env) { if (g_file_descriptor_init_method == nullptr) { jclass klass = GetFileDescriptorClass(env); g_file_descriptor_init_method = FindMethod(env, klass, "", "()V"); } return g_file_descriptor_init_method; } jmethodID JniConstants::GetNioAccessGetBaseArrayMethod(JNIEnv* env) { if (g_nio_access_get_base_array_method == nullptr) { jclass klass = GetNioAccessClass(env); g_nio_access_get_base_array_method = FindStaticMethod(env, klass, "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;"); } return g_nio_access_get_base_array_method; } jmethodID JniConstants::GetNioAccessGetBaseArrayOffsetMethod(JNIEnv* env) { if (g_nio_access_get_base_array_offset_method == nullptr) { jclass klass = GetNioAccessClass(env); g_nio_access_get_base_array_offset_method = FindStaticMethod(env, klass, "getBaseArrayOffset", "(Ljava/nio/Buffer;)I"); } return g_nio_access_get_base_array_offset_method; } jfieldID JniConstants::GetNioBufferAddressField(JNIEnv* env) { if (g_nio_buffer_address_field == nullptr) { jclass klass = GetNioBufferClass(env); g_nio_buffer_address_field = FindField(env, klass, "address", "J"); } return g_nio_buffer_address_field; } jfieldID JniConstants::GetNioBufferElementSizeShiftField(JNIEnv* env) { if (g_nio_buffer_element_size_shift_field == nullptr) { jclass klass = GetNioBufferClass(env); g_nio_buffer_element_size_shift_field = FindField(env, klass, "_elementSizeShift", "I"); } return g_nio_buffer_element_size_shift_field; } jfieldID JniConstants::GetNioBufferLimitField(JNIEnv* env) { if (g_nio_buffer_limit_field == nullptr) { jclass klass = GetNioBufferClass(env); g_nio_buffer_limit_field = FindField(env, klass, "limit", "I"); } return g_nio_buffer_limit_field; } jfieldID JniConstants::GetNioBufferPositionField(JNIEnv* env) { if (g_nio_buffer_position_field == nullptr) { jclass klass = GetNioBufferClass(env); g_nio_buffer_position_field = FindField(env, klass, "position", "I"); } return g_nio_buffer_position_field; } jmethodID JniConstants::GetNioBufferArrayMethod(JNIEnv* env) { if (g_nio_buffer_array_method == nullptr) { jclass klass = GetNioBufferClass(env); g_nio_buffer_array_method = FindMethod(env, klass, "array", "()Ljava/lang/Object;"); } return g_nio_buffer_array_method; } jmethodID JniConstants::GetNioBufferArrayOffsetMethod(JNIEnv* env) { if (g_nio_buffer_array_offset_method == nullptr) { jclass klass = GetNioBufferClass(env); g_nio_buffer_array_offset_method = FindMethod(env, klass, "arrayOffset", "()I"); } return g_nio_buffer_array_offset_method; } jmethodID JniConstants::GetReferenceGetMethod(JNIEnv* env) { if (g_reference_get_method == nullptr) { jclass klass = GetReferenceClass(env); g_reference_get_method = FindMethod(env, klass, "get", "()Ljava/lang/Object;"); } return g_reference_get_method; } void JniConstants::EnsureClassReferencesInitialized(JNIEnv* env) { // Fast check if class references are initialized. if (g_class_refs_initialized.load(std::memory_order_acquire)) { return; } // Slower check with initialization if necessary. std::lock_guard guard(g_class_refs_mutex); if (g_class_refs_initialized.load(std::memory_order_relaxed)) { return; } // Class constants should be initialized only once because they global // references. Field ids and Method ids can be initialized later since they // are not references and races only have trivial performance // consequences. g_file_descriptor_class = FindClass(env, "java/io/FileDescriptor"); g_nio_access_class = FindClass(env, "java/nio/NIOAccess"); g_nio_buffer_class = FindClass(env, "java/nio/Buffer"); g_reference_class = FindClass(env, "java/lang/ref/Reference"); g_string_class = FindClass(env, "java/lang/String"); g_class_refs_initialized.store(true, std::memory_order_release); } void JniConstants::Uninitialize() { // This method is called when a new runtime instance is created. There is no // notification of a runtime instance being destroyed in the JNI interface // so we piggyback on creation. Since only one runtime is supported at a // time, we know the constants are invalid when JNI_CreateJavaVM() is // called. // // Clean shutdown would require calling DeleteGlobalRef() for each of the // class references. std::lock_guard guard(g_class_refs_mutex); g_file_descriptor_class = nullptr; g_file_descriptor_descriptor_field = nullptr; g_file_descriptor_owner_id_field = nullptr; g_file_descriptor_init_method = nullptr; g_nio_access_class = nullptr; g_nio_access_get_base_array_method = nullptr; g_nio_access_get_base_array_offset_method = nullptr; g_nio_buffer_class = nullptr; g_nio_buffer_address_field = nullptr; g_nio_buffer_element_size_shift_field = nullptr; g_nio_buffer_limit_field = nullptr; g_nio_buffer_position_field = nullptr; g_nio_buffer_array_method = nullptr; g_nio_buffer_array_offset_method = nullptr; g_reference_class = nullptr; g_reference_get_method = nullptr; g_string_class = nullptr; g_class_refs_initialized.store(false, std::memory_order_release); } JniConstants.h0100644 0000000 0000000 00000005751 13756723631 012420 0ustar000000000 0000000 /* * Copyright 2018 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. */ #pragma once #include "jni.h" struct JniConstants { // Global reference to java.io.FileDescriptor. static jclass GetFileDescriptorClass(JNIEnv* env); // java.io.FileDescriptor.descriptor. static jfieldID GetFileDescriptorDescriptorField(JNIEnv* env); // java.io.FileDescriptor.ownerId. static jfieldID GetFileDescriptorOwnerIdField(JNIEnv* env); // void java.io.FileDescriptor.(). static jmethodID GetFileDescriptorInitMethod(JNIEnv* env); // Global reference to java.nio.NIOAccess. static jclass GetNioAccessClass(JNIEnv* env); // Object java.nio.NIOAccess.getBaseArray(Buffer); static jmethodID GetNioAccessGetBaseArrayMethod(JNIEnv* env); // int java.nio.NIOAccess.getBaseArrayOffset(Buffer); static jmethodID GetNioAccessGetBaseArrayOffsetMethod(JNIEnv* env); // Global reference to java.nio.Buffer. static jclass GetNioBufferClass(JNIEnv* env); // long java.nio.Buffer.address static jfieldID GetNioBufferAddressField(JNIEnv* env); // int java.nio.Buffer._elementSizeShift static jfieldID GetNioBufferElementSizeShiftField(JNIEnv* env); // int java.nio.Buffer.limit; static jfieldID GetNioBufferLimitField(JNIEnv* env); // int java.nio.Buffer.position; static jfieldID GetNioBufferPositionField(JNIEnv* env); // Object java.nio.Buffer.array() static jmethodID GetNioBufferArrayMethod(JNIEnv* env); // int java.nio.Buffer.arrayOffset() static jmethodID GetNioBufferArrayOffsetMethod(JNIEnv* env); // Global reference to java.lang.ref.Reference. static jclass GetReferenceClass(JNIEnv* env); // Object java.lang.ref.Reference.get() static jmethodID GetReferenceGetMethod(JNIEnv* env); // Global reference to java.lang.String. static jclass GetStringClass(JNIEnv* env); // Ensure class constants are initialized before use. Field and method // constants are lazily initialized via getters. static void EnsureClassReferencesInitialized(JNIEnv* env); // Ensure any cached heap objects from previous VM instances are // invalidated. There is no notification here that a VM is destroyed so this // method must be called when a new VM is created (and calls from any // earlier VM's are completed). The caching of heap objects in this class is // one reason why there is a limit of VM instance per process. static void Uninitialize(); }; JniInvocation.cpp0100644 0000000 0000000 00000023260 13756723631 013103 0ustar000000000 0000000 /* * Copyright (C) 2013 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. */ #include "nativehelper/JniInvocation.h" #ifdef _WIN32 #include #else #include #endif #include #include #include #define LOG_TAG "JniInvocation" #include #ifdef __ANDROID__ #include #endif #include "android-base/errors.h" #include "JniConstants.h" namespace { template void UNUSED(const T&) {} bool IsDebuggable() { #ifdef __ANDROID__ char debuggable[PROP_VALUE_MAX] = {0}; __system_property_get("ro.debuggable", debuggable); return strcmp(debuggable, "1") == 0; #else return false; #endif } int GetLibrarySystemProperty(char* buffer) { #ifdef __ANDROID__ return __system_property_get("persist.sys.dalvik.vm.lib.2", buffer); #else UNUSED(buffer); return 0; #endif } #ifdef _WIN32 #define FUNC_POINTER FARPROC #else #define FUNC_POINTER void* #endif void* OpenLibrary(const char* filename) { #ifdef _WIN32 return LoadLibrary(filename); #else // Load with RTLD_NODELETE in order to ensure that libart.so is not unmapped when it is closed. // This is due to the fact that it is possible that some threads might have yet to finish // exiting even after JNI_DeleteJavaVM returns, which can lead to segfaults if the library is // unloaded. const int kDlopenFlags = RTLD_NOW | RTLD_NODELETE; return dlopen(filename, kDlopenFlags); #endif } int CloseLibrary(void* handle) { #ifdef _WIN32 return FreeLibrary(static_cast(handle)); #else return dlclose(handle); #endif } FUNC_POINTER GetSymbol(void* handle, const char* symbol) { #ifdef _WIN32 return GetProcAddress(static_cast(handle), symbol); #else return dlsym(handle, symbol); #endif } std::string GetError() { #ifdef _WIN32 return android::base::SystemErrorCodeToString(GetLastError()); #else return std::string(dlerror()); #endif } } // namespace struct JniInvocationImpl final { public: JniInvocationImpl(); ~JniInvocationImpl(); bool Init(const char* library); // static const char* GetLibrary(const char* library, char* buffer); static const char* GetLibrary(const char* library, char* buffer, bool (*is_debuggable)() = IsDebuggable, int (*get_library_system_property)(char* buffer) = GetLibrarySystemProperty); static JniInvocationImpl& GetJniInvocation(); jint JNI_GetDefaultJavaVMInitArgs(void* vmargs); jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args); jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count); private: JniInvocationImpl(const JniInvocationImpl&) = delete; JniInvocationImpl& operator=(const JniInvocationImpl&) = delete; bool FindSymbol(FUNC_POINTER* pointer, const char* symbol); static JniInvocationImpl* jni_invocation_; // Handle to library opened with dlopen(). Library exports // JNI_GetDefaultJavaVMInitArgs, JNI_CreateJavaVM, JNI_GetCreatedJavaVMs. void* handle_; jint (*JNI_GetDefaultJavaVMInitArgs_)(void*); jint (*JNI_CreateJavaVM_)(JavaVM**, JNIEnv**, void*); jint (*JNI_GetCreatedJavaVMs_)(JavaVM**, jsize, jsize*); friend class JNIInvocation_Debuggable_Test; friend class JNIInvocation_NonDebuggable_Test; }; // Check JniInvocationImpl size is same as fields, e.g. no vtable present. static_assert(sizeof(JniInvocationImpl) == 4 * sizeof(uintptr_t)); JniInvocationImpl* JniInvocationImpl::jni_invocation_ = NULL; JniInvocationImpl::JniInvocationImpl() : handle_(NULL), JNI_GetDefaultJavaVMInitArgs_(NULL), JNI_CreateJavaVM_(NULL), JNI_GetCreatedJavaVMs_(NULL) { LOG_ALWAYS_FATAL_IF(jni_invocation_ != NULL, "JniInvocation instance already initialized"); jni_invocation_ = this; } JniInvocationImpl::~JniInvocationImpl() { jni_invocation_ = NULL; if (handle_ != NULL) { CloseLibrary(handle_); } } static const char* kLibraryFallback = "libart.so"; const char* JniInvocationImpl::GetLibrary(const char* library, char* buffer, bool (*is_debuggable)(), int (*get_library_system_property)(char* buffer)) { #ifdef __ANDROID__ const char* default_library; if (!is_debuggable()) { // Not a debuggable build. // Do not allow arbitrary library. Ignore the library parameter. This // will also ignore the default library, but initialize to fallback // for cleanliness. library = kLibraryFallback; default_library = kLibraryFallback; } else { // Debuggable build. // Accept the library parameter. For the case it is NULL, load the default // library from the system property. if (buffer != NULL) { if (get_library_system_property(buffer) > 0) { default_library = buffer; } else { default_library = kLibraryFallback; } } else { // No buffer given, just use default fallback. default_library = kLibraryFallback; } } #else UNUSED(buffer); UNUSED(is_debuggable); UNUSED(get_library_system_property); const char* default_library = kLibraryFallback; #endif if (library == NULL) { library = default_library; } return library; } bool JniInvocationImpl::Init(const char* library) { #ifdef __ANDROID__ char buffer[PROP_VALUE_MAX]; #else char* buffer = NULL; #endif library = GetLibrary(library, buffer); handle_ = OpenLibrary(library); if (handle_ == NULL) { if (strcmp(library, kLibraryFallback) == 0) { // Nothing else to try. ALOGE("Failed to dlopen %s: %s", library, GetError().c_str()); return false; } // Note that this is enough to get something like the zygote // running, we can't property_set here to fix this for the future // because we are root and not the system user. See // RuntimeInit.commonInit for where we fix up the property to // avoid future fallbacks. http://b/11463182 ALOGW("Falling back from %s to %s after dlopen error: %s", library, kLibraryFallback, GetError().c_str()); library = kLibraryFallback; handle_ = OpenLibrary(library); if (handle_ == NULL) { ALOGE("Failed to dlopen %s: %s", library, GetError().c_str()); return false; } } if (!FindSymbol(reinterpret_cast(&JNI_GetDefaultJavaVMInitArgs_), "JNI_GetDefaultJavaVMInitArgs")) { return false; } if (!FindSymbol(reinterpret_cast(&JNI_CreateJavaVM_), "JNI_CreateJavaVM")) { return false; } if (!FindSymbol(reinterpret_cast(&JNI_GetCreatedJavaVMs_), "JNI_GetCreatedJavaVMs")) { return false; } return true; } jint JniInvocationImpl::JNI_GetDefaultJavaVMInitArgs(void* vmargs) { return JNI_GetDefaultJavaVMInitArgs_(vmargs); } jint JniInvocationImpl::JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) { return JNI_CreateJavaVM_(p_vm, p_env, vm_args); } jint JniInvocationImpl::JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count) { return JNI_GetCreatedJavaVMs_(vms, size, vm_count); } bool JniInvocationImpl::FindSymbol(FUNC_POINTER* pointer, const char* symbol) { *pointer = GetSymbol(handle_, symbol); if (*pointer == NULL) { ALOGE("Failed to find symbol %s: %s\n", symbol, GetError().c_str()); CloseLibrary(handle_); handle_ = NULL; return false; } return true; } JniInvocationImpl& JniInvocationImpl::GetJniInvocation() { LOG_ALWAYS_FATAL_IF(jni_invocation_ == NULL, "Failed to create JniInvocation instance before using JNI invocation API"); return *jni_invocation_; } MODULE_API jint JNI_GetDefaultJavaVMInitArgs(void* vm_args) { return JniInvocationImpl::GetJniInvocation().JNI_GetDefaultJavaVMInitArgs(vm_args); } MODULE_API jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) { // Ensure any cached heap objects from previous VM instances are // invalidated. There is no notification here that a VM is destroyed. These // cached objects limit us to one VM instance per process. JniConstants::Uninitialize(); return JniInvocationImpl::GetJniInvocation().JNI_CreateJavaVM(p_vm, p_env, vm_args); } MODULE_API jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count) { return JniInvocationImpl::GetJniInvocation().JNI_GetCreatedJavaVMs(vms, size, vm_count); } MODULE_API JniInvocationImpl* JniInvocationCreate() { return new JniInvocationImpl(); } MODULE_API void JniInvocationDestroy(JniInvocationImpl* instance) { delete instance; } MODULE_API int JniInvocationInit(JniInvocationImpl* instance, const char* library) { return instance->Init(library) ? 1 : 0; } MODULE_API const char* JniInvocationGetLibrary(const char* library, char* buffer) { return JniInvocationImpl::GetLibrary(library, buffer); } MODULE_API const char* JniInvocation::GetLibrary(const char* library, char* buffer, bool (*is_debuggable)(), int (*get_library_system_property)(char* buffer)) { return JniInvocationImpl::GetLibrary(library, buffer, is_debuggable, get_library_system_property); } MODULE_LICENSE_APACHE20100644 0000000 0000000 00000000000 13756723631 012745 0ustar000000000 0000000 NOTICE0100644 0000000 0000000 00000024707 13756723631 010540 0ustar000000000 0000000 Copyright (c) 2005-2008, 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. 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. Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS README0100644 0000000 0000000 00000000571 13756723631 010505 0ustar000000000 0000000 Support functions for Android's class libraries These are VM-agnostic native functions that implement methods for system class libraries. All code here: - MUST not be associated with an android.* class (that code lives in frameworks/base/). - SHOULD be written in C rather than C++ where possible. Some helper functions are defined in include/nativehelper/JNIHelp.h. header_only_include/0040755 0000000 0000000 00000000000 13756723631 013621 5ustar000000000 0000000 header_only_include/nativehelper/0040755 0000000 0000000 00000000000 13756723631 016307 5ustar000000000 0000000 header_only_include/nativehelper/nativehelper_utils.h0100644 0000000 0000000 00000003272 13756723631 022367 0ustar000000000 0000000 /* * Copyright (C) 2007 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. */ #ifndef NATIVEHELPER_MACROS_H_ #define NATIVEHELPER_MACROS_H_ #if defined(__cplusplus) #if !defined(DISALLOW_COPY_AND_ASSIGN) // DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. It goes in the private: // declarations in a class. #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ TypeName(const TypeName&) = delete; \ void operator=(const TypeName&) = delete #endif // !defined(DISALLOW_COPY_AND_ASSIGN) #ifndef NATIVEHELPER_JNIHELP_H_ // This seems a header-only include. Provide NPE throwing. static inline int jniThrowNullPointerException(JNIEnv* env, const char* msg) { if (env->ExceptionCheck()) { // Drop any pending exception. env->ExceptionClear(); } jclass e_class = env->FindClass("java/lang/NullPointerException"); if (e_class == nullptr) { return -1; } if (env->ThrowNew(e_class, msg) != JNI_OK) { env->DeleteLocalRef(e_class); return -1; } env->DeleteLocalRef(e_class); return 0; } #endif // NATIVEHELPER_JNIHELP_H_ #endif // defined(__cplusplus) #endif // NATIVEHELPER_MACROS_H_ header_only_include/nativehelper/scoped_bytes.h0100644 0000000 0000000 00000004775 13756723631 021155 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ #ifndef SCOPED_BYTES_H_ #define SCOPED_BYTES_H_ #include "jni.h" #include "nativehelper_utils.h" /** * ScopedBytesRO and ScopedBytesRW attempt to paper over the differences between byte[]s and * ByteBuffers. This in turn helps paper over the differences between non-direct ByteBuffers backed * by byte[]s, direct ByteBuffers backed by bytes[]s, and direct ByteBuffers not backed by byte[]s. * (On Android, this last group only contains MappedByteBuffers.) */ template class ScopedBytes { public: ScopedBytes(JNIEnv* env, jobject object) : mEnv(env), mObject(object), mByteArray(NULL), mPtr(NULL) { if (mObject == NULL) { jniThrowNullPointerException(mEnv, NULL); } else { jclass byteArrayClass = env->FindClass("[B"); if (mEnv->IsInstanceOf(mObject, byteArrayClass)) { mByteArray = reinterpret_cast(mObject); mPtr = mEnv->GetByteArrayElements(mByteArray, NULL); } else { mPtr = reinterpret_cast(mEnv->GetDirectBufferAddress(mObject)); } mEnv->DeleteLocalRef(byteArrayClass); } } ~ScopedBytes() { if (mByteArray != NULL) { mEnv->ReleaseByteArrayElements(mByteArray, mPtr, readOnly ? JNI_ABORT : 0); } } private: JNIEnv* const mEnv; const jobject mObject; jbyteArray mByteArray; protected: jbyte* mPtr; private: DISALLOW_COPY_AND_ASSIGN(ScopedBytes); }; class ScopedBytesRO : public ScopedBytes { public: ScopedBytesRO(JNIEnv* env, jobject object) : ScopedBytes(env, object) {} const jbyte* get() const { return mPtr; } }; class ScopedBytesRW : public ScopedBytes { public: ScopedBytesRW(JNIEnv* env, jobject object) : ScopedBytes(env, object) {} jbyte* get() { return mPtr; } }; #endif // SCOPED_BYTES_H_ header_only_include/nativehelper/scoped_local_frame.h0100644 0000000 0000000 00000002033 13756723631 022254 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ #ifndef SCOPED_LOCAL_FRAME_H_ #define SCOPED_LOCAL_FRAME_H_ #include "jni.h" #include "nativehelper_utils.h" class ScopedLocalFrame { public: explicit ScopedLocalFrame(JNIEnv* env) : mEnv(env) { mEnv->PushLocalFrame(128); } ~ScopedLocalFrame() { mEnv->PopLocalFrame(NULL); } private: JNIEnv* const mEnv; DISALLOW_COPY_AND_ASSIGN(ScopedLocalFrame); }; #endif // SCOPED_LOCAL_FRAME_H_ header_only_include/nativehelper/scoped_local_ref.h0100644 0000000 0000000 00000004435 13756723631 021746 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ #ifndef SCOPED_LOCAL_REF_H_ #define SCOPED_LOCAL_REF_H_ #include #include "jni.h" #include "nativehelper_utils.h" // A smart pointer that deletes a JNI local reference when it goes out of scope. template class ScopedLocalRef { public: ScopedLocalRef(JNIEnv* env, T localRef) : mEnv(env), mLocalRef(localRef) { } ScopedLocalRef(ScopedLocalRef&& s) noexcept : mEnv(s.mEnv), mLocalRef(s.release()) { } explicit ScopedLocalRef(JNIEnv* env) : mEnv(env), mLocalRef(nullptr) { } ~ScopedLocalRef() { reset(); } void reset(T ptr = NULL) { if (ptr != mLocalRef) { if (mLocalRef != NULL) { mEnv->DeleteLocalRef(mLocalRef); } mLocalRef = ptr; } } T release() __attribute__((warn_unused_result)) { T localRef = mLocalRef; mLocalRef = NULL; return localRef; } T get() const { return mLocalRef; } // We do not expose an empty constructor as it can easily lead to errors // using common idioms, e.g.: // ScopedLocalRef<...> ref; // ref.reset(...); // Move assignment operator. ScopedLocalRef& operator=(ScopedLocalRef&& s) noexcept { reset(s.release()); mEnv = s.mEnv; return *this; } // Allows "if (scoped_ref == nullptr)" bool operator==(std::nullptr_t) const { return mLocalRef == nullptr; } // Allows "if (scoped_ref != nullptr)" bool operator!=(std::nullptr_t) const { return mLocalRef != nullptr; } private: JNIEnv* mEnv; T mLocalRef; DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef); }; #endif // SCOPED_LOCAL_REF_H_ header_only_include/nativehelper/scoped_primitive_array.h0100644 0000000 0000000 00000014151 13756723631 023222 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ #ifndef SCOPED_PRIMITIVE_ARRAY_H_ #define SCOPED_PRIMITIVE_ARRAY_H_ #include "jni.h" #include "nativehelper_utils.h" #ifdef POINTER_TYPE #error POINTER_TYPE is defined. #else #define POINTER_TYPE(T) T* /* NOLINT */ #endif #ifdef REFERENCE_TYPE #error REFERENCE_TYPE is defined. #else #define REFERENCE_TYPE(T) T& /* NOLINT */ #endif // ScopedBooleanArrayRO, ScopedByteArrayRO, ScopedCharArrayRO, ScopedDoubleArrayRO, // ScopedFloatArrayRO, ScopedIntArrayRO, ScopedLongArrayRO, and ScopedShortArrayRO provide // convenient read-only access to Java arrays from JNI code. This is cheaper than read-write // access and should be used by default. #define INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(PRIMITIVE_TYPE, NAME) \ class Scoped ## NAME ## ArrayRO { \ public: \ explicit Scoped ## NAME ## ArrayRO(JNIEnv* env) \ : mEnv(env), mJavaArray(NULL), mRawArray(NULL), mSize(0) {} \ Scoped ## NAME ## ArrayRO(JNIEnv* env, PRIMITIVE_TYPE ## Array javaArray) \ : mEnv(env) { \ if (javaArray == NULL) { \ mJavaArray = NULL; \ mSize = 0; \ mRawArray = NULL; \ jniThrowNullPointerException(mEnv, NULL); \ } else { \ reset(javaArray); \ } \ } \ ~Scoped ## NAME ## ArrayRO() { \ if (mRawArray != NULL && mRawArray != mBuffer) { \ mEnv->Release ## NAME ## ArrayElements(mJavaArray, mRawArray, JNI_ABORT); \ } \ } \ void reset(PRIMITIVE_TYPE ## Array javaArray) { \ mJavaArray = javaArray; \ mSize = mEnv->GetArrayLength(mJavaArray); \ if (mSize <= buffer_size) { \ mEnv->Get ## NAME ## ArrayRegion(mJavaArray, 0, mSize, mBuffer); \ mRawArray = mBuffer; \ } else { \ mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, NULL); \ } \ } \ const PRIMITIVE_TYPE* get() const { return mRawArray; } \ PRIMITIVE_TYPE ## Array getJavaArray() const { return mJavaArray; } \ const PRIMITIVE_TYPE& operator[](size_t n) const { return mRawArray[n]; } \ size_t size() const { return mSize; } \ private: \ static const jsize buffer_size = 1024; \ JNIEnv* const mEnv; \ PRIMITIVE_TYPE ## Array mJavaArray; \ POINTER_TYPE(PRIMITIVE_TYPE) mRawArray; \ jsize mSize; \ PRIMITIVE_TYPE mBuffer[buffer_size]; \ DISALLOW_COPY_AND_ASSIGN(Scoped ## NAME ## ArrayRO); \ } INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jboolean, Boolean); INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jbyte, Byte); INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jchar, Char); INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jdouble, Double); INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jfloat, Float); INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jint, Int); INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jlong, Long); INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jshort, Short); #undef INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO // ScopedBooleanArrayRW, ScopedByteArrayRW, ScopedCharArrayRW, ScopedDoubleArrayRW, // ScopedFloatArrayRW, ScopedIntArrayRW, ScopedLongArrayRW, and ScopedShortArrayRW provide // convenient read-write access to Java arrays from JNI code. These are more expensive, // since they entail a copy back onto the Java heap, and should only be used when necessary. #define INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(PRIMITIVE_TYPE, NAME) \ class Scoped ## NAME ## ArrayRW { \ public: \ explicit Scoped ## NAME ## ArrayRW(JNIEnv* env) \ : mEnv(env), mJavaArray(NULL), mRawArray(NULL) {} \ Scoped ## NAME ## ArrayRW(JNIEnv* env, PRIMITIVE_TYPE ## Array javaArray) \ : mEnv(env), mJavaArray(javaArray), mRawArray(NULL) { \ if (mJavaArray == NULL) { \ jniThrowNullPointerException(mEnv, NULL); \ } else { \ mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, NULL); \ } \ } \ ~Scoped ## NAME ## ArrayRW() { \ if (mRawArray) { \ mEnv->Release ## NAME ## ArrayElements(mJavaArray, mRawArray, 0); \ } \ } \ void reset(PRIMITIVE_TYPE ## Array javaArray) { \ mJavaArray = javaArray; \ mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, NULL); \ } \ const PRIMITIVE_TYPE* get() const { return mRawArray; } \ PRIMITIVE_TYPE ## Array getJavaArray() const { return mJavaArray; } \ const PRIMITIVE_TYPE& operator[](size_t n) const { return mRawArray[n]; } \ POINTER_TYPE(PRIMITIVE_TYPE) get() { return mRawArray; } \ REFERENCE_TYPE(PRIMITIVE_TYPE) operator[](size_t n) { return mRawArray[n]; } \ size_t size() const { return mEnv->GetArrayLength(mJavaArray); } \ private: \ JNIEnv* const mEnv; \ PRIMITIVE_TYPE ## Array mJavaArray; \ POINTER_TYPE(PRIMITIVE_TYPE) mRawArray; \ DISALLOW_COPY_AND_ASSIGN(Scoped ## NAME ## ArrayRW); \ } INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jboolean, Boolean); INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jbyte, Byte); INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jchar, Char); INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jdouble, Double); INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jfloat, Float); INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jint, Int); INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jlong, Long); INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jshort, Short); #undef INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW #undef POINTER_TYPE #undef REFERENCE_TYPE #endif // SCOPED_PRIMITIVE_ARRAY_H_ header_only_include/nativehelper/scoped_string_chars.h0100644 0000000 0000000 00000003571 13756723631 022506 0ustar000000000 0000000 /* * Copyright (C) 2011 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. */ #ifndef SCOPED_STRING_CHARS_H_ #define SCOPED_STRING_CHARS_H_ #include "jni.h" #include "nativehelper_utils.h" // A smart pointer that provides access to a jchar* given a JNI jstring. // Unlike GetStringChars, we throw NullPointerException rather than abort if // passed a null jstring, and get will return NULL. // This makes the correct idiom very simple: // // ScopedStringChars name(env, java_name); // if (name.get() == NULL) { // return NULL; // } class ScopedStringChars { public: ScopedStringChars(JNIEnv* env, jstring s) : env_(env), string_(s), size_(0) { if (s == NULL) { chars_ = NULL; jniThrowNullPointerException(env, NULL); } else { chars_ = env->GetStringChars(string_, NULL); if (chars_ != NULL) { size_ = env->GetStringLength(string_); } } } ~ScopedStringChars() { if (chars_ != NULL) { env_->ReleaseStringChars(string_, chars_); } } const jchar* get() const { return chars_; } size_t size() const { return size_; } const jchar& operator[](size_t n) const { return chars_[n]; } private: JNIEnv* const env_; const jstring string_; const jchar* chars_; size_t size_; DISALLOW_COPY_AND_ASSIGN(ScopedStringChars); }; #endif // SCOPED_STRING_CHARS_H_ header_only_include/nativehelper/scoped_utf_chars.h0100644 0000000 0000000 00000004666 13756723631 022004 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ #ifndef SCOPED_UTF_CHARS_H_ #define SCOPED_UTF_CHARS_H_ #include #include "jni.h" #include "nativehelper_utils.h" // A smart pointer that provides read-only access to a Java string's UTF chars. // Unlike GetStringUTFChars, we throw NullPointerException rather than abort if // passed a null jstring, and c_str will return nullptr. // This makes the correct idiom very simple: // // ScopedUtfChars name(env, java_name); // if (name.c_str() == nullptr) { // return nullptr; // } class ScopedUtfChars { public: ScopedUtfChars(JNIEnv* env, jstring s) : env_(env), string_(s) { if (s == nullptr) { utf_chars_ = nullptr; jniThrowNullPointerException(env, nullptr); } else { utf_chars_ = env->GetStringUTFChars(s, nullptr); } } ScopedUtfChars(ScopedUtfChars&& rhs) noexcept : env_(rhs.env_), string_(rhs.string_), utf_chars_(rhs.utf_chars_) { rhs.env_ = nullptr; rhs.string_ = nullptr; rhs.utf_chars_ = nullptr; } ~ScopedUtfChars() { if (utf_chars_) { env_->ReleaseStringUTFChars(string_, utf_chars_); } } ScopedUtfChars& operator=(ScopedUtfChars&& rhs) noexcept { if (this != &rhs) { // Delete the currently owned UTF chars. this->~ScopedUtfChars(); // Move the rhs ScopedUtfChars and zero it out. env_ = rhs.env_; string_ = rhs.string_; utf_chars_ = rhs.utf_chars_; rhs.env_ = nullptr; rhs.string_ = nullptr; rhs.utf_chars_ = nullptr; } return *this; } const char* c_str() const { return utf_chars_; } size_t size() const { return strlen(utf_chars_); } const char& operator[](size_t n) const { return utf_chars_[n]; } private: JNIEnv* env_; jstring string_; const char* utf_chars_; DISALLOW_COPY_AND_ASSIGN(ScopedUtfChars); }; #endif // SCOPED_UTF_CHARS_H_ include/0040755 0000000 0000000 00000000000 13756723631 011250 5ustar000000000 0000000 include/nativehelper/0040755 0000000 0000000 00000000000 13756723631 013736 5ustar000000000 0000000 include/nativehelper/JNIHelp.h0100644 0000000 0000000 00000024600 13756723631 015337 0ustar000000000 0000000 /* * Copyright (C) 2007 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. * * This file may be included by C or C++ code, which is trouble because jni.h * uses different typedefs for JNIEnv in each language. */ #ifndef NATIVEHELPER_JNIHELP_H_ #define NATIVEHELPER_JNIHELP_H_ #include #include #include #include "module_api.h" #ifndef NELEM # define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) #endif /* * Register one or more native methods with a particular class. * "className" looks like "java/lang/String". Aborts on failure. * TODO: fix all callers and change the return type to void. */ MODULE_API 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, so *an* exception will still be pending). * * Currently aborts the VM if it can't throw the exception. */ MODULE_API int jniThrowException(C_JNIEnv* env, const char* className, const char* msg); /* * Throw an exception with the specified class and formatted error 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, so *an* exception will still be pending). * * Currently aborts the VM if it can't throw the exception. */ MODULE_API int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args); /* * Throw a java.lang.NullPointerException, with an optional message. */ MODULE_API int jniThrowNullPointerException(C_JNIEnv* env, const char* msg); /* * Throw a java.lang.RuntimeException, with an optional message. */ MODULE_API int jniThrowRuntimeException(C_JNIEnv* env, const char* msg); /* * Throw a java.io.IOException, generating the message from errno. */ MODULE_API int jniThrowIOException(C_JNIEnv* env, int errnum); /* * Return a pointer to a locale-dependent error string explaining errno * value 'errnum'. The returned pointer may or may not be equal to 'buf'. * This function is thread-safe (unlike strerror) and portable (unlike * strerror_r). */ MODULE_API const char* jniStrError(int errnum, char* buf, size_t buflen); /* * Returns a new java.io.FileDescriptor for the given int fd. */ MODULE_API jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd); /* * Returns the int fd from a java.io.FileDescriptor. */ MODULE_API int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor); /* * Sets the int fd in a java.io.FileDescriptor. Throws java.lang.NullPointerException * if fileDescriptor is null. */ MODULE_API void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value); /* * Returns the long ownerId from a java.io.FileDescriptor. */ MODULE_API jlong jniGetOwnerIdFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor); /* * Gets the managed heap array backing a java.nio.Buffer instance. * * Returns nullptr if there is no array backing. * * This method performs a JNI call to java.nio.NIOAccess.getBaseArray(). */ MODULE_API jarray jniGetNioBufferBaseArray(C_JNIEnv* env, jobject nioBuffer); /* * Gets the offset in bytes from the start of the managed heap array backing the buffer. * * Returns 0 if there is no array backing. * * This method performs a JNI call to java.nio.NIOAccess.getBaseArrayOffset(). */ MODULE_API jint jniGetNioBufferBaseArrayOffset(C_JNIEnv* env, jobject nioBuffer); /* * Gets field information from a java.nio.Buffer instance. * * Reads the |position|, |limit|, and |elementSizeShift| fields from the buffer instance. * * Returns the |address| field of the java.nio.Buffer instance which is only valid (non-zero) when * the buffer is backed by a direct buffer. */ MODULE_API jlong jniGetNioBufferFields(C_JNIEnv* env, jobject nioBuffer, /*out*/jint* position, /*out*/jint* limit, /*out*/jint* elementSizeShift); /* * Gets the current position from a java.nio.Buffer as a pointer to memory in a fixed buffer. * * Returns 0 if |nioBuffer| is not backed by a direct buffer. * * This method reads the |address|, |position|, and |elementSizeShift| fields from the * java.nio.Buffer instance to calculate the pointer address for the current position. */ MODULE_API jlong jniGetNioBufferPointer(C_JNIEnv* env, jobject nioBuffer); /* * Returns the reference from a java.lang.ref.Reference. */ MODULE_API jobject jniGetReferent(C_JNIEnv* env, jobject ref); /* * Returns a Java String object created from UTF-16 data either from jchar or, * if called from C++11, char16_t (a bitwise identical distinct type). */ MODULE_API jstring jniCreateString(C_JNIEnv* env, const jchar* unicodeChars, jsize len); /* * Log a message and an exception. * If exception is NULL, logs the current exception in the JNI environment. */ MODULE_API void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception); /* * Clear the cache of constants libnativehelper is using. */ MODULE_API void jniUninitializeConstants(); /* * For C++ code, we provide inlines that map to the C functions. g++ always * 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 jniThrowException(JNIEnv* env, const char* className, const char* msg) { return jniThrowException(&env->functions, className, msg); } /* * 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, ...) { 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) { return jniThrowNullPointerException(&env->functions, msg); } inline int jniThrowRuntimeException(JNIEnv* env, const char* msg) { return jniThrowRuntimeException(&env->functions, msg); } inline int jniThrowIOException(JNIEnv* env, int errnum) { return jniThrowIOException(&env->functions, errnum); } inline jobject jniCreateFileDescriptor(JNIEnv* env, int fd) { return jniCreateFileDescriptor(&env->functions, fd); } inline int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) { return jniGetFDFromFileDescriptor(&env->functions, fileDescriptor); } inline void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor, int value) { jniSetFileDescriptorOfFD(&env->functions, fileDescriptor, value); } inline jlong jniGetOwnerIdFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) { return jniGetOwnerIdFromFileDescriptor(&env->functions, fileDescriptor); } inline jarray jniGetNioBufferBaseArray(JNIEnv* env, jobject nioBuffer) { return jniGetNioBufferBaseArray(&env->functions, nioBuffer); } inline jint jniGetNioBufferBaseArrayOffset(JNIEnv* env, jobject nioBuffer) { return jniGetNioBufferBaseArrayOffset(&env->functions, nioBuffer); } inline jlong jniGetNioBufferFields(JNIEnv* env, jobject nioBuffer, jint* position, jint* limit, jint* elementSizeShift) { return jniGetNioBufferFields(&env->functions, nioBuffer, position, limit, elementSizeShift); } inline jlong jniGetNioBufferPointer(JNIEnv* env, jobject nioBuffer) { return jniGetNioBufferPointer(&env->functions, nioBuffer); } inline jobject jniGetReferent(JNIEnv* env, jobject ref) { return jniGetReferent(&env->functions, ref); } inline jstring jniCreateString(JNIEnv* env, const jchar* unicodeChars, jsize len) { return jniCreateString(&env->functions, unicodeChars, len); } inline jstring jniCreateString(JNIEnv* env, const char16_t* unicodeChars, jsize len) { return jniCreateString(&env->functions, reinterpret_cast(unicodeChars), len); } inline void jniLogException(JNIEnv* env, int priority, const char* tag, jthrowable exception = NULL) { jniLogException(&env->functions, priority, tag, exception); } #if !defined(DISALLOW_COPY_AND_ASSIGN) // DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. It goes in the private: // declarations in a class. #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ TypeName(const TypeName&) = delete; \ void operator=(const TypeName&) = delete #endif // !defined(DISALLOW_COPY_AND_ASSIGN) #endif // defined(__cplusplus) /* * TEMP_FAILURE_RETRY is defined by some, but not all, versions of * . (Alas, it is not as standard as we'd hoped!) So, if it's * not already defined, then define it here. */ #ifndef TEMP_FAILURE_RETRY /* Used to retry syscalls that can return EINTR. */ #define TEMP_FAILURE_RETRY(exp) ({ \ typeof (exp) _rc; \ do { \ _rc = (exp); \ } while (_rc == -1 && errno == EINTR); \ _rc; }) #endif #endif /* NATIVEHELPER_JNIHELP_H_ */ include/nativehelper/JniInvocation.h0100644 0000000 0000000 00000005474 13756723631 016670 0ustar000000000 0000000 /* * Copyright (C) 2013 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. */ #ifndef JNI_INVOCATION_H_included #define JNI_INVOCATION_H_included #include #include "module_api.h" struct JniInvocationImpl; MODULE_API struct JniInvocationImpl* JniInvocationCreate(); MODULE_API void JniInvocationDestroy(struct JniInvocationImpl* instance); MODULE_API int JniInvocationInit(struct JniInvocationImpl* instance, const char* library); MODULE_API const char* JniInvocationGetLibrary(const char* library, char* buffer); #ifdef __cplusplus // JniInvocation adds a layer of indirection for applications using // the JNI invocation API to allow the JNI implementation to be // selected dynamically. Apps can specify a specific implementation to // be used by calling InitJniInvocation. If this is not done, the // library will chosen based on the value of Android system property // persist.sys.dalvik.vm.lib on the device, and otherwise fall back to // a hard-coded default implementation. class JniInvocation final { public: JniInvocation() { impl_ = JniInvocationCreate(); } ~JniInvocation() { JniInvocationDestroy(impl_); } // Initialize JNI invocation API. library should specifiy a valid // shared library for opening via dlopen providing a JNI invocation // implementation, or null to allow defaulting via // persist.sys.dalvik.vm.lib. bool Init(const char* library) { return JniInvocationInit(impl_, library) != 0; } // Exposes which library is actually loaded from the given name. The // buffer of size PROPERTY_VALUE_MAX will be used to load the system // property for the default library, if necessary. If no buffer is // provided, the fallback value will be used. static const char* GetLibrary(const char* library, char* buffer) { return JniInvocationGetLibrary(library, buffer); } private: JniInvocation(const JniInvocation&) = delete; JniInvocation& operator=(const JniInvocation&) = delete; static const char* GetLibrary(const char* library, char* buffer, bool (*is_debuggable)(), int (*get_library_system_property)(char* buffer)); JniInvocationImpl* impl_; friend class JNIInvocation_Debuggable_Test; friend class JNIInvocation_NonDebuggable_Test; }; #endif // __cplusplus #endif // JNI_INVOCATION_H_included include/nativehelper/ScopedBytes.h0100644 0000000 0000000 00000001416 13756723631 016332 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ #ifndef SCOPED_BYTES_H_included #define SCOPED_BYTES_H_included #include "JNIHelp.h" #include #endif // SCOPED_BYTES_H_included include/nativehelper/ScopedLocalFrame.h0100644 0000000 0000000 00000001424 13756723631 017250 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ #ifndef SCOPED_LOCAL_FRAME_H_included #define SCOPED_LOCAL_FRAME_H_included #include #endif // SCOPED_LOCAL_FRAME_H_included include/nativehelper/ScopedLocalRef.h0100644 0000000 0000000 00000001436 13756723631 016735 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ #ifndef SCOPED_LOCAL_REF_H_included #define SCOPED_LOCAL_REF_H_included #include "JNIHelp.h" #include #endif // SCOPED_LOCAL_REF_H_included include/nativehelper/ScopedPrimitiveArray.h0100644 0000000 0000000 00000001466 13756723631 020220 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ #ifndef SCOPED_PRIMITIVE_ARRAY_H_included #define SCOPED_PRIMITIVE_ARRAY_H_included #include "JNIHelp.h" #include #endif // SCOPED_PRIMITIVE_ARRAY_H_included include/nativehelper/ScopedStringChars.h0100644 0000000 0000000 00000001452 13756723631 017473 0ustar000000000 0000000 /* * Copyright (C) 2011 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. */ #ifndef SCOPED_STRING_CHARS_H_included #define SCOPED_STRING_CHARS_H_included #include "JNIHelp.h" #include #endif // SCOPED_STRING_CHARS_H_included include/nativehelper/ScopedUtfChars.h0100644 0000000 0000000 00000001436 13756723631 016765 0ustar000000000 0000000 /* * Copyright (C) 2010 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. */ #ifndef SCOPED_UTF_CHARS_H_included #define SCOPED_UTF_CHARS_H_included #include "JNIHelp.h" #include #endif // SCOPED_UTF_CHARS_H_included include/nativehelper/module_api.h0100644 0000000 0000000 00000001334 13756723631 016223 0ustar000000000 0000000 /* * Copyright (C) 2019 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. */ #pragma once #ifdef __cplusplus #define MODULE_API extern "C" #else #define MODULE_API #endif // __cplusplus include/nativehelper/toStringArray.h0100644 0000000 0000000 00000004542 13756723631 016721 0ustar000000000 0000000 /* * Copyright (C) 2011 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. */ #ifndef TO_STRING_ARRAY_H_included #define TO_STRING_ARRAY_H_included #include #include #include "module_api.h" // Public API for libnativehelper library. MODULE_API jobjectArray newStringArray(JNIEnv* env, size_t count); MODULE_API jobjectArray toStringArray(JNIEnv* env, const char* const* strings); #ifdef __cplusplus #include #include #include "ScopedLocalRef.h" template jobjectArray toStringArray(JNIEnv* env, Counter* counter, Getter* getter) { size_t count = (*counter)(); jobjectArray result = newStringArray(env, count); if (result == NULL) { return NULL; } for (size_t i = 0; i < count; ++i) { ScopedLocalRef s(env, env->NewStringUTF((*getter)(i))); if (env->ExceptionCheck()) { return NULL; } env->SetObjectArrayElement(result, i, s.get()); if (env->ExceptionCheck()) { return NULL; } } return result; } struct VectorCounter { const std::vector& strings; explicit VectorCounter(const std::vector& strings) : strings(strings) {} size_t operator()() { return strings.size(); } }; struct VectorGetter { const std::vector& strings; explicit VectorGetter(const std::vector& strings) : strings(strings) {} const char* operator()(size_t i) { return strings[i].c_str(); } }; inline jobjectArray toStringArray(JNIEnv* env, const std::vector& strings) { VectorCounter counter(strings); VectorGetter getter(strings); return toStringArray(env, &counter, &getter); } #endif // __cplusplus #endif // TO_STRING_ARRAY_H_included include_jni/0040755 0000000 0000000 00000000000 13756723631 012110 5ustar000000000 0000000 include_jni/jni.h0100644 0000000 0000000 00000146062 13756723631 013047 0ustar000000000 0000000 /* * 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 specification, as defined by Sun: * http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/jniTOC.html * * Everything here is expected to be VM-neutral. */ #ifndef JNI_H_ #define JNI_H_ #include #include /* Primitive types that match up with Java equivalents. */ typedef uint8_t jboolean; /* unsigned 8 bits */ typedef int8_t jbyte; /* signed 8 bits */ typedef uint16_t jchar; /* unsigned 16 bits */ typedef int16_t jshort; /* signed 16 bits */ typedef int32_t jint; /* signed 32 bits */ typedef int64_t jlong; /* signed 64 bits */ typedef float jfloat; /* 32-bit IEEE 754 */ typedef double jdouble; /* 64-bit IEEE 754 */ /* "cardinal indices and sizes" */ typedef jint jsize; #ifdef __cplusplus /* * Reference types, in C++ */ class _jobject {}; class _jclass : public _jobject {}; class _jstring : public _jobject {}; class _jarray : public _jobject {}; class _jobjectArray : public _jarray {}; class _jbooleanArray : public _jarray {}; class _jbyteArray : public _jarray {}; class _jcharArray : public _jarray {}; class _jshortArray : public _jarray {}; class _jintArray : public _jarray {}; class _jlongArray : public _jarray {}; class _jfloatArray : public _jarray {}; class _jdoubleArray : public _jarray {}; class _jthrowable : public _jobject {}; typedef _jobject* jobject; typedef _jclass* jclass; typedef _jstring* jstring; typedef _jarray* jarray; typedef _jobjectArray* jobjectArray; typedef _jbooleanArray* jbooleanArray; typedef _jbyteArray* jbyteArray; typedef _jcharArray* jcharArray; typedef _jshortArray* jshortArray; typedef _jintArray* jintArray; typedef _jlongArray* jlongArray; typedef _jfloatArray* jfloatArray; typedef _jdoubleArray* jdoubleArray; typedef _jthrowable* jthrowable; typedef _jobject* jweak; #else /* not __cplusplus */ /* * Reference types, in C. */ typedef void* jobject; typedef jobject jclass; typedef jobject jstring; typedef jobject jarray; typedef jarray jobjectArray; typedef jarray jbooleanArray; typedef jarray jbyteArray; typedef jarray jcharArray; typedef jarray jshortArray; typedef jarray jintArray; typedef jarray jlongArray; typedef jarray jfloatArray; typedef jarray jdoubleArray; typedef jobject jthrowable; typedef jobject jweak; #endif /* not __cplusplus */ struct _jfieldID; /* opaque structure */ typedef struct _jfieldID* jfieldID; /* field IDs */ struct _jmethodID; /* opaque structure */ typedef struct _jmethodID* jmethodID; /* method IDs */ struct JNIInvokeInterface; typedef union jvalue { jboolean z; jbyte b; jchar c; jshort s; jint i; jlong j; jfloat f; jdouble d; jobject l; } jvalue; typedef enum jobjectRefType { JNIInvalidRefType = 0, JNILocalRefType = 1, JNIGlobalRefType = 2, JNIWeakGlobalRefType = 3 } jobjectRefType; typedef struct { const char* name; const char* signature; void* fnPtr; } JNINativeMethod; struct _JNIEnv; struct _JavaVM; typedef const struct JNINativeInterface* C_JNIEnv; #if defined(__cplusplus) typedef _JNIEnv JNIEnv; typedef _JavaVM JavaVM; #else typedef const struct JNINativeInterface* JNIEnv; typedef const struct JNIInvokeInterface* JavaVM; #endif /* * Table of interface function pointers. */ struct JNINativeInterface { void* reserved0; void* reserved1; void* reserved2; void* reserved3; jint (*GetVersion)(JNIEnv *); jclass (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*, jsize); jclass (*FindClass)(JNIEnv*, const char*); jmethodID (*FromReflectedMethod)(JNIEnv*, jobject); jfieldID (*FromReflectedField)(JNIEnv*, jobject); /* spec doesn't show jboolean parameter */ jobject (*ToReflectedMethod)(JNIEnv*, jclass, jmethodID, jboolean); jclass (*GetSuperclass)(JNIEnv*, jclass); jboolean (*IsAssignableFrom)(JNIEnv*, jclass, jclass); /* spec doesn't show jboolean parameter */ jobject (*ToReflectedField)(JNIEnv*, jclass, jfieldID, jboolean); jint (*Throw)(JNIEnv*, jthrowable); jint (*ThrowNew)(JNIEnv *, jclass, const char *); jthrowable (*ExceptionOccurred)(JNIEnv*); void (*ExceptionDescribe)(JNIEnv*); void (*ExceptionClear)(JNIEnv*); void (*FatalError)(JNIEnv*, const char*); jint (*PushLocalFrame)(JNIEnv*, jint); jobject (*PopLocalFrame)(JNIEnv*, jobject); jobject (*NewGlobalRef)(JNIEnv*, jobject); void (*DeleteGlobalRef)(JNIEnv*, jobject); void (*DeleteLocalRef)(JNIEnv*, jobject); jboolean (*IsSameObject)(JNIEnv*, jobject, jobject); jobject (*NewLocalRef)(JNIEnv*, jobject); jint (*EnsureLocalCapacity)(JNIEnv*, jint); jobject (*AllocObject)(JNIEnv*, jclass); jobject (*NewObject)(JNIEnv*, jclass, jmethodID, ...); jobject (*NewObjectV)(JNIEnv*, jclass, jmethodID, va_list); jobject (*NewObjectA)(JNIEnv*, jclass, jmethodID, const jvalue*); jclass (*GetObjectClass)(JNIEnv*, jobject); jboolean (*IsInstanceOf)(JNIEnv*, jobject, jclass); jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); jobject (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...); jobject (*CallObjectMethodV)(JNIEnv*, jobject, jmethodID, va_list); jobject (*CallObjectMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*); jboolean (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...); jboolean (*CallBooleanMethodV)(JNIEnv*, jobject, jmethodID, va_list); jboolean (*CallBooleanMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*); jbyte (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...); jbyte (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list); jbyte (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*); jchar (*CallCharMethod)(JNIEnv*, jobject, jmethodID, ...); jchar (*CallCharMethodV)(JNIEnv*, jobject, jmethodID, va_list); jchar (*CallCharMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*); jshort (*CallShortMethod)(JNIEnv*, jobject, jmethodID, ...); jshort (*CallShortMethodV)(JNIEnv*, jobject, jmethodID, va_list); jshort (*CallShortMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*); jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...); jint (*CallIntMethodV)(JNIEnv*, jobject, jmethodID, va_list); jint (*CallIntMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*); jlong (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...); jlong (*CallLongMethodV)(JNIEnv*, jobject, jmethodID, va_list); jlong (*CallLongMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*); jfloat (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...); jfloat (*CallFloatMethodV)(JNIEnv*, jobject, jmethodID, va_list); jfloat (*CallFloatMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*); jdouble (*CallDoubleMethod)(JNIEnv*, jobject, jmethodID, ...); jdouble (*CallDoubleMethodV)(JNIEnv*, jobject, jmethodID, va_list); jdouble (*CallDoubleMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*); void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); void (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list); void (*CallVoidMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*); jobject (*CallNonvirtualObjectMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jobject (*CallNonvirtualObjectMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jobject (*CallNonvirtualObjectMethodA)(JNIEnv*, jobject, jclass, jmethodID, const jvalue*); jboolean (*CallNonvirtualBooleanMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jboolean (*CallNonvirtualBooleanMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jboolean (*CallNonvirtualBooleanMethodA)(JNIEnv*, jobject, jclass, jmethodID, const jvalue*); jbyte (*CallNonvirtualByteMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jbyte (*CallNonvirtualByteMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jbyte (*CallNonvirtualByteMethodA)(JNIEnv*, jobject, jclass, jmethodID, const jvalue*); jchar (*CallNonvirtualCharMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jchar (*CallNonvirtualCharMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jchar (*CallNonvirtualCharMethodA)(JNIEnv*, jobject, jclass, jmethodID, const jvalue*); jshort (*CallNonvirtualShortMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jshort (*CallNonvirtualShortMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jshort (*CallNonvirtualShortMethodA)(JNIEnv*, jobject, jclass, jmethodID, const jvalue*); jint (*CallNonvirtualIntMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jint (*CallNonvirtualIntMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jint (*CallNonvirtualIntMethodA)(JNIEnv*, jobject, jclass, jmethodID, const jvalue*); jlong (*CallNonvirtualLongMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jlong (*CallNonvirtualLongMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jlong (*CallNonvirtualLongMethodA)(JNIEnv*, jobject, jclass, jmethodID, const jvalue*); jfloat (*CallNonvirtualFloatMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jfloat (*CallNonvirtualFloatMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jfloat (*CallNonvirtualFloatMethodA)(JNIEnv*, jobject, jclass, jmethodID, const jvalue*); jdouble (*CallNonvirtualDoubleMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jdouble (*CallNonvirtualDoubleMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jdouble (*CallNonvirtualDoubleMethodA)(JNIEnv*, jobject, jclass, jmethodID, const jvalue*); void (*CallNonvirtualVoidMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); void (*CallNonvirtualVoidMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); void (*CallNonvirtualVoidMethodA)(JNIEnv*, jobject, jclass, jmethodID, const jvalue*); jfieldID (*GetFieldID)(JNIEnv*, jclass, const char*, const char*); jobject (*GetObjectField)(JNIEnv*, jobject, jfieldID); jboolean (*GetBooleanField)(JNIEnv*, jobject, jfieldID); jbyte (*GetByteField)(JNIEnv*, jobject, jfieldID); jchar (*GetCharField)(JNIEnv*, jobject, jfieldID); jshort (*GetShortField)(JNIEnv*, jobject, jfieldID); jint (*GetIntField)(JNIEnv*, jobject, jfieldID); jlong (*GetLongField)(JNIEnv*, jobject, jfieldID); jfloat (*GetFloatField)(JNIEnv*, jobject, jfieldID); jdouble (*GetDoubleField)(JNIEnv*, jobject, jfieldID); void (*SetObjectField)(JNIEnv*, jobject, jfieldID, jobject); void (*SetBooleanField)(JNIEnv*, jobject, jfieldID, jboolean); void (*SetByteField)(JNIEnv*, jobject, jfieldID, jbyte); void (*SetCharField)(JNIEnv*, jobject, jfieldID, jchar); void (*SetShortField)(JNIEnv*, jobject, jfieldID, jshort); void (*SetIntField)(JNIEnv*, jobject, jfieldID, jint); void (*SetLongField)(JNIEnv*, jobject, jfieldID, jlong); void (*SetFloatField)(JNIEnv*, jobject, jfieldID, jfloat); void (*SetDoubleField)(JNIEnv*, jobject, jfieldID, jdouble); jmethodID (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*); jobject (*CallStaticObjectMethod)(JNIEnv*, jclass, jmethodID, ...); jobject (*CallStaticObjectMethodV)(JNIEnv*, jclass, jmethodID, va_list); jobject (*CallStaticObjectMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*); jboolean (*CallStaticBooleanMethod)(JNIEnv*, jclass, jmethodID, ...); jboolean (*CallStaticBooleanMethodV)(JNIEnv*, jclass, jmethodID, va_list); jboolean (*CallStaticBooleanMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*); jbyte (*CallStaticByteMethod)(JNIEnv*, jclass, jmethodID, ...); jbyte (*CallStaticByteMethodV)(JNIEnv*, jclass, jmethodID, va_list); jbyte (*CallStaticByteMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*); jchar (*CallStaticCharMethod)(JNIEnv*, jclass, jmethodID, ...); jchar (*CallStaticCharMethodV)(JNIEnv*, jclass, jmethodID, va_list); jchar (*CallStaticCharMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*); jshort (*CallStaticShortMethod)(JNIEnv*, jclass, jmethodID, ...); jshort (*CallStaticShortMethodV)(JNIEnv*, jclass, jmethodID, va_list); jshort (*CallStaticShortMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*); jint (*CallStaticIntMethod)(JNIEnv*, jclass, jmethodID, ...); jint (*CallStaticIntMethodV)(JNIEnv*, jclass, jmethodID, va_list); jint (*CallStaticIntMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*); jlong (*CallStaticLongMethod)(JNIEnv*, jclass, jmethodID, ...); jlong (*CallStaticLongMethodV)(JNIEnv*, jclass, jmethodID, va_list); jlong (*CallStaticLongMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*); jfloat (*CallStaticFloatMethod)(JNIEnv*, jclass, jmethodID, ...); jfloat (*CallStaticFloatMethodV)(JNIEnv*, jclass, jmethodID, va_list); jfloat (*CallStaticFloatMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*); jdouble (*CallStaticDoubleMethod)(JNIEnv*, jclass, jmethodID, ...); jdouble (*CallStaticDoubleMethodV)(JNIEnv*, jclass, jmethodID, va_list); jdouble (*CallStaticDoubleMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*); void (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...); void (*CallStaticVoidMethodV)(JNIEnv*, jclass, jmethodID, va_list); void (*CallStaticVoidMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*); jfieldID (*GetStaticFieldID)(JNIEnv*, jclass, const char*, const char*); jobject (*GetStaticObjectField)(JNIEnv*, jclass, jfieldID); jboolean (*GetStaticBooleanField)(JNIEnv*, jclass, jfieldID); jbyte (*GetStaticByteField)(JNIEnv*, jclass, jfieldID); jchar (*GetStaticCharField)(JNIEnv*, jclass, jfieldID); jshort (*GetStaticShortField)(JNIEnv*, jclass, jfieldID); jint (*GetStaticIntField)(JNIEnv*, jclass, jfieldID); jlong (*GetStaticLongField)(JNIEnv*, jclass, jfieldID); jfloat (*GetStaticFloatField)(JNIEnv*, jclass, jfieldID); jdouble (*GetStaticDoubleField)(JNIEnv*, jclass, jfieldID); void (*SetStaticObjectField)(JNIEnv*, jclass, jfieldID, jobject); void (*SetStaticBooleanField)(JNIEnv*, jclass, jfieldID, jboolean); void (*SetStaticByteField)(JNIEnv*, jclass, jfieldID, jbyte); void (*SetStaticCharField)(JNIEnv*, jclass, jfieldID, jchar); void (*SetStaticShortField)(JNIEnv*, jclass, jfieldID, jshort); void (*SetStaticIntField)(JNIEnv*, jclass, jfieldID, jint); void (*SetStaticLongField)(JNIEnv*, jclass, jfieldID, jlong); void (*SetStaticFloatField)(JNIEnv*, jclass, jfieldID, jfloat); void (*SetStaticDoubleField)(JNIEnv*, jclass, jfieldID, jdouble); jstring (*NewString)(JNIEnv*, const jchar*, jsize); jsize (*GetStringLength)(JNIEnv*, jstring); const jchar* (*GetStringChars)(JNIEnv*, jstring, jboolean*); void (*ReleaseStringChars)(JNIEnv*, jstring, const jchar*); jstring (*NewStringUTF)(JNIEnv*, const char*); jsize (*GetStringUTFLength)(JNIEnv*, jstring); /* JNI spec says this returns const jbyte*, but that's inconsistent */ const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*); void (*ReleaseStringUTFChars)(JNIEnv*, jstring, const char*); jsize (*GetArrayLength)(JNIEnv*, jarray); jobjectArray (*NewObjectArray)(JNIEnv*, jsize, jclass, jobject); jobject (*GetObjectArrayElement)(JNIEnv*, jobjectArray, jsize); void (*SetObjectArrayElement)(JNIEnv*, jobjectArray, jsize, jobject); jbooleanArray (*NewBooleanArray)(JNIEnv*, jsize); jbyteArray (*NewByteArray)(JNIEnv*, jsize); jcharArray (*NewCharArray)(JNIEnv*, jsize); jshortArray (*NewShortArray)(JNIEnv*, jsize); jintArray (*NewIntArray)(JNIEnv*, jsize); jlongArray (*NewLongArray)(JNIEnv*, jsize); jfloatArray (*NewFloatArray)(JNIEnv*, jsize); jdoubleArray (*NewDoubleArray)(JNIEnv*, jsize); jboolean* (*GetBooleanArrayElements)(JNIEnv*, jbooleanArray, jboolean*); jbyte* (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*); jchar* (*GetCharArrayElements)(JNIEnv*, jcharArray, jboolean*); jshort* (*GetShortArrayElements)(JNIEnv*, jshortArray, jboolean*); jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*); jlong* (*GetLongArrayElements)(JNIEnv*, jlongArray, jboolean*); jfloat* (*GetFloatArrayElements)(JNIEnv*, jfloatArray, jboolean*); jdouble* (*GetDoubleArrayElements)(JNIEnv*, jdoubleArray, jboolean*); void (*ReleaseBooleanArrayElements)(JNIEnv*, jbooleanArray, jboolean*, jint); void (*ReleaseByteArrayElements)(JNIEnv*, jbyteArray, jbyte*, jint); void (*ReleaseCharArrayElements)(JNIEnv*, jcharArray, jchar*, jint); void (*ReleaseShortArrayElements)(JNIEnv*, jshortArray, jshort*, jint); void (*ReleaseIntArrayElements)(JNIEnv*, jintArray, jint*, jint); void (*ReleaseLongArrayElements)(JNIEnv*, jlongArray, jlong*, jint); void (*ReleaseFloatArrayElements)(JNIEnv*, jfloatArray, jfloat*, jint); void (*ReleaseDoubleArrayElements)(JNIEnv*, jdoubleArray, jdouble*, jint); void (*GetBooleanArrayRegion)(JNIEnv*, jbooleanArray, jsize, jsize, jboolean*); void (*GetByteArrayRegion)(JNIEnv*, jbyteArray, jsize, jsize, jbyte*); void (*GetCharArrayRegion)(JNIEnv*, jcharArray, jsize, jsize, jchar*); void (*GetShortArrayRegion)(JNIEnv*, jshortArray, jsize, jsize, jshort*); void (*GetIntArrayRegion)(JNIEnv*, jintArray, jsize, jsize, jint*); void (*GetLongArrayRegion)(JNIEnv*, jlongArray, jsize, jsize, jlong*); void (*GetFloatArrayRegion)(JNIEnv*, jfloatArray, jsize, jsize, jfloat*); void (*GetDoubleArrayRegion)(JNIEnv*, jdoubleArray, jsize, jsize, jdouble*); /* spec shows these without const; some jni.h do, some don't */ void (*SetBooleanArrayRegion)(JNIEnv*, jbooleanArray, jsize, jsize, const jboolean*); void (*SetByteArrayRegion)(JNIEnv*, jbyteArray, jsize, jsize, const jbyte*); void (*SetCharArrayRegion)(JNIEnv*, jcharArray, jsize, jsize, const jchar*); void (*SetShortArrayRegion)(JNIEnv*, jshortArray, jsize, jsize, const jshort*); void (*SetIntArrayRegion)(JNIEnv*, jintArray, jsize, jsize, const jint*); void (*SetLongArrayRegion)(JNIEnv*, jlongArray, jsize, jsize, const jlong*); void (*SetFloatArrayRegion)(JNIEnv*, jfloatArray, jsize, jsize, const jfloat*); void (*SetDoubleArrayRegion)(JNIEnv*, jdoubleArray, jsize, jsize, const jdouble*); jint (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*, jint); jint (*UnregisterNatives)(JNIEnv*, jclass); jint (*MonitorEnter)(JNIEnv*, jobject); jint (*MonitorExit)(JNIEnv*, jobject); jint (*GetJavaVM)(JNIEnv*, JavaVM**); void (*GetStringRegion)(JNIEnv*, jstring, jsize, jsize, jchar*); void (*GetStringUTFRegion)(JNIEnv*, jstring, jsize, jsize, char*); void* (*GetPrimitiveArrayCritical)(JNIEnv*, jarray, jboolean*); void (*ReleasePrimitiveArrayCritical)(JNIEnv*, jarray, void*, jint); const jchar* (*GetStringCritical)(JNIEnv*, jstring, jboolean*); void (*ReleaseStringCritical)(JNIEnv*, jstring, const jchar*); jweak (*NewWeakGlobalRef)(JNIEnv*, jobject); void (*DeleteWeakGlobalRef)(JNIEnv*, jweak); jboolean (*ExceptionCheck)(JNIEnv*); jobject (*NewDirectByteBuffer)(JNIEnv*, void*, jlong); void* (*GetDirectBufferAddress)(JNIEnv*, jobject); jlong (*GetDirectBufferCapacity)(JNIEnv*, jobject); /* added in JNI 1.6 */ jobjectRefType (*GetObjectRefType)(JNIEnv*, jobject); }; /* * C++ object wrapper. * * This is usually overlaid on a C struct whose first element is a * JNINativeInterface*. We rely somewhat on compiler behavior. */ struct _JNIEnv { /* do not rename this; it does not seem to be entirely opaque */ const struct JNINativeInterface* functions; #if defined(__cplusplus) jint GetVersion() { return functions->GetVersion(this); } jclass DefineClass(const char *name, jobject loader, const jbyte* buf, jsize bufLen) { return functions->DefineClass(this, name, loader, buf, bufLen); } jclass FindClass(const char* name) { return functions->FindClass(this, name); } jmethodID FromReflectedMethod(jobject method) { return functions->FromReflectedMethod(this, method); } jfieldID FromReflectedField(jobject field) { return functions->FromReflectedField(this, field); } jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic) { return functions->ToReflectedMethod(this, cls, methodID, isStatic); } jclass GetSuperclass(jclass clazz) { return functions->GetSuperclass(this, clazz); } jboolean IsAssignableFrom(jclass clazz1, jclass clazz2) { return functions->IsAssignableFrom(this, clazz1, clazz2); } jobject ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic) { return functions->ToReflectedField(this, cls, fieldID, isStatic); } jint Throw(jthrowable obj) { return functions->Throw(this, obj); } jint ThrowNew(jclass clazz, const char* message) { return functions->ThrowNew(this, clazz, message); } jthrowable ExceptionOccurred() { return functions->ExceptionOccurred(this); } void ExceptionDescribe() { functions->ExceptionDescribe(this); } void ExceptionClear() { functions->ExceptionClear(this); } void FatalError(const char* msg) { functions->FatalError(this, msg); } jint PushLocalFrame(jint capacity) { return functions->PushLocalFrame(this, capacity); } jobject PopLocalFrame(jobject result) { return functions->PopLocalFrame(this, result); } jobject NewGlobalRef(jobject obj) { return functions->NewGlobalRef(this, obj); } void DeleteGlobalRef(jobject globalRef) { functions->DeleteGlobalRef(this, globalRef); } void DeleteLocalRef(jobject localRef) { functions->DeleteLocalRef(this, localRef); } jboolean IsSameObject(jobject ref1, jobject ref2) { return functions->IsSameObject(this, ref1, ref2); } jobject NewLocalRef(jobject ref) { return functions->NewLocalRef(this, ref); } jint EnsureLocalCapacity(jint capacity) { return functions->EnsureLocalCapacity(this, capacity); } jobject AllocObject(jclass clazz) { return functions->AllocObject(this, clazz); } jobject NewObject(jclass clazz, jmethodID methodID, ...) { va_list args; va_start(args, methodID); jobject result = functions->NewObjectV(this, clazz, methodID, args); va_end(args); return result; } jobject NewObjectV(jclass clazz, jmethodID methodID, va_list args) { return functions->NewObjectV(this, clazz, methodID, args); } jobject NewObjectA(jclass clazz, jmethodID methodID, const jvalue* args) { return functions->NewObjectA(this, clazz, methodID, args); } jclass GetObjectClass(jobject obj) { return functions->GetObjectClass(this, obj); } jboolean IsInstanceOf(jobject obj, jclass clazz) { return functions->IsInstanceOf(this, obj, clazz); } jmethodID GetMethodID(jclass clazz, const char* name, const char* sig) { return functions->GetMethodID(this, clazz, name, sig); } #define CALL_TYPE_METHOD(_jtype, _jname) \ _jtype Call##_jname##Method(jobject obj, jmethodID methodID, ...) \ { \ _jtype result; \ va_list args; \ va_start(args, methodID); \ result = functions->Call##_jname##MethodV(this, obj, methodID, \ args); \ va_end(args); \ return result; \ } #define CALL_TYPE_METHODV(_jtype, _jname) \ _jtype Call##_jname##MethodV(jobject obj, jmethodID methodID, \ va_list args) \ { return functions->Call##_jname##MethodV(this, obj, methodID, args); } #define CALL_TYPE_METHODA(_jtype, _jname) \ _jtype Call##_jname##MethodA(jobject obj, jmethodID methodID, \ const jvalue* args) \ { return functions->Call##_jname##MethodA(this, obj, methodID, args); } #define CALL_TYPE(_jtype, _jname) \ CALL_TYPE_METHOD(_jtype, _jname) \ CALL_TYPE_METHODV(_jtype, _jname) \ CALL_TYPE_METHODA(_jtype, _jname) CALL_TYPE(jobject, Object) CALL_TYPE(jboolean, Boolean) CALL_TYPE(jbyte, Byte) CALL_TYPE(jchar, Char) CALL_TYPE(jshort, Short) CALL_TYPE(jint, Int) CALL_TYPE(jlong, Long) CALL_TYPE(jfloat, Float) CALL_TYPE(jdouble, Double) void CallVoidMethod(jobject obj, jmethodID methodID, ...) { va_list args; va_start(args, methodID); functions->CallVoidMethodV(this, obj, methodID, args); va_end(args); } void CallVoidMethodV(jobject obj, jmethodID methodID, va_list args) { functions->CallVoidMethodV(this, obj, methodID, args); } void CallVoidMethodA(jobject obj, jmethodID methodID, const jvalue* args) { functions->CallVoidMethodA(this, obj, methodID, args); } #define CALL_NONVIRT_TYPE_METHOD(_jtype, _jname) \ _jtype CallNonvirtual##_jname##Method(jobject obj, jclass clazz, \ jmethodID methodID, ...) \ { \ _jtype result; \ va_list args; \ va_start(args, methodID); \ result = functions->CallNonvirtual##_jname##MethodV(this, obj, \ clazz, methodID, args); \ va_end(args); \ return result; \ } #define CALL_NONVIRT_TYPE_METHODV(_jtype, _jname) \ _jtype CallNonvirtual##_jname##MethodV(jobject obj, jclass clazz, \ jmethodID methodID, va_list args) \ { return functions->CallNonvirtual##_jname##MethodV(this, obj, clazz, \ methodID, args); } #define CALL_NONVIRT_TYPE_METHODA(_jtype, _jname) \ _jtype CallNonvirtual##_jname##MethodA(jobject obj, jclass clazz, \ jmethodID methodID, const jvalue* args) \ { return functions->CallNonvirtual##_jname##MethodA(this, obj, clazz, \ methodID, args); } #define CALL_NONVIRT_TYPE(_jtype, _jname) \ CALL_NONVIRT_TYPE_METHOD(_jtype, _jname) \ CALL_NONVIRT_TYPE_METHODV(_jtype, _jname) \ CALL_NONVIRT_TYPE_METHODA(_jtype, _jname) CALL_NONVIRT_TYPE(jobject, Object) CALL_NONVIRT_TYPE(jboolean, Boolean) CALL_NONVIRT_TYPE(jbyte, Byte) CALL_NONVIRT_TYPE(jchar, Char) CALL_NONVIRT_TYPE(jshort, Short) CALL_NONVIRT_TYPE(jint, Int) CALL_NONVIRT_TYPE(jlong, Long) CALL_NONVIRT_TYPE(jfloat, Float) CALL_NONVIRT_TYPE(jdouble, Double) void CallNonvirtualVoidMethod(jobject obj, jclass clazz, jmethodID methodID, ...) { va_list args; va_start(args, methodID); functions->CallNonvirtualVoidMethodV(this, obj, clazz, methodID, args); va_end(args); } void CallNonvirtualVoidMethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args) { functions->CallNonvirtualVoidMethodV(this, obj, clazz, methodID, args); } void CallNonvirtualVoidMethodA(jobject obj, jclass clazz, jmethodID methodID, const jvalue* args) { functions->CallNonvirtualVoidMethodA(this, obj, clazz, methodID, args); } jfieldID GetFieldID(jclass clazz, const char* name, const char* sig) { return functions->GetFieldID(this, clazz, name, sig); } jobject GetObjectField(jobject obj, jfieldID fieldID) { return functions->GetObjectField(this, obj, fieldID); } jboolean GetBooleanField(jobject obj, jfieldID fieldID) { return functions->GetBooleanField(this, obj, fieldID); } jbyte GetByteField(jobject obj, jfieldID fieldID) { return functions->GetByteField(this, obj, fieldID); } jchar GetCharField(jobject obj, jfieldID fieldID) { return functions->GetCharField(this, obj, fieldID); } jshort GetShortField(jobject obj, jfieldID fieldID) { return functions->GetShortField(this, obj, fieldID); } jint GetIntField(jobject obj, jfieldID fieldID) { return functions->GetIntField(this, obj, fieldID); } jlong GetLongField(jobject obj, jfieldID fieldID) { return functions->GetLongField(this, obj, fieldID); } jfloat GetFloatField(jobject obj, jfieldID fieldID) { return functions->GetFloatField(this, obj, fieldID); } jdouble GetDoubleField(jobject obj, jfieldID fieldID) { return functions->GetDoubleField(this, obj, fieldID); } void SetObjectField(jobject obj, jfieldID fieldID, jobject value) { functions->SetObjectField(this, obj, fieldID, value); } void SetBooleanField(jobject obj, jfieldID fieldID, jboolean value) { functions->SetBooleanField(this, obj, fieldID, value); } void SetByteField(jobject obj, jfieldID fieldID, jbyte value) { functions->SetByteField(this, obj, fieldID, value); } void SetCharField(jobject obj, jfieldID fieldID, jchar value) { functions->SetCharField(this, obj, fieldID, value); } void SetShortField(jobject obj, jfieldID fieldID, jshort value) { functions->SetShortField(this, obj, fieldID, value); } void SetIntField(jobject obj, jfieldID fieldID, jint value) { functions->SetIntField(this, obj, fieldID, value); } void SetLongField(jobject obj, jfieldID fieldID, jlong value) { functions->SetLongField(this, obj, fieldID, value); } void SetFloatField(jobject obj, jfieldID fieldID, jfloat value) { functions->SetFloatField(this, obj, fieldID, value); } void SetDoubleField(jobject obj, jfieldID fieldID, jdouble value) { functions->SetDoubleField(this, obj, fieldID, value); } jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig) { return functions->GetStaticMethodID(this, clazz, name, sig); } #define CALL_STATIC_TYPE_METHOD(_jtype, _jname) \ _jtype CallStatic##_jname##Method(jclass clazz, jmethodID methodID, \ ...) \ { \ _jtype result; \ va_list args; \ va_start(args, methodID); \ result = functions->CallStatic##_jname##MethodV(this, clazz, \ methodID, args); \ va_end(args); \ return result; \ } #define CALL_STATIC_TYPE_METHODV(_jtype, _jname) \ _jtype CallStatic##_jname##MethodV(jclass clazz, jmethodID methodID, \ va_list args) \ { return functions->CallStatic##_jname##MethodV(this, clazz, methodID, \ args); } #define CALL_STATIC_TYPE_METHODA(_jtype, _jname) \ _jtype CallStatic##_jname##MethodA(jclass clazz, jmethodID methodID, \ const jvalue* args) \ { return functions->CallStatic##_jname##MethodA(this, clazz, methodID, \ args); } #define CALL_STATIC_TYPE(_jtype, _jname) \ CALL_STATIC_TYPE_METHOD(_jtype, _jname) \ CALL_STATIC_TYPE_METHODV(_jtype, _jname) \ CALL_STATIC_TYPE_METHODA(_jtype, _jname) CALL_STATIC_TYPE(jobject, Object) CALL_STATIC_TYPE(jboolean, Boolean) CALL_STATIC_TYPE(jbyte, Byte) CALL_STATIC_TYPE(jchar, Char) CALL_STATIC_TYPE(jshort, Short) CALL_STATIC_TYPE(jint, Int) CALL_STATIC_TYPE(jlong, Long) CALL_STATIC_TYPE(jfloat, Float) CALL_STATIC_TYPE(jdouble, Double) void CallStaticVoidMethod(jclass clazz, jmethodID methodID, ...) { va_list args; va_start(args, methodID); functions->CallStaticVoidMethodV(this, clazz, methodID, args); va_end(args); } void CallStaticVoidMethodV(jclass clazz, jmethodID methodID, va_list args) { functions->CallStaticVoidMethodV(this, clazz, methodID, args); } void CallStaticVoidMethodA(jclass clazz, jmethodID methodID, const jvalue* args) { functions->CallStaticVoidMethodA(this, clazz, methodID, args); } jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig) { return functions->GetStaticFieldID(this, clazz, name, sig); } jobject GetStaticObjectField(jclass clazz, jfieldID fieldID) { return functions->GetStaticObjectField(this, clazz, fieldID); } jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID) { return functions->GetStaticBooleanField(this, clazz, fieldID); } jbyte GetStaticByteField(jclass clazz, jfieldID fieldID) { return functions->GetStaticByteField(this, clazz, fieldID); } jchar GetStaticCharField(jclass clazz, jfieldID fieldID) { return functions->GetStaticCharField(this, clazz, fieldID); } jshort GetStaticShortField(jclass clazz, jfieldID fieldID) { return functions->GetStaticShortField(this, clazz, fieldID); } jint GetStaticIntField(jclass clazz, jfieldID fieldID) { return functions->GetStaticIntField(this, clazz, fieldID); } jlong GetStaticLongField(jclass clazz, jfieldID fieldID) { return functions->GetStaticLongField(this, clazz, fieldID); } jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID) { return functions->GetStaticFloatField(this, clazz, fieldID); } jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID) { return functions->GetStaticDoubleField(this, clazz, fieldID); } void SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value) { functions->SetStaticObjectField(this, clazz, fieldID, value); } void SetStaticBooleanField(jclass clazz, jfieldID fieldID, jboolean value) { functions->SetStaticBooleanField(this, clazz, fieldID, value); } void SetStaticByteField(jclass clazz, jfieldID fieldID, jbyte value) { functions->SetStaticByteField(this, clazz, fieldID, value); } void SetStaticCharField(jclass clazz, jfieldID fieldID, jchar value) { functions->SetStaticCharField(this, clazz, fieldID, value); } void SetStaticShortField(jclass clazz, jfieldID fieldID, jshort value) { functions->SetStaticShortField(this, clazz, fieldID, value); } void SetStaticIntField(jclass clazz, jfieldID fieldID, jint value) { functions->SetStaticIntField(this, clazz, fieldID, value); } void SetStaticLongField(jclass clazz, jfieldID fieldID, jlong value) { functions->SetStaticLongField(this, clazz, fieldID, value); } void SetStaticFloatField(jclass clazz, jfieldID fieldID, jfloat value) { functions->SetStaticFloatField(this, clazz, fieldID, value); } void SetStaticDoubleField(jclass clazz, jfieldID fieldID, jdouble value) { functions->SetStaticDoubleField(this, clazz, fieldID, value); } jstring NewString(const jchar* unicodeChars, jsize len) { return functions->NewString(this, unicodeChars, len); } jsize GetStringLength(jstring string) { return functions->GetStringLength(this, string); } const jchar* GetStringChars(jstring string, jboolean* isCopy) { return functions->GetStringChars(this, string, isCopy); } void ReleaseStringChars(jstring string, const jchar* chars) { functions->ReleaseStringChars(this, string, chars); } jstring NewStringUTF(const char* bytes) { return functions->NewStringUTF(this, bytes); } jsize GetStringUTFLength(jstring string) { return functions->GetStringUTFLength(this, string); } const char* GetStringUTFChars(jstring string, jboolean* isCopy) { return functions->GetStringUTFChars(this, string, isCopy); } void ReleaseStringUTFChars(jstring string, const char* utf) { functions->ReleaseStringUTFChars(this, string, utf); } jsize GetArrayLength(jarray array) { return functions->GetArrayLength(this, array); } jobjectArray NewObjectArray(jsize length, jclass elementClass, jobject initialElement) { return functions->NewObjectArray(this, length, elementClass, initialElement); } jobject GetObjectArrayElement(jobjectArray array, jsize index) { return functions->GetObjectArrayElement(this, array, index); } void SetObjectArrayElement(jobjectArray array, jsize index, jobject value) { functions->SetObjectArrayElement(this, array, index, value); } jbooleanArray NewBooleanArray(jsize length) { return functions->NewBooleanArray(this, length); } jbyteArray NewByteArray(jsize length) { return functions->NewByteArray(this, length); } jcharArray NewCharArray(jsize length) { return functions->NewCharArray(this, length); } jshortArray NewShortArray(jsize length) { return functions->NewShortArray(this, length); } jintArray NewIntArray(jsize length) { return functions->NewIntArray(this, length); } jlongArray NewLongArray(jsize length) { return functions->NewLongArray(this, length); } jfloatArray NewFloatArray(jsize length) { return functions->NewFloatArray(this, length); } jdoubleArray NewDoubleArray(jsize length) { return functions->NewDoubleArray(this, length); } jboolean* GetBooleanArrayElements(jbooleanArray array, jboolean* isCopy) { return functions->GetBooleanArrayElements(this, array, isCopy); } jbyte* GetByteArrayElements(jbyteArray array, jboolean* isCopy) { return functions->GetByteArrayElements(this, array, isCopy); } jchar* GetCharArrayElements(jcharArray array, jboolean* isCopy) { return functions->GetCharArrayElements(this, array, isCopy); } jshort* GetShortArrayElements(jshortArray array, jboolean* isCopy) { return functions->GetShortArrayElements(this, array, isCopy); } jint* GetIntArrayElements(jintArray array, jboolean* isCopy) { return functions->GetIntArrayElements(this, array, isCopy); } jlong* GetLongArrayElements(jlongArray array, jboolean* isCopy) { return functions->GetLongArrayElements(this, array, isCopy); } jfloat* GetFloatArrayElements(jfloatArray array, jboolean* isCopy) { return functions->GetFloatArrayElements(this, array, isCopy); } jdouble* GetDoubleArrayElements(jdoubleArray array, jboolean* isCopy) { return functions->GetDoubleArrayElements(this, array, isCopy); } void ReleaseBooleanArrayElements(jbooleanArray array, jboolean* elems, jint mode) { functions->ReleaseBooleanArrayElements(this, array, elems, mode); } void ReleaseByteArrayElements(jbyteArray array, jbyte* elems, jint mode) { functions->ReleaseByteArrayElements(this, array, elems, mode); } void ReleaseCharArrayElements(jcharArray array, jchar* elems, jint mode) { functions->ReleaseCharArrayElements(this, array, elems, mode); } void ReleaseShortArrayElements(jshortArray array, jshort* elems, jint mode) { functions->ReleaseShortArrayElements(this, array, elems, mode); } void ReleaseIntArrayElements(jintArray array, jint* elems, jint mode) { functions->ReleaseIntArrayElements(this, array, elems, mode); } void ReleaseLongArrayElements(jlongArray array, jlong* elems, jint mode) { functions->ReleaseLongArrayElements(this, array, elems, mode); } void ReleaseFloatArrayElements(jfloatArray array, jfloat* elems, jint mode) { functions->ReleaseFloatArrayElements(this, array, elems, mode); } void ReleaseDoubleArrayElements(jdoubleArray array, jdouble* elems, jint mode) { functions->ReleaseDoubleArrayElements(this, array, elems, mode); } void GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, jboolean* buf) { functions->GetBooleanArrayRegion(this, array, start, len, buf); } void GetByteArrayRegion(jbyteArray array, jsize start, jsize len, jbyte* buf) { functions->GetByteArrayRegion(this, array, start, len, buf); } void GetCharArrayRegion(jcharArray array, jsize start, jsize len, jchar* buf) { functions->GetCharArrayRegion(this, array, start, len, buf); } void GetShortArrayRegion(jshortArray array, jsize start, jsize len, jshort* buf) { functions->GetShortArrayRegion(this, array, start, len, buf); } void GetIntArrayRegion(jintArray array, jsize start, jsize len, jint* buf) { functions->GetIntArrayRegion(this, array, start, len, buf); } void GetLongArrayRegion(jlongArray array, jsize start, jsize len, jlong* buf) { functions->GetLongArrayRegion(this, array, start, len, buf); } void GetFloatArrayRegion(jfloatArray array, jsize start, jsize len, jfloat* buf) { functions->GetFloatArrayRegion(this, array, start, len, buf); } void GetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, jdouble* buf) { functions->GetDoubleArrayRegion(this, array, start, len, buf); } void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, const jboolean* buf) { functions->SetBooleanArrayRegion(this, array, start, len, buf); } void SetByteArrayRegion(jbyteArray array, jsize start, jsize len, const jbyte* buf) { functions->SetByteArrayRegion(this, array, start, len, buf); } void SetCharArrayRegion(jcharArray array, jsize start, jsize len, const jchar* buf) { functions->SetCharArrayRegion(this, array, start, len, buf); } void SetShortArrayRegion(jshortArray array, jsize start, jsize len, const jshort* buf) { functions->SetShortArrayRegion(this, array, start, len, buf); } void SetIntArrayRegion(jintArray array, jsize start, jsize len, const jint* buf) { functions->SetIntArrayRegion(this, array, start, len, buf); } void SetLongArrayRegion(jlongArray array, jsize start, jsize len, const jlong* buf) { functions->SetLongArrayRegion(this, array, start, len, buf); } void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, const jfloat* buf) { functions->SetFloatArrayRegion(this, array, start, len, buf); } void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, const jdouble* buf) { functions->SetDoubleArrayRegion(this, array, start, len, buf); } jint RegisterNatives(jclass clazz, const JNINativeMethod* methods, jint nMethods) { return functions->RegisterNatives(this, clazz, methods, nMethods); } jint UnregisterNatives(jclass clazz) { return functions->UnregisterNatives(this, clazz); } jint MonitorEnter(jobject obj) { return functions->MonitorEnter(this, obj); } jint MonitorExit(jobject obj) { return functions->MonitorExit(this, obj); } jint GetJavaVM(JavaVM** vm) { return functions->GetJavaVM(this, vm); } void GetStringRegion(jstring str, jsize start, jsize len, jchar* buf) { functions->GetStringRegion(this, str, start, len, buf); } void GetStringUTFRegion(jstring str, jsize start, jsize len, char* buf) { return functions->GetStringUTFRegion(this, str, start, len, buf); } void* GetPrimitiveArrayCritical(jarray array, jboolean* isCopy) { return functions->GetPrimitiveArrayCritical(this, array, isCopy); } void ReleasePrimitiveArrayCritical(jarray array, void* carray, jint mode) { functions->ReleasePrimitiveArrayCritical(this, array, carray, mode); } const jchar* GetStringCritical(jstring string, jboolean* isCopy) { return functions->GetStringCritical(this, string, isCopy); } void ReleaseStringCritical(jstring string, const jchar* carray) { functions->ReleaseStringCritical(this, string, carray); } jweak NewWeakGlobalRef(jobject obj) { return functions->NewWeakGlobalRef(this, obj); } void DeleteWeakGlobalRef(jweak obj) { functions->DeleteWeakGlobalRef(this, obj); } jboolean ExceptionCheck() { return functions->ExceptionCheck(this); } jobject NewDirectByteBuffer(void* address, jlong capacity) { return functions->NewDirectByteBuffer(this, address, capacity); } void* GetDirectBufferAddress(jobject buf) { return functions->GetDirectBufferAddress(this, buf); } jlong GetDirectBufferCapacity(jobject buf) { return functions->GetDirectBufferCapacity(this, buf); } /* added in JNI 1.6 */ jobjectRefType GetObjectRefType(jobject obj) { return functions->GetObjectRefType(this, obj); } #endif /*__cplusplus*/ }; /* * JNI invocation interface. */ struct JNIInvokeInterface { void* reserved0; void* reserved1; void* reserved2; jint (*DestroyJavaVM)(JavaVM*); jint (*AttachCurrentThread)(JavaVM*, JNIEnv**, void*); jint (*DetachCurrentThread)(JavaVM*); jint (*GetEnv)(JavaVM*, void**, jint); jint (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*); }; /* * C++ version. */ struct _JavaVM { const struct JNIInvokeInterface* functions; #if defined(__cplusplus) jint DestroyJavaVM() { return functions->DestroyJavaVM(this); } jint AttachCurrentThread(JNIEnv** p_env, void* thr_args) { return functions->AttachCurrentThread(this, p_env, thr_args); } jint DetachCurrentThread() { return functions->DetachCurrentThread(this); } jint GetEnv(void** env, jint version) { return functions->GetEnv(this, env, version); } jint AttachCurrentThreadAsDaemon(JNIEnv** p_env, void* thr_args) { return functions->AttachCurrentThreadAsDaemon(this, p_env, thr_args); } #endif /*__cplusplus*/ }; struct JavaVMAttachArgs { jint version; /* must be >= JNI_VERSION_1_2 */ const char* name; /* NULL or name of thread as modified UTF-8 str */ jobject group; /* global ref of a ThreadGroup object, or NULL */ }; typedef struct JavaVMAttachArgs JavaVMAttachArgs; /* * JNI 1.2+ initialization. (As of 1.6, the pre-1.2 structures are no * longer supported.) */ typedef struct JavaVMOption { const char* optionString; void* extraInfo; } JavaVMOption; typedef struct JavaVMInitArgs { jint version; /* use JNI_VERSION_1_2 or later */ jint nOptions; JavaVMOption* options; jboolean ignoreUnrecognized; } JavaVMInitArgs; #ifdef __cplusplus extern "C" { #endif /* * VM initialization functions. * * Note these are the only symbols exported for JNI by the VM. */ jint JNI_GetDefaultJavaVMInitArgs(void*); jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*); jint JNI_GetCreatedJavaVMs(JavaVM**, jsize, jsize*); #define JNIIMPORT #define JNIEXPORT __attribute__ ((visibility ("default"))) #define JNICALL /* * Prototypes for functions exported by loadable shared libs. These are * called by JNI, not provided by JNI. */ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved); JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved); #ifdef __cplusplus } #endif /* * Manifest constants. */ #define JNI_FALSE 0 #define JNI_TRUE 1 #define JNI_VERSION_1_1 0x00010001 #define JNI_VERSION_1_2 0x00010002 #define JNI_VERSION_1_4 0x00010004 #define JNI_VERSION_1_6 0x00010006 #define JNI_OK (0) /* no error */ #define JNI_ERR (-1) /* generic error */ #define JNI_EDETACHED (-2) /* thread detached from the VM */ #define JNI_EVERSION (-3) /* JNI version error */ #define JNI_ENOMEM (-4) /* Out of memory */ #define JNI_EEXIST (-5) /* VM already created */ #define JNI_EINVAL (-6) /* Invalid argument */ #define JNI_COMMIT 1 /* copy content, do not free buffer */ #define JNI_ABORT 2 /* free buffer w/o copying back */ #endif /* JNI_H_ */ libnativehelper.map.txt0100644 0000000 0000000 00000001647 13756723631 014324 0ustar000000000 0000000 # This library should only export C linkage definitions. # # VERSION string that follows is derived from _. LIBNATIVEHELPER_1 { global: JNI_GetDefaultJavaVMInitArgs; JNI_CreateJavaVM; JNI_GetCreatedJavaVMs; jniRegisterNativeMethods; jniThrowException; jniThrowExceptionFmt; jniThrowNullPointerException; jniThrowRuntimeException; jniThrowIOException; jniStrError; jniCreateFileDescriptor; jniGetFDFromFileDescriptor; jniSetFileDescriptorOfFD; jniGetOwnerIdFromFileDescriptor; jniGetNioBufferBaseArray; jniGetNioBufferBaseArrayOffset; jniGetNioBufferPointer; jniGetNioBufferFields; jniGetReferent; jniCreateString; jniLogException; jniUninitializeConstants; JniInvocationCreate; JniInvocationDestroy; JniInvocationInit; JniInvocationGetLibrary; newStringArray; toStringArray; local: *; }; platform_include/0040755 0000000 0000000 00000000000 13756723631 013154 5ustar000000000 0000000 platform_include/nativehelper/0040755 0000000 0000000 00000000000 13756723631 015642 5ustar000000000 0000000 platform_include/nativehelper/detail/0040755 0000000 0000000 00000000000 13756723631 017104 5ustar000000000 0000000 platform_include/nativehelper/detail/signature_checker.h0100644 0000000 0000000 00000142105 13756723631 022742 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ /* * WARNING: Do not include and use these directly. Use jni_macros.h instead! * The "detail" namespace should be a strong hint not to depend on the internals, * which could change at any time. * * This implements the underlying mechanism for compile-time JNI signature/ctype checking * and inference. * * This file provides the constexpr basic blocks such as strings, arrays, vectors * as well as the JNI-specific parsing functionality. * * Everything is implemented via generic-style (templates without metaprogramming) * wherever possible. Traditional template metaprogramming is used sparingly. * * Everything in this file except ostream<< is constexpr. */ #pragma once #include // std::ostream #include // jni typedefs, JniNativeMethod. #include // std::common_type, std::remove_cv namespace nativehelper { namespace detail { // If CHECK evaluates to false then X_ASSERT will halt compilation. // // Asserts meant to be used only within constexpr context. #if defined(JNI_SIGNATURE_CHECKER_DISABLE_ASSERTS) # define X_ASSERT(CHECK) do { if ((false)) { (CHECK) ? void(0) : void(0); } } while (false) #else # define X_ASSERT(CHECK) \ ( (CHECK) ? void(0) : jni_assertion_failure(#CHECK) ) #endif // The runtime 'jni_assert' will never get called from a constexpr context; // instead compilation will abort with a stack trace. // // Inspect the frame above this one to see the exact nature of the failure. inline void jni_assertion_failure(const char* /*msg*/) __attribute__((noreturn)); inline void jni_assertion_failure(const char* /*msg*/) { std::terminate(); } // An immutable constexpr string view, similar to std::string_view but for C++14. // For a mutable string see instead ConstexprVector. // // As it is a read-only view into a string, it is not guaranteed to be zero-terminated. struct ConstexprStringView { // Implicit conversion from string literal: // ConstexprStringView str = "hello_world"; template constexpr ConstexprStringView(const char (& lit)[N]) // NOLINT: explicit. : _array(lit), _size(N - 1) { // Using an array of characters is not allowed because the inferred size would be wrong. // Use the other constructor instead for that. X_ASSERT(lit[N - 1] == '\0'); } constexpr ConstexprStringView(const char* ptr, size_t size) : _array(ptr), _size(size) { // See the below constructor instead. X_ASSERT(ptr != nullptr); } // No-arg constructor: Create empty view. constexpr ConstexprStringView() : _array(""), _size(0u) {} constexpr size_t size() const { return _size; } constexpr bool empty() const { return size() == 0u; } constexpr char operator[](size_t i) const { X_ASSERT(i <= size()); return _array[i]; } // Create substring from this[start..start+len). constexpr ConstexprStringView substr(size_t start, size_t len) const { X_ASSERT(start <= size()); X_ASSERT(len <= size() - start); return ConstexprStringView(&_array[start], len); } // Create maximum length substring that begins at 'start'. constexpr ConstexprStringView substr(size_t start) const { X_ASSERT(start <= size()); return substr(start, size() - start); } using const_iterator = const char*; constexpr const_iterator begin() const { return &_array[0]; } constexpr const_iterator end() const { return &_array[size()]; } private: const char* _array; // Never-null for simplicity. size_t _size; }; constexpr bool operator==(const ConstexprStringView& lhs, const ConstexprStringView& rhs) { if (lhs.size() != rhs.size()) { return false; } for (size_t i = 0; i < lhs.size(); ++i) { if (lhs[i] != rhs[i]) { return false; } } return true; } constexpr bool operator!=(const ConstexprStringView& lhs, const ConstexprStringView& rhs) { return !(lhs == rhs); } inline std::ostream& operator<<(std::ostream& os, const ConstexprStringView& str) { for (char c : str) { os << c; } return os; } constexpr bool IsValidJniDescriptorStart(char shorty) { constexpr char kValidJniStarts[] = {'V', 'Z', 'B', 'C', 'S', 'I', 'J', 'F', 'D', 'L', '[', '(', ')'}; for (char c : kValidJniStarts) { if (c == shorty) { return true; } } return false; } // A constexpr "vector" that supports storing a variable amount of Ts // in an array-like interface. // // An up-front kMaxSize must be given since constexpr does not support // dynamic allocations. template struct ConstexprVector { public: constexpr explicit ConstexprVector() : _size(0u), _array{} { } private: // Custom iterator to support ptr-one-past-end into the union array without // undefined behavior. template struct VectorIterator { Elem* ptr; constexpr VectorIterator& operator++() { ++ptr; return *this; } constexpr VectorIterator operator++(int) const { VectorIterator tmp(*this); ++tmp; return tmp; } constexpr /*T&*/ auto& operator*() { // Use 'auto' here since using 'T' is incorrect with const_iterator. return ptr->_value; } constexpr const /*T&*/ auto& operator*() const { // Use 'auto' here for consistency with above. return ptr->_value; } constexpr bool operator==(const VectorIterator& other) const { return ptr == other.ptr; } constexpr bool operator!=(const VectorIterator& other) const { return !(*this == other); } }; // Do not require that T is default-constructible by using a union. struct MaybeElement { union { T _value; }; }; public: using iterator = VectorIterator; using const_iterator = VectorIterator; constexpr iterator begin() { return {&_array[0]}; } constexpr iterator end() { return {&_array[size()]}; } constexpr const_iterator begin() const { return {&_array[0]}; } constexpr const_iterator end() const { return {&_array[size()]}; } constexpr void push_back(const T& value) { X_ASSERT(_size + 1 <= kMaxSize); _array[_size]._value = value; _size++; } // A pop operation could also be added since constexpr T's // have default destructors, it would just be _size--. // We do not need a pop() here though. constexpr const T& operator[](size_t i) const { return _array[i]._value; } constexpr T& operator[](size_t i) { return _array[i]._value; } constexpr size_t size() const { return _size; } private: size_t _size; MaybeElement _array[kMaxSize]; }; // Parsed and validated "long" form of a single JNI descriptor. // e.g. one of "J", "Ljava/lang/Object;" etc. struct JniDescriptorNode { ConstexprStringView longy; constexpr JniDescriptorNode(ConstexprStringView longy) : longy(longy) { // NOLINT(google-explicit-constructor) X_ASSERT(!longy.empty()); } constexpr JniDescriptorNode() : longy() {} constexpr char shorty() { // Must be initialized with the non-default constructor. X_ASSERT(!longy.empty()); return longy[0]; } }; inline std::ostream& operator<<(std::ostream& os, const JniDescriptorNode& node) { os << node.longy; return os; } // Equivalent of C++17 std::optional. // // An optional is essentially a type safe // union { // void Nothing, // T Some; // }; // template struct ConstexprOptional { // Create a default optional with no value. constexpr ConstexprOptional() : _has_value(false), _nothing() { } // Create an optional with a value. constexpr ConstexprOptional(const T& value) // NOLINT(google-explicit-constructor) : _has_value(true), _value(value) { } constexpr explicit operator bool() const { return _has_value; } constexpr bool has_value() const { return _has_value; } constexpr const T& value() const { X_ASSERT(has_value()); return _value; } constexpr const T* operator->() const { return &(value()); } constexpr const T& operator*() const { return value(); } private: bool _has_value; // The "Nothing" is likely unnecessary but improves readability. struct Nothing {}; union { Nothing _nothing; T _value; }; }; template constexpr bool operator==(const ConstexprOptional& lhs, const ConstexprOptional& rhs) { if (lhs && rhs) { return lhs.value() == rhs.value(); } return lhs.has_value() == rhs.has_value(); } template constexpr bool operator!=(const ConstexprOptional& lhs, const ConstexprOptional& rhs) { return !(lhs == rhs); } template inline std::ostream& operator<<(std::ostream& os, const ConstexprOptional& val) { if (val) { os << val.value(); } return os; } // Equivalent of std::nullopt // Allows implicit conversion to any empty ConstexprOptional. // Mostly useful for macros that need to return an empty constexpr optional. struct NullConstexprOptional { template constexpr operator ConstexprOptional() const { // NOLINT(google-explicit-constructor) return ConstexprOptional(); } }; inline std::ostream& operator<<(std::ostream& os, NullConstexprOptional) { return os; } #if !defined(PARSE_FAILURES_NONFATAL) // Unfortunately we cannot have custom messages here, as it just prints a stack trace with the // macros expanded. This is at least more flexible than static_assert which requires a string // literal. // NOTE: The message string literal must be on same line as the macro to be seen during a // compilation error. #define PARSE_FAILURE(msg) X_ASSERT(! #msg) #define PARSE_ASSERT_MSG(cond, msg) X_ASSERT(#msg && (cond)) #define PARSE_ASSERT(cond) X_ASSERT(cond) #else #define PARSE_FAILURE(msg) return NullConstexprOptional{}; #define PARSE_ASSERT_MSG(cond, msg) if (!(cond)) { PARSE_FAILURE(msg); } #define PARSE_ASSERT(cond) if (!(cond)) { PARSE_FAILURE(""); } #endif // This is a placeholder function and should not be called directly. constexpr void ParseFailure(const char* msg) { (void) msg; // intentionally no-op. } // Temporary parse data when parsing a function descriptor. struct ParseTypeDescriptorResult { // A single argument descriptor, e.g. "V" or "Ljava/lang/Object;" ConstexprStringView token; // The remainder of the function descriptor yet to be parsed. ConstexprStringView remainder; constexpr bool has_token() const { return token.size() > 0u; } constexpr bool has_remainder() const { return remainder.size() > 0u; } constexpr JniDescriptorNode as_node() const { X_ASSERT(has_token()); return {token}; } }; // Parse a single type descriptor out of a function type descriptor substring, // and return the token and the remainder string. // // If parsing fails (i.e. illegal syntax), then: // parses are fatal -> assertion is triggered (default behavior), // parses are nonfatal -> returns nullopt (test behavior). constexpr ConstexprOptional ParseSingleTypeDescriptor(ConstexprStringView single_type, bool allow_void = false) { constexpr NullConstexprOptional kUnreachable = {}; // Nothing else left. if (single_type.size() == 0) { return ParseTypeDescriptorResult{}; } ConstexprStringView token; ConstexprStringView remainder = single_type.substr(/*start*/1u); char c = single_type[0]; PARSE_ASSERT(IsValidJniDescriptorStart(c)); enum State { kSingleCharacter, kArray, kObject }; State state = kSingleCharacter; // Parse the first character to figure out if we should parse the rest. switch (c) { case '!': { constexpr bool fast_jni_is_deprecated = false; PARSE_ASSERT(fast_jni_is_deprecated); break; } case 'V': if (!allow_void) { constexpr bool void_type_descriptor_only_allowed_in_return_type = false; PARSE_ASSERT(void_type_descriptor_only_allowed_in_return_type); } [[clang::fallthrough]]; case 'Z': case 'B': case 'C': case 'S': case 'I': case 'J': case 'F': case 'D': token = single_type.substr(/*start*/0u, /*len*/1u); break; case 'L': state = kObject; break; case '[': state = kArray; break; default: { // See JNI Chapter 3: Type Signatures. PARSE_FAILURE("Expected a valid type descriptor character."); return kUnreachable; } } // Possibly parse an arbitary-long remainder substring. switch (state) { case kSingleCharacter: return {{token, remainder}}; case kArray: { // Recursively parse the array component, as it's just any non-void type descriptor. ConstexprOptional maybe_res = ParseSingleTypeDescriptor(remainder, /*allow_void*/false); PARSE_ASSERT(maybe_res); // Downstream parsing has asserted, bail out. ParseTypeDescriptorResult res = maybe_res.value(); // Reject illegal array type descriptors such as "]". PARSE_ASSERT_MSG(res.has_token(), "All array types must follow by their component type (e.g. ']I', ']]Z', etc. "); token = single_type.substr(/*start*/0u, res.token.size() + 1u); return {{token, res.remainder}}; } case kObject: { // Parse the fully qualified class, e.g. Lfoo/bar/baz; // Note checking that each part of the class name is a valid class identifier // is too complicated (JLS 3.8). // This simple check simply scans until the next ';'. bool found_semicolon = false; size_t semicolon_len = 0; for (size_t i = 0; i < single_type.size(); ++i) { switch (single_type[i]) { case ')': case '(': case '[': PARSE_FAILURE("Object identifiers cannot have ()[ in them."); break; } if (single_type[i] == ';') { semicolon_len = i + 1; found_semicolon = true; break; } } PARSE_ASSERT(found_semicolon); token = single_type.substr(/*start*/0u, semicolon_len); remainder = single_type.substr(/*start*/semicolon_len); bool class_name_is_empty = token.size() <= 2u; // e.g. "L;" PARSE_ASSERT(!class_name_is_empty); return {{token, remainder}}; } default: X_ASSERT(false); } X_ASSERT(false); return kUnreachable; } // Abstract data type to represent container for Ret(Args,...). template struct FunctionSignatureDescriptor { ConstexprVector args; T ret; static constexpr size_t max_size = kMaxSize; }; template inline std::ostream& operator<<( std::ostream& os, const FunctionSignatureDescriptor& signature) { size_t count = 0; os << "args={"; for (auto& arg : signature.args) { os << arg; if (count != signature.args.size() - 1) { os << ","; } ++count; } os << "}, ret="; os << signature.ret; return os; } // Ret(Args...) of JniDescriptorNode. template using JniSignatureDescriptor = FunctionSignatureDescriptor; // Parse a JNI function signature descriptor into a JniSignatureDescriptor. // // If parsing fails (i.e. illegal syntax), then: // parses are fatal -> assertion is triggered (default behavior), // parses are nonfatal -> returns nullopt (test behavior). template constexpr ConstexprOptional> ParseSignatureAsList(ConstexprStringView signature) { // The list of JNI descriptors cannot possibly exceed the number of characters // in the JNI string literal. We leverage this to give an upper bound of the strlen. // This is a bit wasteful but in constexpr there *must* be a fixed upper size for data structures. ConstexprVector jni_desc_node_list; JniDescriptorNode return_jni_desc; enum State { kInitial = 0, kParsingParameters = 1, kParsingReturnType = 2, kCompleted = 3, }; State state = kInitial; while (!signature.empty()) { switch (state) { case kInitial: { char c = signature[0]; PARSE_ASSERT_MSG(c == '(', "First character of a JNI signature must be a '('"); state = kParsingParameters; signature = signature.substr(/*start*/1u); break; } case kParsingParameters: { char c = signature[0]; if (c == ')') { state = kParsingReturnType; signature = signature.substr(/*start*/1u); break; } ConstexprOptional res = ParseSingleTypeDescriptor(signature, /*allow_void*/false); PARSE_ASSERT(res); jni_desc_node_list.push_back(res->as_node()); signature = res->remainder; break; } case kParsingReturnType: { ConstexprOptional res = ParseSingleTypeDescriptor(signature, /*allow_void*/true); PARSE_ASSERT(res); return_jni_desc = res->as_node(); signature = res->remainder; state = kCompleted; break; } default: { // e.g. "()VI" is illegal because the V terminates the signature. PARSE_FAILURE("Signature had left over tokens after parsing return type"); break; } } } switch (state) { case kCompleted: // Everything is ok. break; case kParsingParameters: PARSE_FAILURE("Signature was missing ')'"); break; case kParsingReturnType: PARSE_FAILURE("Missing return type"); case kInitial: PARSE_FAILURE("Cannot have an empty signature"); default: X_ASSERT(false); } return {{jni_desc_node_list, return_jni_desc}}; } // What kind of JNI does this type belong to? enum NativeKind { kNotJni, // Illegal parameter used inside of a function type. kNormalJniCallingConventionParameter, kNormalNative, kFastNative, // Also valid in normal. kCriticalNative, // Also valid in fast/normal. }; // Is this type final, i.e. it cannot be subtyped? enum TypeFinal { kNotFinal, kFinal // e.g. any primitive or any "final" class such as String. }; // What position is the JNI type allowed to be in? // Ignored when in a CriticalNative context. enum NativePositionAllowed { kNotAnyPosition, kReturnPosition, kZerothPosition, kFirstOrLaterPosition, kSecondOrLaterPosition, }; constexpr NativePositionAllowed ConvertPositionToAllowed(size_t position) { switch (position) { case 0: return kZerothPosition; case 1: return kFirstOrLaterPosition; default: return kSecondOrLaterPosition; } } // Type traits for a JNI parameter type. See below for specializations. template struct jni_type_trait { static constexpr NativeKind native_kind = kNotJni; static constexpr const char type_descriptor[] = "(illegal)"; static constexpr NativePositionAllowed position_allowed = kNotAnyPosition; static constexpr TypeFinal type_finality = kNotFinal; static constexpr const char type_name[] = "(illegal)"; }; // Access the jni_type_trait from a non-templated constexpr function. // Identical non-static fields to jni_type_trait, see Reify(). struct ReifiedJniTypeTrait { NativeKind native_kind; ConstexprStringView type_descriptor; NativePositionAllowed position_allowed; TypeFinal type_finality; ConstexprStringView type_name; template static constexpr ReifiedJniTypeTrait Reify() { // This should perhaps be called 'Type Erasure' except we don't use virtuals, // so it's not quite the same idiom. using TR = jni_type_trait; return {TR::native_kind, TR::type_descriptor, TR::position_allowed, TR::type_finality, TR::type_name}; } // Find the most similar ReifiedJniTypeTrait corresponding to the type descriptor. // // Any type can be found by using the exact canonical type descriptor as listed // in the jni type traits definitions. // // Non-final JNI types have limited support for inexact similarity: // [[* | [L* -> jobjectArray // L* -> jobject // // Otherwise return a nullopt. static constexpr ConstexprOptional MostSimilarTypeDescriptor(ConstexprStringView type_descriptor); }; constexpr bool operator==(const ReifiedJniTypeTrait& lhs, const ReifiedJniTypeTrait& rhs) { return lhs.native_kind == rhs.native_kind && rhs.type_descriptor == lhs.type_descriptor && lhs.position_allowed == rhs.position_allowed && rhs.type_finality == lhs.type_finality && lhs.type_name == rhs.type_name; } inline std::ostream& operator<<(std::ostream& os, const ReifiedJniTypeTrait& rjtt) { // os << "ReifiedJniTypeTrait<" << rjft.type_name << ">"; os << rjtt.type_name; return os; } // Template specialization for any JNI typedefs. #define JNI_TYPE_TRAIT(jtype, the_type_descriptor, the_native_kind, the_type_finality, the_position) \ template <> \ struct jni_type_trait< jtype > { \ static constexpr NativeKind native_kind = the_native_kind; \ static constexpr const char type_descriptor[] = the_type_descriptor; \ static constexpr NativePositionAllowed position_allowed = the_position; \ static constexpr TypeFinal type_finality = the_type_finality; \ static constexpr const char type_name[] = #jtype; \ }; #define DEFINE_JNI_TYPE_TRAIT(TYPE_TRAIT_FN) \ TYPE_TRAIT_FN(jboolean, "Z", kCriticalNative, kFinal, kSecondOrLaterPosition) \ TYPE_TRAIT_FN(jbyte, "B", kCriticalNative, kFinal, kSecondOrLaterPosition) \ TYPE_TRAIT_FN(jchar, "C", kCriticalNative, kFinal, kSecondOrLaterPosition) \ TYPE_TRAIT_FN(jshort, "S", kCriticalNative, kFinal, kSecondOrLaterPosition) \ TYPE_TRAIT_FN(jint, "I", kCriticalNative, kFinal, kSecondOrLaterPosition) \ TYPE_TRAIT_FN(jlong, "J", kCriticalNative, kFinal, kSecondOrLaterPosition) \ TYPE_TRAIT_FN(jfloat, "F", kCriticalNative, kFinal, kSecondOrLaterPosition) \ TYPE_TRAIT_FN(jdouble, "D", kCriticalNative, kFinal, kSecondOrLaterPosition) \ TYPE_TRAIT_FN(jobject, "Ljava/lang/Object;", kFastNative, kNotFinal, kFirstOrLaterPosition) \ TYPE_TRAIT_FN(jclass, "Ljava/lang/Class;", kFastNative, kFinal, kFirstOrLaterPosition) \ TYPE_TRAIT_FN(jstring, "Ljava/lang/String;", kFastNative, kFinal, kSecondOrLaterPosition) \ TYPE_TRAIT_FN(jarray, "Ljava/lang/Object;", kFastNative, kNotFinal, kSecondOrLaterPosition) \ TYPE_TRAIT_FN(jobjectArray, "[Ljava/lang/Object;", kFastNative, kNotFinal, kSecondOrLaterPosition) \ TYPE_TRAIT_FN(jbooleanArray, "[Z", kFastNative, kFinal, kSecondOrLaterPosition) \ TYPE_TRAIT_FN(jbyteArray, "[B", kFastNative, kFinal, kSecondOrLaterPosition) \ TYPE_TRAIT_FN(jcharArray, "[C", kFastNative, kFinal, kSecondOrLaterPosition) \ TYPE_TRAIT_FN(jshortArray, "[S", kFastNative, kFinal, kSecondOrLaterPosition) \ TYPE_TRAIT_FN(jintArray, "[I", kFastNative, kFinal, kSecondOrLaterPosition) \ TYPE_TRAIT_FN(jlongArray, "[J", kFastNative, kFinal, kSecondOrLaterPosition) \ TYPE_TRAIT_FN(jfloatArray, "[F", kFastNative, kFinal, kSecondOrLaterPosition) \ TYPE_TRAIT_FN(jdoubleArray, "[D", kFastNative, kFinal, kSecondOrLaterPosition) \ TYPE_TRAIT_FN(jthrowable, "Ljava/lang/Throwable;", kFastNative, kNotFinal, kSecondOrLaterPosition) \ TYPE_TRAIT_FN(JNIEnv*, "", kNormalJniCallingConventionParameter, kFinal, kZerothPosition) \ TYPE_TRAIT_FN(void, "V", kCriticalNative, kFinal, kReturnPosition) \ DEFINE_JNI_TYPE_TRAIT(JNI_TYPE_TRAIT) // See ReifiedJniTypeTrait for documentation. constexpr ConstexprOptional ReifiedJniTypeTrait::MostSimilarTypeDescriptor(ConstexprStringView type_descriptor) { #define MATCH_EXACT_TYPE_DESCRIPTOR_FN(type, type_desc, native_kind, ...) \ if (type_descriptor == type_desc && native_kind >= kNormalNative) { \ return { Reify() }; \ } // Attempt to look up by the precise type match first. DEFINE_JNI_TYPE_TRAIT(MATCH_EXACT_TYPE_DESCRIPTOR_FN); // Otherwise, we need to do an imprecise match: char shorty = type_descriptor.size() >= 1 ? type_descriptor[0] : '\0'; if (shorty == 'L') { // Something more specific like Ljava/lang/Throwable, string, etc // is already matched by the macro-expanded conditions above. return {Reify()}; } else if (type_descriptor.size() >= 2) { auto shorty_shorty = type_descriptor.substr(/*start*/0, /*size*/2u); if (shorty_shorty == "[[" || shorty_shorty == "[L") { // JNI arrays are covariant, so any type T[] (T!=primitive) is castable to Object[]. return {Reify()}; } } // To handle completely invalid values. return NullConstexprOptional{}; } // Is this actual JNI position consistent with the expected position? constexpr bool IsValidJniParameterPosition(NativeKind native_kind, NativePositionAllowed position, NativePositionAllowed expected_position) { X_ASSERT(expected_position != kNotAnyPosition); if (native_kind == kCriticalNative) { // CriticalNatives ignore positions since the first 2 special // parameters are stripped. return true; } // Is this a return-only position? if (expected_position == kReturnPosition) { if (position != kReturnPosition) { // void can only be in the return position. return false; } // Don't do the other non-return position checks for a return-only position. return true; } // JNIEnv* can only be in the first spot. if (position == kZerothPosition && expected_position != kZerothPosition) { return false; // jobject, jclass can be 1st or anywhere afterwards. } else if (position == kFirstOrLaterPosition && expected_position != kFirstOrLaterPosition) { return false; // All other parameters must be in 2nd+ spot, or in the return type. } else if (position == kSecondOrLaterPosition || position == kReturnPosition) { if (expected_position != kFirstOrLaterPosition && expected_position != kSecondOrLaterPosition) { return false; } } return true; } // Check if a jni parameter type is valid given its position and native_kind. template constexpr bool IsValidJniParameter(NativeKind native_kind, NativePositionAllowed position) { // const,volatile does not affect JNI compatibility since it does not change ABI. using expected_trait = jni_type_trait::type>; NativeKind expected_native_kind = expected_trait::native_kind; // Most types 'T' are not valid for JNI. if (expected_native_kind == NativeKind::kNotJni) { return false; } // The rest of the types might be valid, but it depends on the context (native_kind) // and also on their position within the parameters. // Position-check first. NativePositionAllowed expected_position = expected_trait::position_allowed; if (!IsValidJniParameterPosition(native_kind, position, expected_position)) { return false; } // Ensure the type appropriate is for the native kind. if (expected_native_kind == kNormalJniCallingConventionParameter) { // It's always wrong to use a JNIEnv* anywhere but the 0th spot. if (native_kind == kCriticalNative) { // CriticalNative does not allow using a JNIEnv*. return false; } return true; // OK: JniEnv* used in 0th position. } else if (expected_native_kind == kCriticalNative) { // CriticalNative arguments are always valid JNI types anywhere used. return true; } else if (native_kind == kCriticalNative) { // The expected_native_kind was non-critical but we are in a critical context. // Illegal type. return false; } // Everything else is fine, e.g. fast/normal native + fast/normal native parameters. return true; } // Is there sufficient number of parameters given the kind of JNI that it is? constexpr bool IsJniParameterCountValid(NativeKind native_kind, size_t count) { if (native_kind == kNormalNative || native_kind == kFastNative) { return count >= 2u; } else if (native_kind == kCriticalNative) { return true; } constexpr bool invalid_parameter = false; X_ASSERT(invalid_parameter); return false; } // Basic template interface. See below for partial specializations. // // Each instantiation will have a 'value' field that determines whether or not // all of the Args are valid JNI arguments given their native_kind. template struct is_valid_jni_argument_type { // static constexpr bool value = ?; }; template struct is_valid_jni_argument_type { static constexpr bool value = true; }; template struct is_valid_jni_argument_type { static constexpr bool value = IsValidJniParameter(native_kind, ConvertPositionToAllowed(position)); }; template struct is_valid_jni_argument_type { static constexpr bool value = IsValidJniParameter(native_kind, ConvertPositionToAllowed(position)) && is_valid_jni_argument_type::value; }; // This helper is required to decompose the function type into a list of arg types. template struct is_valid_jni_function_type_helper; template struct is_valid_jni_function_type_helper { static constexpr bool value = IsJniParameterCountValid(native_kind, sizeof...(Args)) && IsValidJniParameter(native_kind, kReturnPosition) && is_valid_jni_argument_type::value; }; // Is this function type 'T' a valid C++ function type given the native_kind? template constexpr bool IsValidJniFunctionType() { return is_valid_jni_function_type_helper::value; // TODO: we could replace template metaprogramming with constexpr by // using FunctionTypeMetafunction. } // Many parts of std::array is not constexpr until C++17. template struct ConstexprArray { // Intentionally public to conform to std::array. // This means all constructors are implicit. // *NOT* meant to be used directly, use the below functions instead. // // The reason std::array has it is to support direct-list-initialization, // e.g. "ConstexprArray{T{...}, T{...}, T{...}, ...};" // // Note that otherwise this would need a very complicated variadic // argument constructor to only support list of Ts. T _array[N]; constexpr size_t size() const { return N; } using iterator = T*; using const_iterator = const T*; constexpr iterator begin() { return &_array[0]; } constexpr iterator end() { return &_array[N]; } constexpr const_iterator begin() const { return &_array[0]; } constexpr const_iterator end() const { return &_array[N]; } constexpr T& operator[](size_t i) { return _array[i]; } constexpr const T& operator[](size_t i) const { return _array[i]; } }; // Why do we need this? // auto x = {1,2,3} creates an initializer_list, // but they can't be returned because it contains pointers to temporaries. // auto x[] = {1,2,3} doesn't even work because auto for arrays is not supported. // // an alternative would be to pull up std::common_t directly into the call site // std::common_type_t array[] = {1,2,3} // but that's even more cludgier. // // As the other "stdlib-wannabe" functions, it's weaker than the library // fundamentals std::make_array but good enough for our use. template constexpr auto MakeArray(Args&& ... args) { return ConstexprArray::type, sizeof...(Args)>{args...}; } // See below. template struct FunctionTypeMetafunction { }; // Enables the "map" operation over the function component types. template struct FunctionTypeMetafunction { // Count how many arguments there are, and add 1 for the return type. static constexpr size_t count = sizeof...(Args) + 1u; // args and return type. // Return an array where the metafunction 'Func' has been applied // to every argument type. The metafunction must be returning a common type. template class Func> static constexpr auto map_args() { return map_args_impl(holder < Args > {}...); } // Apply the metafunction 'Func' over the return type. template class Func> static constexpr auto map_return() { return Func{}(); } private: template struct holder { }; template class Func, typename Arg0, typename... ArgsRest> static constexpr auto map_args_impl(holder, holder...) { // One does not simply call MakeArray with 0 template arguments... auto array = MakeArray( Func{}()... ); return array; } template class Func> static constexpr auto map_args_impl() { // This overload provides support for MakeArray() with 0 arguments. using ComponentType = decltype(Func{}()); return ConstexprArray{}; } }; // Apply ReifiedJniTypeTrait::Reify for every function component type. template struct ReifyJniTypeMetafunction { constexpr ReifiedJniTypeTrait operator()() const { auto res = ReifiedJniTypeTrait::Reify(); X_ASSERT(res.native_kind != kNotJni); return res; } }; // Ret(Args...) where every component is a ReifiedJniTypeTrait. template using ReifiedJniSignature = FunctionSignatureDescriptor; // Attempts to convert the function type T into a list of ReifiedJniTypeTraits // that correspond to the function components. // // If conversion fails (i.e. non-jni compatible types), then: // parses are fatal -> assertion is triggered (default behavior), // parses are nonfatal -> returns nullopt (test behavior). template ::count> constexpr ConstexprOptional> MaybeMakeReifiedJniSignature() { if (!IsValidJniFunctionType()) { PARSE_FAILURE("The function signature has one or more types incompatible with JNI."); } ReifiedJniTypeTrait return_jni_trait = FunctionTypeMetafunction::template map_return(); constexpr size_t kSkipArgumentPrefix = (native_kind != kCriticalNative) ? 2u : 0u; ConstexprVector args; auto args_list = FunctionTypeMetafunction::template map_args(); size_t args_index = 0; for (auto& arg : args_list) { // Ignore the 'JNIEnv*, jobject' / 'JNIEnv*, jclass' prefix, // as its not part of the function descriptor string. if (args_index >= kSkipArgumentPrefix) { args.push_back(arg); } ++args_index; } return {{args, return_jni_trait}}; } #define COMPARE_DESCRIPTOR_CHECK(expr) if (!(expr)) return false #define COMPARE_DESCRIPTOR_FAILURE_MSG(msg) if ((true)) return false // Compares a user-defined JNI descriptor (of a single argument or return value) // to a reified jni type trait that was derived from the C++ function type. // // If comparison fails (i.e. non-jni compatible types), then: // parses are fatal -> assertion is triggered (default behavior), // parses are nonfatal -> returns false (test behavior). constexpr bool CompareJniDescriptorNodeErased(JniDescriptorNode user_defined_descriptor, ReifiedJniTypeTrait derived) { ConstexprOptional user_reified_opt = ReifiedJniTypeTrait::MostSimilarTypeDescriptor(user_defined_descriptor.longy); if (!user_reified_opt.has_value()) { COMPARE_DESCRIPTOR_FAILURE_MSG( "Could not find any JNI C++ type corresponding to the type descriptor"); } char user_shorty = user_defined_descriptor.longy.size() > 0 ? user_defined_descriptor.longy[0] : '\0'; ReifiedJniTypeTrait user = user_reified_opt.value(); if (user == derived) { // If we had a similar match, immediately return success. return true; } else if (derived.type_name == "jthrowable") { if (user_shorty == 'L') { // Weakly allow any objects to correspond to a jthrowable. // We do not know the managed type system so we have to be permissive here. return true; } else { COMPARE_DESCRIPTOR_FAILURE_MSG( "jthrowable must correspond to an object type descriptor"); } } else if (derived.type_name == "jarray") { if (user_shorty == '[') { // a jarray is the base type for all other array types. Allow. return true; } else { // Ljava/lang/Object; is the root for all array types. // Already handled above in 'if user == derived'. COMPARE_DESCRIPTOR_FAILURE_MSG( "jarray must correspond to array type descriptor"); } } // Otherwise, the comparison has failed and the rest of this is only to // pick the most appropriate error message. // // Note: A weaker form of comparison would allow matching 'Ljava/lang/String;' // against 'jobject', etc. However the policy choice here is to enforce the strictest // comparison that we can to utilize the type system to its fullest. if (derived.type_finality == kFinal || user.type_finality == kFinal) { // Final types, e.g. "I", "Ljava/lang/String;" etc must match exactly // the C++ jni descriptor string ('I' -> jint, 'Ljava/lang/String;' -> jstring). COMPARE_DESCRIPTOR_FAILURE_MSG( "The JNI descriptor string must be the exact type equivalent of the " "C++ function signature."); } else if (user_shorty == '[') { COMPARE_DESCRIPTOR_FAILURE_MSG( "The array JNI descriptor must correspond to j${type}Array or jarray"); } else if (user_shorty == 'L') { COMPARE_DESCRIPTOR_FAILURE_MSG( "The object JNI descriptor must correspond to jobject."); } else { X_ASSERT(false); // We should never get here, but either way this means the types did not match COMPARE_DESCRIPTOR_FAILURE_MSG( "The JNI type descriptor string does not correspond to the C++ JNI type."); } } // Matches a user-defined JNI function descriptor against the C++ function type. // // If matches fails, then: // parses are fatal -> assertion is triggered (default behavior), // parses are nonfatal -> returns false (test behavior). template constexpr bool MatchJniDescriptorWithFunctionType(ConstexprStringView user_function_descriptor) { constexpr size_t kReifiedMaxSize = FunctionTypeMetafunction::count; ConstexprOptional> reified_signature_opt = MaybeMakeReifiedJniSignature(); if (!reified_signature_opt) { // Assertion handling done by MaybeMakeReifiedJniSignature. return false; } ConstexprOptional> user_jni_sig_desc_opt = ParseSignatureAsList(user_function_descriptor); if (!user_jni_sig_desc_opt) { // Assertion handling done by ParseSignatureAsList. return false; } ReifiedJniSignature reified_signature = reified_signature_opt.value(); JniSignatureDescriptor user_jni_sig_desc = user_jni_sig_desc_opt.value(); if (reified_signature.args.size() != user_jni_sig_desc.args.size()) { COMPARE_DESCRIPTOR_FAILURE_MSG( "Number of parameters in JNI descriptor string" "did not match number of parameters in C++ function type"); } else if (!CompareJniDescriptorNodeErased(user_jni_sig_desc.ret, reified_signature.ret)) { // Assertion handling done by CompareJniDescriptorNodeErased. return false; } else { for (size_t i = 0; i < user_jni_sig_desc.args.size(); ++i) { if (!CompareJniDescriptorNodeErased(user_jni_sig_desc.args[i], reified_signature.args[i])) { // Assertion handling done by CompareJniDescriptorNodeErased. return false; } } } return true; } // Supports inferring the JNI function descriptor string from the C++ // function type when all type components are final. template struct InferJniDescriptor { static constexpr size_t kMaxSize = FunctionTypeMetafunction::count; // Convert the C++ function type into a JniSignatureDescriptor which holds // the canonical (according to jni_traits) descriptors for each component. // The C++ type -> JNI mapping must be nonambiguous (see jni_macros.h for exact rules). // // If conversion fails (i.e. C++ signatures is illegal for JNI, or the types are ambiguous): // if parsing is fatal -> assertion failure (default behavior) // if parsing is nonfatal -> returns nullopt (test behavior). static constexpr ConstexprOptional> FromFunctionType() { constexpr size_t kReifiedMaxSize = kMaxSize; ConstexprOptional> reified_signature_opt = MaybeMakeReifiedJniSignature(); if (!reified_signature_opt) { // Assertion handling done by MaybeMakeReifiedJniSignature. return NullConstexprOptional{}; } ReifiedJniSignature reified_signature = reified_signature_opt.value(); JniSignatureDescriptor signature_descriptor; if (reified_signature.ret.type_finality != kFinal) { // e.g. jint, jfloatArray, jstring, jclass are ok. jobject, jthrowable, jarray are not. PARSE_FAILURE("Bad return type. Only unambigous (final) types can be used to infer a signature."); // NOLINT } signature_descriptor.ret = JniDescriptorNode{reified_signature.ret.type_descriptor}; for (size_t i = 0; i < reified_signature.args.size(); ++i) { const ReifiedJniTypeTrait& arg_trait = reified_signature.args[i]; if (arg_trait.type_finality != kFinal) { PARSE_FAILURE("Bad parameter type. Only unambigous (final) types can be used to infer a signature."); // NOLINT } signature_descriptor.args.push_back(JniDescriptorNode{ arg_trait.type_descriptor}); } return {signature_descriptor}; } // Calculate the exact string size that the JNI descriptor will be // at runtime. // // Without this we cannot allocate enough space within static storage // to fit the compile-time evaluated string. static constexpr size_t CalculateStringSize() { ConstexprOptional> signature_descriptor_opt = FromFunctionType(); if (!signature_descriptor_opt) { // Assertion handling done by FromFunctionType. return 0u; } JniSignatureDescriptor signature_descriptor = signature_descriptor_opt.value(); size_t acc_size = 1u; // All sigs start with '('. // Now add every parameter. for (size_t j = 0; j < signature_descriptor.args.size(); ++j) { const JniDescriptorNode& arg_descriptor = signature_descriptor.args[j]; // for (const JniDescriptorNode& arg_descriptor : signature_descriptor.args) { acc_size += arg_descriptor.longy.size(); } acc_size += 1u; // Add space for ')'. // Add space for the return value. acc_size += signature_descriptor.ret.longy.size(); return acc_size; } static constexpr size_t kMaxStringSize = CalculateStringSize(); using ConstexprStringDescriptorType = ConstexprArray; // Convert the JniSignatureDescriptor we get in FromFunctionType() // into a flat constexpr char array. // // This is done by repeated string concatenation at compile-time. static constexpr ConstexprStringDescriptorType GetString() { ConstexprStringDescriptorType c_str{}; ConstexprOptional> signature_descriptor_opt = FromFunctionType(); if (!signature_descriptor_opt.has_value()) { // Assertion handling done by FromFunctionType. c_str[0] = '\0'; return c_str; } JniSignatureDescriptor signature_descriptor = signature_descriptor_opt.value(); size_t pos = 0u; c_str[pos++] = '('; // Copy all parameter descriptors. for (size_t j = 0; j < signature_descriptor.args.size(); ++j) { const JniDescriptorNode& arg_descriptor = signature_descriptor.args[j]; ConstexprStringView longy = arg_descriptor.longy; for (size_t i = 0; i < longy.size(); ++i) { c_str[pos++] = longy[i]; } } c_str[pos++] = ')'; // Copy return descriptor. ConstexprStringView longy = signature_descriptor.ret.longy; for (size_t i = 0; i < longy.size(); ++i) { c_str[pos++] = longy[i]; } X_ASSERT(pos == kMaxStringSize); c_str[pos] = '\0'; return c_str; } // Turn a pure constexpr string into one that can be accessed at non-constexpr // time. Note that the 'static constexpr' storage must be in the scope of a // function (prior to C++17) to avoid linking errors. static const char* GetStringAtRuntime() { static constexpr ConstexprStringDescriptorType str = GetString(); return &str[0]; } }; // Expression to return JNINativeMethod, performs checking on signature+fn. #define MAKE_CHECKED_JNI_NATIVE_METHOD(native_kind, name_, signature_, fn) \ ([]() { \ using namespace nativehelper::detail; /* NOLINT(google-build-using-namespace) */ \ static_assert( \ MatchJniDescriptorWithFunctionType(signature_),\ "JNI signature doesn't match C++ function type."); \ /* Suppress implicit cast warnings by explicitly casting. */ \ return JNINativeMethod { \ const_cast(name_), \ const_cast(signature_), \ reinterpret_cast(&(fn))}; \ })() // Expression to return JNINativeMethod, infers signature from fn. #define MAKE_INFERRED_JNI_NATIVE_METHOD(native_kind, name_, fn) \ ([]() { \ using namespace nativehelper::detail; /* NOLINT(google-build-using-namespace) */ \ /* Suppress implicit cast warnings by explicitly casting. */ \ return JNINativeMethod { \ const_cast(name_), \ const_cast( \ InferJniDescriptor::GetStringAtRuntime()), \ reinterpret_cast(&(fn))}; \ })() } // namespace detail } // namespace nativehelper platform_include/nativehelper/jni_macros.h0100644 0000000 0000000 00000031370 13756723631 020140 0ustar000000000 0000000 /* * Copyright (C) 2017 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. */ /** * Compile-time, zero-cost checking of JNI signatures against their C++ function type. * This can trigger compile-time assertions if any of the input is invalid: * (a) The signature specified does not conform to the JNI function descriptor syntax. * (b) The C++ function is itself an invalid JNI function (e.g. missing JNIEnv*, etc). * (c) The descriptor does not match the C++ function (e.g. "()V" will not match jint(jint)). * * The fundamental macros are as following: * MAKE_JNI_[FAST_|CRITICAL_]NATIVE_METHOD - Create a checked JNINativeMethod{name, sig, func}. * MAKE_JNI_[FAST_|CRITICAL_]NATIVE_METHOD_AUTOSIG - Same as above, but infer the JNI signature. * * Usage examples: * // path/to/package/KlassName.java * class KlassName { * native jobject normal(int x); * @FastNative native jobject fast(int x); * @CriticalNative native int critical(long ptr); * } * // path_to_package_KlassName.cpp * jobject KlassName_normal(JNIEnv*,jobject,jint) {...} * jobject KlassName_fast(JNIEnv*,jobject,jint) {...} * jint KlassName_critical(jlong) {...} * * // Manually specify each signature: * JNINativeMethod[] gMethods = { * MAKE_JNI_NATIVE_METHOD("normal", "(I)Ljava/lang/Object;", KlassName_normal), * MAKE_JNI_FAST_NATIVE_METHOD("fast", "(I)Ljava/lang/Object;", KlassName_fast), * MAKE_JNI_CRITICAL_NATIVE_METHOD("critical", "(Z)I", KlassName_critical), * }; * * // Automatically infer the signature: * JNINativeMethod[] gMethodsAutomaticSignature = { * MAKE_JNI_NATIVE_METHOD_AUTOSIG("normal", KlassName_normal), * MAKE_JNI_FAST_NATIVE_METHOD_AUTOSIG("fast", KlassName_fast), * MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG("critical", KlassName_critical), * }; * * // and then call JNIEnv::RegisterNatives with gMethods as usual. * * For convenience the following macros are defined: * [FAST_|CRITICAL_]NATIVE_METHOD - Return JNINativeMethod for class, func name, and signature. * OVERLOADED_[FAST_|CRITICAL_]NATIVE_METHOD - Same as above but allows a separate func identifier. * [FAST_|CRITICAL_]NATIVE_METHOD_AUTOSIG - Return JNINativeMethod, sig inferred from function. * * The FAST_ prefix corresponds to functions annotated with @FastNative, * and the CRITICAL_ prefix corresponds to functions annotated with @CriticalNative. * See dalvik.annotation.optimization.CriticalNative for more details. * * ======================================= * Checking rules * ======================================= * * --------------------------------------- * JNI descriptor syntax for functions * * Refer to "Chapter 3: JNI Types and Data Structures" of the JNI specification * under the subsection "Type Signatures" table entry "method type". * * JNI signatures not conforming to the above syntax are rejected. * --------------------------------------- * C++ function types * * A normal or @FastNative JNI function type must be of the form * * ReturnType (JNIEnv*, jclass|jobject, [ArgTypes...]) {} * * A @CriticalNative JNI function type: * * must be of the form... ReturnType ([ArgTypes...]){} * and must not contain any Reference Types. * * Refer to "Chapter 3: JNI Types and Data Structures" of the JNI specification * under the subsection "Primitive Types" and "Reference Types" for the list * of valid argument/return types. * * C++ function types not conforming to the above requirements are rejected. * --------------------------------------- * Matching of C++ function type against JNI function descriptor. * * Assuming all of the above conditions are met for signature and C++ type validity, * then matching between the signature and the type validity can occur: * * Given a signature (Args...)Ret and the * C++ function type of the form "CRet fn(JNIEnv*, jclass|jobject, CArgs...)", * or for @CriticalNative of the form "CRet fn(CArgs...)" * * The number of Args... and the number of CArgs... must be equal. * * If so, attempt to match every component from the signature and function type * against each other: * * ReturnType: * V <-> void * ArgumentType * * ArgumentType: * PrimitiveType * ReferenceType [except for @CriticalNative] * * PrimitiveType: * Z <-> jboolean * B <-> jbyte * C <-> jchar * S <-> jshort * I <-> jint * J <-> jlong * F <-> jfloat * D <-> jdouble * * ReferenceType: * Ljava/lang/String; <-> jstring * Ljava/lang/Class; <-> jclass * L*; <- jobject * Ljava/lang/Throwable; -> jthrowable * L*; <- jthrowable * [ PrimitiveType <-> ${CPrimitiveType}Array * [ ReferenceType <-> jobjectArray * [* <- jarray * * Wherein <-> represents a strong match (if the left or right pattern occurs, * then left must match right, otherwise matching fails). <- and -> represent * weak matches (that is, other match rules can be still attempted). * * Sidenote: Whilst a jobject could also represent a jclass, jstring, etc, * the stricter approach is taken: the most exact C++ type must be used. */ #ifndef NATIVEHELPER_JNI_MACROS_H #define NATIVEHELPER_JNI_MACROS_H // The below basic macros do not perform automatic stringification, // invoked e.g. as MAKE_JNI_NATIVE_METHOD("some_name", "()V", void_fn) // An expression that evaluates to JNINativeMethod { name, signature, function }, // and applies the above compile-time checking for signature+function. // The equivalent Java Language code must not be annotated with @FastNative/@CriticalNative. #define MAKE_JNI_NATIVE_METHOD(name, signature, function) \ _NATIVEHELPER_JNI_MAKE_METHOD(kNormalNative, name, signature, function) // An expression that evaluates to JNINativeMethod { name, signature, function }, // and applies the above compile-time checking for signature+function. // The equivalent Java Language code must be annotated with @FastNative. #define MAKE_JNI_FAST_NATIVE_METHOD(name, signature, function) \ _NATIVEHELPER_JNI_MAKE_METHOD(kFastNative, name, signature, function) // An expression that evaluates to JNINativeMethod { name, signature, function }, // and applies the above compile-time checking for signature+function. // The equivalent Java Language code must be annotated with @CriticalNative. #define MAKE_JNI_CRITICAL_NATIVE_METHOD(name, signature, function) \ _NATIVEHELPER_JNI_MAKE_METHOD(kCriticalNative, name, signature, function) // Automatically signature-inferencing macros are also available, // which also checks the C++ function types for validity: // An expression that evalutes to JNINativeMethod { name, infersig(function), function) } // by inferring the signature at compile-time. Only works when the C++ function type // corresponds to one unambigous JNI parameter (e.g. 'jintArray' -> '[I' but 'jobject' -> ???). // // The equivalent Java Language code must not be annotated with @FastNative/@CriticalNative. #define MAKE_JNI_NATIVE_METHOD_AUTOSIG(name, function) \ _NATIVEHELPER_JNI_MAKE_METHOD_AUTOSIG(kNormalNative, name, function) // An expression that evalutes to JNINativeMethod { name, infersig(function), function) } // by inferring the signature at compile-time. Only works when the C++ function type // corresponds to one unambigous JNI parameter (e.g. 'jintArray' -> '[I' but 'jobject' -> ???). // // The equivalent Java Language code must be annotated with @FastNative. #define MAKE_JNI_FAST_NATIVE_METHOD_AUTOSIG(name, function) \ _NATIVEHELPER_JNI_MAKE_METHOD_AUTOSIG(kFastNative, name, function) // An expression that evalutes to JNINativeMethod { name, infersig(function), function) } // by inferring the signature at compile-time. // // The equivalent Java Language code must be annotated with @CriticalNative. #define MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG(name, function) \ _NATIVEHELPER_JNI_MAKE_METHOD_AUTOSIG(kCriticalNative, name, function) // Convenience macros when the functions follow the naming convention: // .java file .cpp file // JavaLanguageName <-> ${ClassName}_${JavaLanguageName} // // Stringification is done automatically, invoked as: // NATIVE_[FAST_|CRITICAL]_METHOD(ClassName, JavaLanguageName, Signature) // // Intended to construct a JNINativeMethod. // (Assumes the C name is the ClassName_JavaMethodName). // // The Java Language code must be annotated with one of (none,@FastNative,@CriticalNative) // for the (none,FAST_,CRITICAL_) variants of these macros. #define NATIVE_METHOD(className, functionName, signature) \ MAKE_JNI_NATIVE_METHOD(#functionName, signature, className ## _ ## functionName) #define OVERLOADED_NATIVE_METHOD(className, functionName, signature, identifier) \ MAKE_JNI_NATIVE_METHOD(#functionName, signature, className ## _ ## identifier) #define NATIVE_METHOD_AUTOSIG(className, functionName) \ MAKE_JNI_NATIVE_METHOD_AUTOSIG(#functionName, className ## _ ## functionName) #define FAST_NATIVE_METHOD(className, functionName, signature) \ MAKE_JNI_FAST_NATIVE_METHOD(#functionName, signature, className ## _ ## functionName) #define OVERLOADED_FAST_NATIVE_METHOD(className, functionName, signature, identifier) \ MAKE_JNI_FAST_NATIVE_METHOD(#functionName, signature, className ## _ ## identifier) #define FAST_NATIVE_METHOD_AUTOSIG(className, functionName) \ MAKE_JNI_FAST_NATIVE_METHOD_AUTOSIG(#functionName, className ## _ ## functionName) #define CRITICAL_NATIVE_METHOD(className, functionName, signature) \ MAKE_JNI_CRITICAL_NATIVE_METHOD(#functionName, signature, className ## _ ## functionName) #define OVERLOADED_CRITICAL_NATIVE_METHOD(className, functionName, signature, identifier) \ MAKE_JNI_CRITICAL_NATIVE_METHOD(#functionName, signature, className ## _ ## identifier) #define CRITICAL_NATIVE_METHOD_AUTOSIG(className, functionName) \ MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG(#functionName, className ## _ ## functionName) //////////////////////////////////////////////////////// // IMPLEMENTATION ONLY. // DO NOT USE DIRECTLY. //////////////////////////////////////////////////////// #if defined(__cplusplus) && __cplusplus >= 201402L #include "nativehelper/detail/signature_checker.h" // for MAKE_CHECKED_JNI_NATIVE_METHOD #endif // Expands to an expression whose type is JNINativeMethod. // This is for older versions of C++ or C, so it has no compile-time checking. #define _NATIVEHELPER_JNI_MAKE_METHOD_OLD(kind, name, sig, fn) \ ( \ (JNINativeMethod) { \ (name), \ (sig), \ _NATIVEHELPER_JNI_MACRO_CAST(reinterpret_cast, void *)(fn) \ } \ ) // C++14 or better, use compile-time checking. #if defined(__cplusplus) && __cplusplus >= 201402L // Expands to a compound expression whose type is JNINativeMethod. #define _NATIVEHELPER_JNI_MAKE_METHOD(kind, name, sig, fn) \ MAKE_CHECKED_JNI_NATIVE_METHOD(kind, name, sig, fn) // Expands to a compound expression whose type is JNINativeMethod. #define _NATIVEHELPER_JNI_MAKE_METHOD_AUTOSIG(kind, name, function) \ MAKE_INFERRED_JNI_NATIVE_METHOD(kind, name, function) #else // Older versions of C++ or C code get the regular macro that's unchecked. // Expands to a compound expression whose type is JNINativeMethod. #define _NATIVEHELPER_JNI_MAKE_METHOD(kind, name, sig, fn) \ _NATIVEHELPER_JNI_MAKE_METHOD_OLD(kind, name, sig, fn) // Need C++14 or newer to use the AUTOSIG macros. #define _NATIVEHELPER_JNI_MAKE_METHOD_AUTOSIG(kind, name, function) \ static_assert(false, "Cannot infer JNI signatures prior to C++14 for function " #function); #endif // C++14 check // C-style cast for C, C++-style cast for C++ to avoid warnings/errors. #if defined(__cplusplus) #define _NATIVEHELPER_JNI_MACRO_CAST(which_cast, to) \ which_cast #else #define _NATIVEHELPER_JNI_MACRO_CAST(which_cast, to) \ (to) #endif #endif // NATIVEHELPER_JNI_MACROS_H tests/0040755 0000000 0000000 00000000000 13756723631 010767 5ustar000000000 0000000 tests/Android.bp0100644 0000000 0000000 00000003476 13756723631 012701 0ustar000000000 0000000 // Build the unit tests. cc_test { name: "JniInvocation_test", test_suites: ["device-tests"], host_supported: true, srcs: ["JniInvocation_test.cpp"], cflags: ["-Wall", "-Werror"], // Link to the non-stub version of the library to access some internal // functions. bootstrap: true, shared_libs: ["libnativehelper"], } cc_test { name: "JniSafeRegisterNativeMethods_test", host_supported: true, srcs: ["JniSafeRegisterNativeMethods_test.cpp"], cflags: [ // Base set of cflags used by all things ART. "-fno-rtti", "-ggdb3", "-Wall", "-Werror", "-Wextra", "-Wstrict-aliasing", "-fstrict-aliasing", "-Wunreachable-code", "-Wredundant-decls", "-Wshadow", "-Wunused", "-fvisibility=protected", // Warn about thread safety violations with clang. "-Wthread-safety", "-Wthread-safety-negative", // Warn if switch fallthroughs aren't annotated. "-Wimplicit-fallthrough", // Enable float equality warnings. "-Wfloat-equal", // Enable warning of converting ints to void*. "-Wint-to-void-pointer-cast", // Enable warning of wrong unused annotations. "-Wused-but-marked-unused", // Enable warning for deprecated language features. "-Wdeprecated", // Enable warning for unreachable break & return. "-Wunreachable-code-break", "-Wunreachable-code-return", // Enable thread annotations for std::mutex, etc. "-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS", ], tidy: true, shared_libs: ["libnativehelper"], } cc_test { name: "libnativehelper_api_test", host_supported: true, cflags: ["-Wall", "-Werror"], srcs: ["libnativehelper_api_test.c"], // C Compilation test. tidy: true, shared_libs: ["libnativehelper"], } tests/AndroidTest.xml0100644 0000000 0000000 00000002336 13756723631 013732 0ustar000000000 0000000 tests/JniInvocation_test.cpp0100644 0000000 0000000 00000005556 13756723631 015314 0ustar000000000 0000000 /* * Copyright (C) 2014 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 "NativeBridge_test" #include #include #include "string.h" #if defined(__ANDROID__) && defined(__BIONIC__) #define HAVE_TEST_STUFF 1 #else #undef HAVE_TEST_STUFF #endif #ifdef HAVE_TEST_STUFF // PROPERTY_VALUE_MAX. #include "cutils/properties.h" #endif #ifdef HAVE_TEST_STUFF static const char* kTestNonNull = "libartd.so"; static const char* kTestNonNull2 = "libartd2.so"; static const char* kExpected = "libart.so"; #endif TEST(JNIInvocation, Debuggable) { #ifdef HAVE_TEST_STUFF auto is_debuggable = []() { return true; }; auto get_library_system_property = [](char* buffer) -> int { strcpy(buffer, kTestNonNull2); return sizeof(kTestNonNull2); }; char buffer[PROPERTY_VALUE_MAX]; const char* result = JniInvocation::GetLibrary(NULL, buffer, is_debuggable, get_library_system_property); EXPECT_FALSE(result == NULL); if (result != NULL) { EXPECT_TRUE(strcmp(result, kTestNonNull2) == 0); EXPECT_FALSE(strcmp(result, kExpected) == 0); } result = JniInvocation::GetLibrary(kTestNonNull, buffer, is_debuggable, get_library_system_property); EXPECT_FALSE(result == NULL); if (result != NULL) { EXPECT_TRUE(strcmp(result, kTestNonNull) == 0); EXPECT_FALSE(strcmp(result, kTestNonNull2) == 0); } #else GTEST_LOG_(WARNING) << "Host testing unsupported. Please run target tests."; #endif } TEST(JNIInvocation, NonDebuggable) { #ifdef HAVE_TEST_STUFF auto is_debuggable = []() { return false; }; char buffer[PROPERTY_VALUE_MAX]; const char* result = JniInvocation::GetLibrary(NULL, buffer, is_debuggable, nullptr); EXPECT_FALSE(result == NULL); if (result != NULL) { EXPECT_TRUE(strcmp(result, kExpected) == 0); EXPECT_FALSE(strcmp(result, kTestNonNull) == 0); EXPECT_FALSE(strcmp(result, kTestNonNull2) == 0); } result = JniInvocation::GetLibrary(kTestNonNull, buffer, is_debuggable, nullptr); EXPECT_FALSE(result == NULL); if (result != NULL) { EXPECT_TRUE(strcmp(result, kExpected) == 0); EXPECT_FALSE(strcmp(result, kTestNonNull) == 0); } #else GTEST_LOG_(WARNING) << "Host testing unsupported. Please run target tests."; #endif } tests/JniSafeRegisterNativeMethods_test.cpp0100644 0000000 0000000 00000154632 13756723631 020261 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wused-but-marked-unused" #pragma clang diagnostic ignored "-Wdeprecated-dynamic-exception-spec" #pragma clang diagnostic ignored "-Wdeprecated" #include #pragma clang diagnostic pop #include #define PARSE_FAILURES_NONFATAL // return empty optionals wherever possible instead of asserting. #include "nativehelper/jni_macros.h" // Provide static storage to these values so they can be used in a runtime context. // This has to be defined local to the test translation unit to avoid ODR violations prior to C++17. #define STORAGE_FN_FOR_JNI_TRAITS(jtype, ...) \ constexpr char nativehelper::detail::jni_type_trait::type_descriptor[]; \ constexpr char nativehelper::detail::jni_type_trait::type_name[]; DEFINE_JNI_TYPE_TRAIT(STORAGE_FN_FOR_JNI_TRAITS) template std::string stringify_helper(const T& val) { std::stringstream ss; ss << val; return ss.str(); } #define EXPECT_STRINGIFY_EQ(x, y) EXPECT_EQ(stringify_helper(x), stringify_helper(y)) TEST(JniSafeRegisterNativeMethods, StringParsing) { using namespace nativehelper::detail; // NOLINT // Super basic bring-up tests for core functionality. { constexpr ConstexprStringView v_str = "V"; EXPECT_EQ(1u, v_str.size()); EXPECT_EQ(false, v_str.empty()); std::stringstream ss; ss << v_str; EXPECT_EQ("V", ss.str()); } { auto parse = ParseSingleTypeDescriptor("", /*allow_void*/true); EXPECT_EQ("", parse->token); EXPECT_EQ("", parse->remainder); } { auto parse = ParseSingleTypeDescriptor("V", /*allow_void*/true); EXPECT_EQ("V", parse->token); EXPECT_EQ("", parse->remainder); } { auto parse = ParseSingleTypeDescriptor("[I"); EXPECT_EQ("[I", parse->token); EXPECT_EQ("", parse->remainder); } { auto parse = ParseSingleTypeDescriptor("LObject;"); EXPECT_EQ("LObject;", parse->token); EXPECT_EQ("", parse->remainder); } { auto parse = ParseSingleTypeDescriptor("LBadObject);"); EXPECT_FALSE(parse.has_value()); } { auto parse = ParseSingleTypeDescriptor("LBadObject(;"); EXPECT_FALSE(parse.has_value()); } { auto parse = ParseSingleTypeDescriptor("LBadObject[;"); EXPECT_FALSE(parse.has_value()); } // Stringify is used for convenience to make writing out tests easier. // Transforms as "(XYZ)W" -> "args={X,Y,Z}, ret=W" #define PARSE_SIGNATURE_AS_LIST(str) (ParseSignatureAsList(str)) { constexpr auto jni_descriptor = PARSE_SIGNATURE_AS_LIST("()V"); EXPECT_STRINGIFY_EQ("args={}, ret=V", jni_descriptor); } { constexpr auto jni_descriptor = PARSE_SIGNATURE_AS_LIST("()Ljava/lang/Object;"); EXPECT_STRINGIFY_EQ("args={}, ret=Ljava/lang/Object;", jni_descriptor); } { constexpr auto jni_descriptor = PARSE_SIGNATURE_AS_LIST("()[I"); EXPECT_STRINGIFY_EQ("args={}, ret=[I", jni_descriptor); } #define EXPECT_OK_SIGNATURE_PARSE(signature, args, ret) \ do { \ constexpr auto jni_descriptor = PARSE_SIGNATURE_AS_LIST(signature); \ EXPECT_EQ(true, jni_descriptor.has_value()); \ EXPECT_STRINGIFY_EQ("args={" args "}, ret=" ret, jni_descriptor); \ } while (0) // Exhaustive tests for successful parsing. EXPECT_OK_SIGNATURE_PARSE("()V", /*args*/"", /*ret*/"V"); EXPECT_OK_SIGNATURE_PARSE("()Z", /*args*/"", /*ret*/"Z"); EXPECT_OK_SIGNATURE_PARSE("()B", /*args*/"", /*ret*/"B"); EXPECT_OK_SIGNATURE_PARSE("()C", /*args*/"", /*ret*/"C"); EXPECT_OK_SIGNATURE_PARSE("()S", /*args*/"", /*ret*/"S"); EXPECT_OK_SIGNATURE_PARSE("()I", /*args*/"", /*ret*/"I"); EXPECT_OK_SIGNATURE_PARSE("()F", /*args*/"", /*ret*/"F"); EXPECT_OK_SIGNATURE_PARSE("()J", /*args*/"", /*ret*/"J"); EXPECT_OK_SIGNATURE_PARSE("()D", /*args*/"", /*ret*/"D"); EXPECT_OK_SIGNATURE_PARSE("()Ljava/lang/Object;", /*args*/"", /*ret*/"Ljava/lang/Object;"); EXPECT_OK_SIGNATURE_PARSE("()[Ljava/lang/Object;", /*args*/"", /*ret*/"[Ljava/lang/Object;"); EXPECT_OK_SIGNATURE_PARSE("()[I", /*args*/"", /*ret*/"[I"); EXPECT_OK_SIGNATURE_PARSE("()[[I", /*args*/"", /*ret*/"[[I"); EXPECT_OK_SIGNATURE_PARSE("()[[[I", /*args*/"", /*ret*/"[[[I"); EXPECT_OK_SIGNATURE_PARSE("(Z)V", /*args*/"Z", /*ret*/"V"); EXPECT_OK_SIGNATURE_PARSE("(B)V", /*args*/"B", /*ret*/"V"); EXPECT_OK_SIGNATURE_PARSE("(C)D", /*args*/"C", /*ret*/"D"); EXPECT_OK_SIGNATURE_PARSE("(S)V", /*args*/"S", /*ret*/"V"); EXPECT_OK_SIGNATURE_PARSE("(I)V", /*args*/"I", /*ret*/"V"); EXPECT_OK_SIGNATURE_PARSE("(F)V", /*args*/"F", /*ret*/"V"); EXPECT_OK_SIGNATURE_PARSE("(J)F", /*args*/"J", /*ret*/"F"); EXPECT_OK_SIGNATURE_PARSE("(D)V", /*args*/"D", /*ret*/"V"); EXPECT_OK_SIGNATURE_PARSE("(Ljava/lang/Object;)V", "Ljava/lang/Object;", "V"); EXPECT_OK_SIGNATURE_PARSE("([Ljava/lang/Object;)V", "[Ljava/lang/Object;", "V"); EXPECT_OK_SIGNATURE_PARSE("([I)V", /*ret*/"[I", "V"); EXPECT_OK_SIGNATURE_PARSE("([[I)V", /*ret*/"[[I", "V"); EXPECT_OK_SIGNATURE_PARSE("([[[I)V", /*ret*/"[[[I", "V"); EXPECT_OK_SIGNATURE_PARSE("(ZIJ)V", /*args*/"Z,I,J", /*ret*/"V"); EXPECT_OK_SIGNATURE_PARSE("(B[IJ)V", /*args*/"B,[I,J", /*ret*/"V"); EXPECT_OK_SIGNATURE_PARSE("(Ljava/lang/Object;B)D", /*args*/"Ljava/lang/Object;,B", /*ret*/"D"); EXPECT_OK_SIGNATURE_PARSE("(Ljava/lang/Object;Ljava/lang/String;IF)D", /*args*/"Ljava/lang/Object;,Ljava/lang/String;,I,F", /*ret*/"D"); EXPECT_OK_SIGNATURE_PARSE("([[[Ljava/lang/Object;Ljava/lang/String;IF)D", /*args*/"[[[Ljava/lang/Object;,Ljava/lang/String;,I,F", /*ret*/"D"); /* * Test Failures in Parsing */ #define EXPECT_FAILED_SIGNATURE_PARSE(jni_descriptor) \ EXPECT_STRINGIFY_EQ(ConstexprOptional>{},\ ParseSignatureAsList(jni_descriptor)) // For the failures to work we must turn off 'PARSE_FAILURES_FATAL'. // Otherwise they immediately cause a crash, which is actually the desired behavior // when this is used by the end-user in REGISTER_NATIVE_METHOD. { EXPECT_FAILED_SIGNATURE_PARSE(""); EXPECT_FAILED_SIGNATURE_PARSE("A"); EXPECT_FAILED_SIGNATURE_PARSE(")"); EXPECT_FAILED_SIGNATURE_PARSE("V"); EXPECT_FAILED_SIGNATURE_PARSE("("); EXPECT_FAILED_SIGNATURE_PARSE("(A"); EXPECT_FAILED_SIGNATURE_PARSE("()"); EXPECT_FAILED_SIGNATURE_PARSE("()A"); EXPECT_FAILED_SIGNATURE_PARSE("()VV"); EXPECT_FAILED_SIGNATURE_PARSE("()L"); EXPECT_FAILED_SIGNATURE_PARSE("()L;"); EXPECT_FAILED_SIGNATURE_PARSE("()BAD;"); EXPECT_FAILED_SIGNATURE_PARSE("()Ljava/lang/Object"); EXPECT_FAILED_SIGNATURE_PARSE("()Ljava/lang/Object;X"); EXPECT_FAILED_SIGNATURE_PARSE("(V)V"); EXPECT_FAILED_SIGNATURE_PARSE("(ILcat)V"); EXPECT_FAILED_SIGNATURE_PARSE("([dog)V"); EXPECT_FAILED_SIGNATURE_PARSE("(IV)V"); EXPECT_FAILED_SIGNATURE_PARSE("([V)V"); EXPECT_FAILED_SIGNATURE_PARSE("([[V)V"); EXPECT_FAILED_SIGNATURE_PARSE("()v"); EXPECT_FAILED_SIGNATURE_PARSE("()i"); EXPECT_FAILED_SIGNATURE_PARSE("()f"); } } #define EXPECT_IS_VALID_JNI_ARGUMENT_TYPE(expected, expr) \ { constexpr bool is_valid = (expr); \ EXPECT_EQ(expected, is_valid) << #expr; \ } // Basic smoke tests for parameter validity. // See below for more exhaustive tests. TEST(JniSafeRegisterNativeMethods, ParameterTypes) { using namespace nativehelper::detail; // NOLINT EXPECT_TRUE(IsJniParameterCountValid(kCriticalNative, 0u)); EXPECT_TRUE(IsJniParameterCountValid(kCriticalNative, 1u)); EXPECT_TRUE(IsJniParameterCountValid(kCriticalNative, 2u)); EXPECT_TRUE(IsJniParameterCountValid(kCriticalNative, 3u)); EXPECT_TRUE(IsJniParameterCountValid(kCriticalNative, 4u)); EXPECT_FALSE(IsJniParameterCountValid(kNormalNative, 0u)); EXPECT_FALSE(IsJniParameterCountValid(kNormalNative, 1u)); EXPECT_TRUE(IsJniParameterCountValid(kNormalNative, 2u)); EXPECT_TRUE(IsJniParameterCountValid(kNormalNative, 3u)); EXPECT_TRUE(IsJniParameterCountValid(kNormalNative, 4u)); EXPECT_TRUE((IsValidJniParameter(kNormalNative, kReturnPosition))); EXPECT_IS_VALID_JNI_ARGUMENT_TYPE(true,(is_valid_jni_argument_type::value)); EXPECT_IS_VALID_JNI_ARGUMENT_TYPE(true,(is_valid_jni_argument_type::value)); EXPECT_IS_VALID_JNI_ARGUMENT_TYPE(true,(is_valid_jni_argument_type::value)); EXPECT_IS_VALID_JNI_ARGUMENT_TYPE(false,(is_valid_jni_argument_type::value)); } struct TestReturnAnything { template operator T() const { // NOLINT return T{}; } }; namespace test_jni { void empty_fn() {} } struct TestJni { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" // Always bad. static void bad_cptr(const char* ptr) {} static void* bad_ret_ptr() { return nullptr; } static JNIEnv* bad_ret_env() { return nullptr; } static void bad_wrongplace_env(jobject, JNIEnv*) {} static void bad_wrongplace_env2(jobject, jobject, JNIEnv*) {} static void v_e(JNIEnv*) {} static void v_ei(JNIEnv*, jint l) {} static void v_el(JNIEnv*, jlong l) {} static void v_et(JNIEnv*, jstring) {} static jobject o_none() { return nullptr; } static void bad_noref_jint_norm(JNIEnv*, jclass, jint&) {} static void bad_noref_jint_crit(jint&) {} // Good depending on the context: // CriticalNative static void empty_fn() {} static jint int_fn() { return 0; } static void v_() {} // Note: volatile,const don't participate in the function signature // but we still have these here to clarify that it is indeed allowed. static void v_vol_i(volatile jint) {} static void v_const_i(const jint) {} static void v_i(jint) {} static void v_l(jlong) {} static void v_lib(jlong, jint, jboolean) {} static jshort s_lib(jlong, jint, jboolean) { return 0; } // Normal or FastNative. static void v_eo(JNIEnv*, jobject) {} static void v_eoo(JNIEnv*, jobject, jobject) {} static void v_ek(JNIEnv*, jclass) {} static void v_eolib(JNIEnv*, jobject, jlong, jint, jboolean) {} static jshort s_eolAibA(JNIEnv*, jobject, jlongArray, jint, jbooleanArray) { return 0; } #define DEC_TEST_FN_IMPL(name, ret_t, ...) \ static ret_t name (__VA_ARGS__) { return TestReturnAnything{}; } #define DEC_TEST_FN(name, correct, ret_t, ...) \ DEC_TEST_FN_IMPL(normal_ ## name, ret_t, JNIEnv*, jobject, __VA_ARGS__) \ DEC_TEST_FN_IMPL(normal2_ ## name, ret_t, JNIEnv*, jclass, __VA_ARGS__) \ DEC_TEST_FN_IMPL(critical_ ## name, ret_t, __VA_ARGS__) #define DEC_TEST_FN0(name, correct, ret_t) \ DEC_TEST_FN_IMPL(normal_ ## name, ret_t, JNIEnv*, jobject) \ DEC_TEST_FN_IMPL(normal2_ ## name, ret_t, JNIEnv*, jclass) \ DEC_TEST_FN_IMPL(critical_ ## name, ret_t) #define JNI_TEST_FN(FN, FN0) \ FN0(a0,CRITICAL,void) \ FN0(a ,CRITICAL,jboolean) \ FN0(a1,CRITICAL,jbyte) \ FN0(g, CRITICAL,jchar) \ FN0(c, CRITICAL,jshort) \ FN0(b, CRITICAL,jint) \ FN0(f, CRITICAL,jlong) \ FN0(d, CRITICAL,jfloat) \ FN0(e, CRITICAL,jdouble) \ FN0(f2,NORMAL ,jobject) \ FN0(f3,NORMAL ,jclass) \ FN0(fr,NORMAL ,jstring) \ FN0(fa,NORMAL ,jarray) \ FN0(fb,NORMAL ,jobjectArray) \ FN0(fc,NORMAL ,jbooleanArray) \ FN0(fd,NORMAL ,jcharArray) \ FN0(fe,NORMAL ,jshortArray) \ FN0(ff,NORMAL ,jintArray) \ FN0(fg,NORMAL ,jlongArray) \ FN0(fk,NORMAL ,jfloatArray) \ FN0(fi,NORMAL ,jdoubleArray) \ FN0(fl,NORMAL ,jthrowable) \ FN(aa, CRITICAL,jboolean,jboolean) \ FN(ax, CRITICAL,jbyte,jbyte) \ FN(ag, CRITICAL,jchar,jchar) \ FN(ac, CRITICAL,jshort,jshort) \ FN(ac2,CRITICAL,jshort,jshort,jchar) \ FN(ab, CRITICAL,jint,jint) \ FN(af, CRITICAL,jlong,jlong) \ FN(ad, CRITICAL,jfloat,jfloat) \ FN(ae, CRITICAL,jdouble,jdouble) \ FN(af2,NORMAL ,jobject,jobject) \ FN(af3,NORMAL ,jclass,jclass) \ FN(afr,NORMAL ,jstring,jstring) \ FN(afa,NORMAL ,jarray,jarray) \ FN(afb,NORMAL ,jobjectArray,jobjectArray) \ FN(afc,NORMAL ,jbooleanArray,jbooleanArray) \ FN(afd,NORMAL ,jcharArray,jcharArray) \ FN(afe,NORMAL ,jshortArray,jshortArray) \ FN(aff,NORMAL ,jintArray,jintArray) \ FN(afg,NORMAL ,jlongArray,jlongArray) \ FN(afk,NORMAL ,jfloatArray,jfloatArray) \ FN(afi,NORMAL ,jdoubleArray,jdoubleArray) \ FN(agi,NORMAL ,jdoubleArray,jdoubleArray,jobject) \ FN(afl,NORMAL ,jthrowable,jthrowable) \ \ FN0(z0,ILLEGAL ,JNIEnv*) \ FN(z1, ILLEGAL ,void, JNIEnv*) \ FN(z2, ILLEGAL ,JNIEnv*, JNIEnv*) \ FN(z3, ILLEGAL ,void, void*) \ FN0(z4,ILLEGAL ,void*) \ #define JNI_TEST_FN_BOTH(x) JNI_TEST_FN(x,x) // we generate a return statement because some functions are non-void. // disable the useless warning about returning from a non-void function. #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wreturn-type" JNI_TEST_FN(DEC_TEST_FN, DEC_TEST_FN0); #pragma clang diagnostic pop // TODO: probably should be an x-macro table // and that way we can add critical/normal to it as well // and also the type descriptor, and reuse this for multiple tests. #pragma clang diagnostic pop }; // Note: Using function-local structs does not work. // Template parameters must have linkage, which function-local structs lack. TEST(JniSafeRegisterNativeMethods, FunctionTypes) { using namespace nativehelper::detail; // NOLINT // The exact error messages are not tested but they would be seen in the compiler // stack trace when used from a constexpr context. #define IS_VALID_JNI_FUNCTION_TYPE(native_kind, func) \ (IsValidJniFunctionType()) #define IS_VALID_NORMAL_JNI_FUNCTION_TYPE(func) IS_VALID_JNI_FUNCTION_TYPE(kNormalNative, func) #define IS_VALID_CRITICAL_JNI_FUNCTION_TYPE(func) IS_VALID_JNI_FUNCTION_TYPE(kCriticalNative, func) #define EXPECT_ILLEGAL_JNI_FUNCTION_TYPE(func) \ do { \ EXPECT_FALSE(IS_VALID_CRITICAL_JNI_FUNCTION_TYPE(func)); \ EXPECT_FALSE(IS_VALID_NORMAL_JNI_FUNCTION_TYPE(func)); \ } while (false) #define EXPECT_NORMAL_JNI_FUNCTION_TYPE(func) \ do { \ EXPECT_FALSE(IS_VALID_CRITICAL_JNI_FUNCTION_TYPE(func)); \ EXPECT_TRUE(IS_VALID_NORMAL_JNI_FUNCTION_TYPE(func)); \ } while (false) #define EXPECT_CRITICAL_JNI_FUNCTION_TYPE(func) \ do { \ EXPECT_TRUE(IS_VALID_CRITICAL_JNI_FUNCTION_TYPE(func)); \ EXPECT_FALSE(IS_VALID_NORMAL_JNI_FUNCTION_TYPE(func)); \ } while (false) { EXPECT_ILLEGAL_JNI_FUNCTION_TYPE(TestJni::bad_cptr); EXPECT_ILLEGAL_JNI_FUNCTION_TYPE(TestJni::bad_ret_ptr); EXPECT_ILLEGAL_JNI_FUNCTION_TYPE(TestJni::bad_ret_env); EXPECT_ILLEGAL_JNI_FUNCTION_TYPE(TestJni::bad_wrongplace_env); EXPECT_ILLEGAL_JNI_FUNCTION_TYPE(TestJni::bad_wrongplace_env2); EXPECT_CRITICAL_JNI_FUNCTION_TYPE(TestJni::empty_fn); EXPECT_CRITICAL_JNI_FUNCTION_TYPE(test_jni::empty_fn); EXPECT_CRITICAL_JNI_FUNCTION_TYPE(TestJni::int_fn); EXPECT_CRITICAL_JNI_FUNCTION_TYPE(TestJni::v_); EXPECT_CRITICAL_JNI_FUNCTION_TYPE(TestJni::v_vol_i); EXPECT_CRITICAL_JNI_FUNCTION_TYPE(TestJni::v_const_i); EXPECT_CRITICAL_JNI_FUNCTION_TYPE(TestJni::v_i); EXPECT_CRITICAL_JNI_FUNCTION_TYPE(TestJni::v_l); EXPECT_ILLEGAL_JNI_FUNCTION_TYPE(TestJni::v_e); EXPECT_ILLEGAL_JNI_FUNCTION_TYPE(TestJni::v_ei); EXPECT_ILLEGAL_JNI_FUNCTION_TYPE(TestJni::v_el); EXPECT_ILLEGAL_JNI_FUNCTION_TYPE(TestJni::v_et); EXPECT_NORMAL_JNI_FUNCTION_TYPE(TestJni::v_eo); EXPECT_NORMAL_JNI_FUNCTION_TYPE(TestJni::v_ek); EXPECT_ILLEGAL_JNI_FUNCTION_TYPE(TestJni::o_none); EXPECT_ILLEGAL_JNI_FUNCTION_TYPE(TestJni::bad_noref_jint_norm); EXPECT_ILLEGAL_JNI_FUNCTION_TYPE(TestJni::bad_noref_jint_crit); } enum class TestJniKind { ILLEGAL, NORMAL, CRITICAL }; // ILLEGAL signatures are always illegal. bool kExpected_ILLEGAL_against_NORMAL = false; bool kExpected_ILLEGAL_against_CRITICAL = false; // NORMAL signatures are only legal for Normal JNI. bool kExpected_NORMAL_against_NORMAL = true; bool kExpected_NORMAL_against_CRITICAL = false; // CRITICAL signatures are legal for both Normal+Critical JNI. bool kExpected_CRITICAL_against_CRITICAL = true; bool kExpected_CRITICAL_against_NORMAL = true; // Note that we munge normal and critical type signatures separately // and that a normal_ prefixed is always a bad critical signature, // and a critical_ prefixed signature is always a bad normal signature. // See JNI_TEST_FN_MAKE_TEST for the implementation of this logic. #undef EXPECTED_FOR #define EXPECTED_FOR(jni_kind, context) \ (kExpected_ ## jni_kind ## _against_ ## context) { #define JNI_TEST_FN_MAKE_TEST(name, jni_kind, ...) \ do { \ EXPECT_EQ(EXPECTED_FOR(jni_kind, NORMAL), \ IS_VALID_NORMAL_JNI_FUNCTION_TYPE(TestJni::normal_ ## name)); \ EXPECT_FALSE(IS_VALID_CRITICAL_JNI_FUNCTION_TYPE(TestJni::normal_ ## name)); \ EXPECT_EQ(EXPECTED_FOR(jni_kind, NORMAL), \ IS_VALID_NORMAL_JNI_FUNCTION_TYPE(TestJni::normal2_ ## name)); \ EXPECT_FALSE(IS_VALID_CRITICAL_JNI_FUNCTION_TYPE(TestJni::normal2_ ## name)); \ EXPECT_EQ(EXPECTED_FOR(jni_kind, CRITICAL), \ IS_VALID_CRITICAL_JNI_FUNCTION_TYPE(TestJni::critical_ ## name)); \ EXPECT_FALSE(IS_VALID_NORMAL_JNI_FUNCTION_TYPE(TestJni::critical_ ## name)); \ } while (false); JNI_TEST_FN_BOTH(JNI_TEST_FN_MAKE_TEST); } } #define EXPECT_CONSTEXPR_EQ(lhs, rhs) \ { constexpr auto lhs_val = (lhs); \ constexpr auto rhs_val = (rhs); \ EXPECT_EQ(lhs_val, rhs_val) << "LHS: " << #lhs << ", RHS: " << #rhs; \ } TEST(JniSafeRegisterNativeMethods, FunctionTypeDescriptorConversion) { using namespace nativehelper::detail; // NOLINT { constexpr auto cvrt = MaybeMakeReifiedJniSignature(); ASSERT_TRUE(cvrt.has_value()); EXPECT_CONSTEXPR_EQ(2u, cvrt->max_size); EXPECT_CONSTEXPR_EQ(1u, cvrt->args.size()); EXPECT_STRINGIFY_EQ("args={jint}, ret=void", cvrt.value()); } { constexpr auto cvrt = MaybeMakeReifiedJniSignature(); EXPECT_FALSE(cvrt.has_value()); } { constexpr auto cvrt = MaybeMakeReifiedJniSignature(); ASSERT_TRUE(cvrt.has_value()); EXPECT_EQ(2u, cvrt->args.size()); EXPECT_STRINGIFY_EQ("args={jdoubleArray,jobject}, ret=jdoubleArray", cvrt.value()); } { constexpr auto cvrt = MaybeMakeReifiedJniSignature(); ASSERT_TRUE(cvrt.has_value()); EXPECT_EQ(2u, cvrt->args.size()); EXPECT_STRINGIFY_EQ("args={jshort,jchar}, ret=jshort", cvrt.value()); } // TODO: use JNI_TEST_FN to generate these tests automatically. } struct test_function_traits { static int int_returning_function() { return 0; } }; template struct apply_return_type { constexpr int operator()() const { return sizeof(T) == sizeof(int); } }; #define FN_ARGS_PAIR(fn) decltype(fn), (fn) TEST(JniSafeRegisterNativeMethods, FunctionTraits) { using namespace nativehelper::detail; // NOLINT using traits_for_int_ret = FunctionTypeMetafunction; int applied = traits_for_int_ret::map_return(); EXPECT_EQ(1, applied); auto arr = traits_for_int_ret::map_args(); EXPECT_EQ(0u, arr.size()); } struct IntHolder { int value; }; constexpr int GetTestValue(const IntHolder& i) { return i.value; } constexpr int GetTestValue(int i) { return i; } template constexpr size_t SumUpVector(const nativehelper::detail::ConstexprVector& vec) { size_t s = 0; for (const T& elem : vec) { s += static_cast(GetTestValue(elem)); } return s; } template constexpr auto make_test_int_vector() { using namespace nativehelper::detail; // NOLINT ConstexprVector vec_int; vec_int.push_back(T{1}); vec_int.push_back(T{2}); vec_int.push_back(T{3}); vec_int.push_back(T{4}); vec_int.push_back(T{5}); return vec_int; } TEST(JniSafeRegisterNativeMethods, ConstexprOptional) { using namespace nativehelper::detail; // NOLINT ConstexprOptional int_opt; EXPECT_FALSE(int_opt.has_value()); int_opt = ConstexprOptional(12345); EXPECT_EQ(12345, int_opt.value()); EXPECT_EQ(12345, *int_opt); } TEST(JniSafeRegisterNativeMethods, ConstexprVector) { using namespace nativehelper::detail; // NOLINT { constexpr ConstexprVector vec_int = make_test_int_vector(); constexpr size_t the_sum = SumUpVector(vec_int); EXPECT_EQ(15u, the_sum); } { constexpr ConstexprVector vec_int = make_test_int_vector(); constexpr size_t the_sum = SumUpVector(vec_int); EXPECT_EQ(15u, the_sum); } } // Need this intermediate function to make a JniDescriptorNode from a string literal. // C++ doesn't do implicit conversion through two+ type constructors. constexpr nativehelper::detail::JniDescriptorNode MakeNode( nativehelper::detail::ConstexprStringView str) { return nativehelper::detail::JniDescriptorNode{str}; } #define EXPECT_EQUALISH_JNI_DESCRIPTORS_IMPL(user_desc, derived, cond) \ do { \ constexpr bool res = \ CompareJniDescriptorNodeErased(MakeNode(user_desc), \ ReifiedJniTypeTrait::Reify()); \ (void)res; \ EXPECT_ ## cond(CompareJniDescriptorNodeErased(MakeNode(user_desc), \ ReifiedJniTypeTrait::Reify())); \ } while (0); #define EXPECT_EQUALISH_JNI_DESCRIPTORS(user_desc, derived_desc) \ EXPECT_EQUALISH_JNI_DESCRIPTORS_IMPL(user_desc, derived_desc, TRUE) #define EXPECT_NOT_EQUALISH_JNI_DESCRIPTORS(user_desc, derived_desc) \ EXPECT_EQUALISH_JNI_DESCRIPTORS_IMPL(user_desc, derived_desc, FALSE) TEST(JniSafeRegisterNativeMethods, CompareJniDescriptorNodeErased) { using namespace nativehelper::detail; // NOLINT EXPECT_EQUALISH_JNI_DESCRIPTORS("V", void); EXPECT_NOT_EQUALISH_JNI_DESCRIPTORS("V", jint); EXPECT_EQUALISH_JNI_DESCRIPTORS("Z", jboolean); EXPECT_NOT_EQUALISH_JNI_DESCRIPTORS("Z", void); EXPECT_NOT_EQUALISH_JNI_DESCRIPTORS("Z", jobject); EXPECT_EQUALISH_JNI_DESCRIPTORS("J", jlong); EXPECT_NOT_EQUALISH_JNI_DESCRIPTORS("J", jobject); EXPECT_NOT_EQUALISH_JNI_DESCRIPTORS("J", jthrowable); EXPECT_NOT_EQUALISH_JNI_DESCRIPTORS("J", jint); EXPECT_EQUALISH_JNI_DESCRIPTORS("Ljava/lang/String;", jstring); EXPECT_EQUALISH_JNI_DESCRIPTORS("Ljava/lang/Class;", jclass); EXPECT_EQUALISH_JNI_DESCRIPTORS("Ljava/lang/Object;", jobject); EXPECT_EQUALISH_JNI_DESCRIPTORS("Ljava/lang/Integer;", jobject); EXPECT_NOT_EQUALISH_JNI_DESCRIPTORS("[Z", jthrowable); EXPECT_NOT_EQUALISH_JNI_DESCRIPTORS("[Z", jobjectArray); EXPECT_NOT_EQUALISH_JNI_DESCRIPTORS("Ljava/lang/Integer;", jintArray); EXPECT_NOT_EQUALISH_JNI_DESCRIPTORS("Ljava/lang/Integer;", jarray); EXPECT_NOT_EQUALISH_JNI_DESCRIPTORS("Ljava/lang/Integer;", jarray); // Stricter checks. EXPECT_NOT_EQUALISH_JNI_DESCRIPTORS("Ljava/lang/Object;", jobjectArray); EXPECT_NOT_EQUALISH_JNI_DESCRIPTORS("Ljava/lang/String;", jobject); EXPECT_NOT_EQUALISH_JNI_DESCRIPTORS("Ljava/lang/Class;", jobject); EXPECT_NOT_EQUALISH_JNI_DESCRIPTORS("[Z", jobject); EXPECT_NOT_EQUALISH_JNI_DESCRIPTORS("[Ljava/lang/Object;", jobject); EXPECT_NOT_EQUALISH_JNI_DESCRIPTORS("Ljava/lang/Object;", jarray); // Permissive checks that are weaker than normal. EXPECT_EQUALISH_JNI_DESCRIPTORS("Ljava/lang/Exception;", jobject); EXPECT_EQUALISH_JNI_DESCRIPTORS("Ljava/lang/Error;", jobject); EXPECT_EQUALISH_JNI_DESCRIPTORS("[Z", jarray); EXPECT_EQUALISH_JNI_DESCRIPTORS("[I", jarray); EXPECT_EQUALISH_JNI_DESCRIPTORS("[[Z", jarray); EXPECT_EQUALISH_JNI_DESCRIPTORS("[[Ljava/lang/Object;", jarray); // jthrowable-related checks. EXPECT_NOT_EQUALISH_JNI_DESCRIPTORS("Ljava/lang/Throwable;", jobject); EXPECT_EQUALISH_JNI_DESCRIPTORS("Ljava/lang/Throwable;", jthrowable); EXPECT_EQUALISH_JNI_DESCRIPTORS("Ljava/lang/Exception;", jthrowable); EXPECT_EQUALISH_JNI_DESCRIPTORS("Ljava/lang/Error;", jthrowable); } #define EXPECT_SIMILAR_TYPE_DESCRIPTOR_MATCH(type_desc, type) \ do { \ constexpr auto res = ReifiedJniTypeTrait::MostSimilarTypeDescriptor(type_desc); \ EXPECT_TRUE((ReifiedJniTypeTrait::MostSimilarTypeDescriptor(type_desc)).has_value()); \ if (res.has_value()) EXPECT_EQ(ReifiedJniTypeTrait::Reify(), res.value()); \ } while (false) #define EXPECT_SIMILAR_TYPE_DESCRIPTOR_NO_MATCH(type_desc) \ do { \ auto res = ReifiedJniTypeTrait::MostSimilarTypeDescriptor(type_desc); \ EXPECT_FALSE(res.has_value()); \ } while (false) #define JNI_TYPE_TRAIT_MUST_BE_SAME_FN(type_name, type_desc, ...) \ /* skip jarray because it aliases Ljava/lang/Object; */ \ do { \ constexpr auto str_type_name = ConstexprStringView(#type_name); \ if (str_type_name != "jarray" && str_type_name != "JNIEnv*") { \ EXPECT_SIMILAR_TYPE_DESCRIPTOR_MATCH(type_desc, type_name); \ } \ } while(false); TEST(JniSafeRegisterNativeMethods, MostSimilarTypeDescriptor) { using namespace nativehelper::detail; // NOLINT EXPECT_SIMILAR_TYPE_DESCRIPTOR_MATCH("Z", jboolean); EXPECT_SIMILAR_TYPE_DESCRIPTOR_MATCH("[[I", jobjectArray); EXPECT_SIMILAR_TYPE_DESCRIPTOR_MATCH("[[Z", jobjectArray); EXPECT_SIMILAR_TYPE_DESCRIPTOR_MATCH("[Ljava/lang/String;", jobjectArray); EXPECT_SIMILAR_TYPE_DESCRIPTOR_MATCH("[Ljava/lang/Integer;", jobjectArray); EXPECT_SIMILAR_TYPE_DESCRIPTOR_NO_MATCH("illegal"); EXPECT_SIMILAR_TYPE_DESCRIPTOR_NO_MATCH("?"); EXPECT_SIMILAR_TYPE_DESCRIPTOR_NO_MATCH(""); DEFINE_JNI_TYPE_TRAIT(JNI_TYPE_TRAIT_MUST_BE_SAME_FN); } #define ENFORCE_CONSTEXPR(expr) \ static_assert(__builtin_constant_p(expr), "Expression must be constexpr") #define EXPECT_MATCH_JNI_DESCRIPTOR_AGAINST_FUNCTION_IMPL(cond, native_kind, func, desc) \ do { \ ENFORCE_CONSTEXPR((MatchJniDescriptorWithFunctionType< \ native_kind, \ decltype(func), \ func, \ sizeof(desc)>(desc))); \ EXPECT_ ## cond((MatchJniDescriptorWithFunctionType< \ native_kind, \ decltype(func), \ func, \ sizeof(desc)>(desc))); \ } while(0) #define EXPECT_MATCH_JNI_DESCRIPTOR_AGAINST_FUNCTION(native_kind, func, desc) \ EXPECT_MATCH_JNI_DESCRIPTOR_AGAINST_FUNCTION_IMPL(TRUE, native_kind, func, desc) #define EXPECT_NO_MATCH_JNI_DESCRIPTOR_AGAINST_FUNCTION(native_kind, func, desc) \ EXPECT_MATCH_JNI_DESCRIPTOR_AGAINST_FUNCTION_IMPL(FALSE, native_kind, func, desc) TEST(JniSafeRegisterNativeMethods, MatchJniDescriptorWithFunctionType) { using namespace nativehelper::detail; // NOLINT // Bad C++ signature. EXPECT_NO_MATCH_JNI_DESCRIPTOR_AGAINST_FUNCTION(kCriticalNative, TestJni::bad_cptr, "()V"); EXPECT_NO_MATCH_JNI_DESCRIPTOR_AGAINST_FUNCTION(kNormalNative, TestJni::bad_cptr, "()V"); // JNI type descriptor is not legal (by itself). EXPECT_NO_MATCH_JNI_DESCRIPTOR_AGAINST_FUNCTION(kCriticalNative, TestJni::v_, "BAD"); EXPECT_NO_MATCH_JNI_DESCRIPTOR_AGAINST_FUNCTION(kNormalNative, TestJni::v_eo, "BAD"); // Number of parameters in signature vs C++ function does not match. EXPECT_NO_MATCH_JNI_DESCRIPTOR_AGAINST_FUNCTION(kCriticalNative, TestJni::v_i, "()V"); EXPECT_NO_MATCH_JNI_DESCRIPTOR_AGAINST_FUNCTION(kNormalNative, TestJni::v_eoo, "()V"); // Return types don't match. EXPECT_NO_MATCH_JNI_DESCRIPTOR_AGAINST_FUNCTION(kCriticalNative, TestJni::v_, "()Z"); EXPECT_NO_MATCH_JNI_DESCRIPTOR_AGAINST_FUNCTION(kFastNative, TestJni::v_eo, "()Z"); // Argument types don't match. EXPECT_NO_MATCH_JNI_DESCRIPTOR_AGAINST_FUNCTION(kCriticalNative, TestJni::v_i, "(Z)V"); EXPECT_NO_MATCH_JNI_DESCRIPTOR_AGAINST_FUNCTION(kNormalNative, TestJni::v_eoo, "(Ljava/lang/Class;)V"); // OK. EXPECT_MATCH_JNI_DESCRIPTOR_AGAINST_FUNCTION(kCriticalNative, TestJni::v_i, "(I)V"); EXPECT_MATCH_JNI_DESCRIPTOR_AGAINST_FUNCTION(kNormalNative, TestJni::v_eoo, "(Ljava/lang/Object;)V"); EXPECT_MATCH_JNI_DESCRIPTOR_AGAINST_FUNCTION(kCriticalNative, TestJni::v_lib, "(JIZ)V"); EXPECT_MATCH_JNI_DESCRIPTOR_AGAINST_FUNCTION(kNormalNative, TestJni::v_eolib, "(JIZ)V"); EXPECT_MATCH_JNI_DESCRIPTOR_AGAINST_FUNCTION(kCriticalNative, TestJni::s_lib, "(JIZ)S"); EXPECT_MATCH_JNI_DESCRIPTOR_AGAINST_FUNCTION(kNormalNative, TestJni::s_eolAibA, "([JI[Z)S"); } TEST(JniSafeRegisterNativeMethods, Infer) { using namespace nativehelper::detail; // NOLINT { using Infer_v_eolib_t = InferJniDescriptor; EXPECT_CONSTEXPR_EQ(6u, Infer_v_eolib_t::kMaxStringSize); std::string x = Infer_v_eolib_t::GetStringAtRuntime(); EXPECT_STRINGIFY_EQ("(JIZ)V", x.c_str()); } { using Infer_v_eolib_t = InferJniDescriptor; EXPECT_STRINGIFY_EQ("args={[J,I,[Z}, ret=S", Infer_v_eolib_t::FromFunctionType().value()); EXPECT_CONSTEXPR_EQ(8u, Infer_v_eolib_t::kMaxStringSize); std::string x = Infer_v_eolib_t::GetStringAtRuntime(); EXPECT_STRINGIFY_EQ("([JI[Z)S", x.c_str()); } } // Test the macro definition only. See other tests above for signature-match testing. TEST(JniSafeRegisterNativeMethods, MakeCheckedJniNativeMethod) { // Ensure the temporary variables don't conflict with other local vars of same name. JNINativeMethod tmp_native_method; // shadow test. (void) tmp_native_method; bool is_signature_valid = true; // shadow test. (void) is_signature_valid; // Ensure it works with critical. { JNINativeMethod m = MAKE_CHECKED_JNI_NATIVE_METHOD(kCriticalNative, "v_lib", "(JIZ)V", TestJni::v_lib); (void)m; } // Ensure it works with normal. { JNINativeMethod m = MAKE_CHECKED_JNI_NATIVE_METHOD(kNormalNative, "v_eolib", "(JIZ)V", TestJni::v_eolib); (void)m; } // Make sure macros properly expand inside of an array. { JNINativeMethod m_array[] = { MAKE_CHECKED_JNI_NATIVE_METHOD(kCriticalNative, "v_lib", "(JIZ)V", TestJni::v_lib), MAKE_CHECKED_JNI_NATIVE_METHOD(kNormalNative, "v_eolib", "(JIZ)V", TestJni::v_eolib), }; (void)m_array; } { JNINativeMethod m_array_direct[] { MAKE_CHECKED_JNI_NATIVE_METHOD(kCriticalNative, "v_lib", "(JIZ)V", TestJni::v_lib), MAKE_CHECKED_JNI_NATIVE_METHOD(kNormalNative, "v_eolib", "(JIZ)V", TestJni::v_eolib), }; (void)m_array_direct; } } static auto sTestCheckedAtFileScope = MAKE_CHECKED_JNI_NATIVE_METHOD(kCriticalNative, "v_lib", "(JIZ)V", TestJni::v_lib); static auto sTestInferredAtFileScope = MAKE_INFERRED_JNI_NATIVE_METHOD(kCriticalNative, "v_lib", TestJni::v_lib); TEST(JniSafeRegisterNativeMethods, TestInferredJniNativeMethod) { (void) sTestCheckedAtFileScope; (void) sTestInferredAtFileScope; // Ensure it works with critical. { JNINativeMethod m = MAKE_INFERRED_JNI_NATIVE_METHOD(kCriticalNative, "v_lib", TestJni::v_lib); (void)m; } // Ensure it works with normal. { JNINativeMethod m = MAKE_INFERRED_JNI_NATIVE_METHOD(kNormalNative, "v_eolib", TestJni::v_eolib); (void)m; } } static void TestJniMacros_v_lib(jlong, jint, jboolean) {} static void TestJniMacros_v_lib_od(jlong, jint, jboolean) {} static void TestJniMacros_v_eolib(JNIEnv*, jobject, jlong, jint, jboolean) {} static void TestJniMacros_v_eolib_od(JNIEnv*, jobject, jlong, jint, jboolean) {} #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-parameter" static jint android_os_Parcel_dataSize(jlong) { return 0; } static jint android_os_Parcel_dataAvail(jlong) { return 0; } static jint android_os_Parcel_dataPosition(jlong) { return 0; } static jint android_os_Parcel_dataCapacity(jlong) { return 0; } static jlong android_os_Parcel_setDataSize(JNIEnv*, jclass, jlong, jint) { return 0; } static void android_os_Parcel_setDataPosition(jlong, jint) {} static void android_os_Parcel_setDataCapacity(JNIEnv*, jclass, jlong, jint) {} static jboolean android_os_Parcel_pushAllowFds(jlong, jboolean) { return true; } static void android_os_Parcel_restoreAllowFds(jlong, jboolean) {} static void android_os_Parcel_writeByteArray(JNIEnv*, jclass, jlong, jbyteArray, jint, jint) {} static void android_os_Parcel_writeBlob(JNIEnv*, jclass, jlong, jbyteArray, jint, jint) {} static void android_os_Parcel_writeInt(JNIEnv*, jclass, jlong, jint) {} static void android_os_Parcel_writeLong(JNIEnv* env, jclass clazz, jlong nativePtr, jlong val) {} static void android_os_Parcel_writeFloat(JNIEnv* env, jclass clazz, jlong nativePtr, jfloat val) {} static void android_os_Parcel_writeDouble(JNIEnv* env, jclass clazz, jlong nativePtr, jdouble val) {} static void android_os_Parcel_writeString(JNIEnv* env, jclass clazz, jlong nativePtr, jstring val) {} static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object) {} static jlong android_os_Parcel_writeFileDescriptor(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object) { return 0; } static jbyteArray android_os_Parcel_createByteArray(JNIEnv* env, jclass clazz, jlong nativePtr) { return nullptr; } static jboolean android_os_Parcel_readByteArray(JNIEnv* env, jclass clazz, jlong nativePtr, jbyteArray dest, jint destLen) { return false; } static jbyteArray android_os_Parcel_readBlob(JNIEnv* env, jclass clazz, jlong nativePtr) { return nullptr; } static jint android_os_Parcel_readInt(jlong nativePtr) { return 0; } static jlong android_os_Parcel_readLong(jlong nativePtr) { return 0; } static jfloat android_os_Parcel_readFloat(jlong nativePtr) { return 0.0f; } static jdouble android_os_Parcel_readDouble(jlong nativePtr) { return 0.0; } static jstring android_os_Parcel_readString(JNIEnv* env, jclass clazz, jlong nativePtr) { return nullptr; } static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr) { return nullptr; } static jobject android_os_Parcel_readFileDescriptor(JNIEnv* env, jclass clazz, jlong nativePtr) { return nullptr; } static jobject android_os_Parcel_openFileDescriptor(JNIEnv* env, jclass clazz, jstring name, jint mode) { return 0; } static jobject android_os_Parcel_dupFileDescriptor(JNIEnv* env, jclass clazz, jobject orig) { return 0; } static void android_os_Parcel_closeFileDescriptor(JNIEnv* env, jclass clazz, jobject object) {} static void android_os_Parcel_clearFileDescriptor(JNIEnv* env, jclass clazz, jobject object) {} static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz) { return 0; } static jlong android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativePtr) { return 0; } static void android_os_Parcel_destroy(JNIEnv* env, jclass clazz, jlong nativePtr) {} static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong nativePtr) { return 0; } static jlong android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong nativePtr, jbyteArray data, jint offset, jint length) { return 0; } static jint android_os_Parcel_compareData(JNIEnv* env, jclass clazz, jlong thisNativePtr, jlong otherNativePtr) { return 0; } static jlong android_os_Parcel_appendFrom(JNIEnv* env, jclass clazz, jlong thisNativePtr, jlong otherNativePtr, jint offset, jint length) { return 0; } static jboolean android_os_Parcel_hasFileDescriptors(jlong nativePtr) { return 0; } static void android_os_Parcel_writeInterfaceToken(JNIEnv* env, jclass clazz, jlong nativePtr, jstring name) {} static void android_os_Parcel_enforceInterface(JNIEnv* env, jclass clazz, jlong nativePtr, jstring name) {} static jlong android_os_Parcel_getGlobalAllocSize(JNIEnv* env, jclass clazz) { return 0; } static jlong android_os_Parcel_getGlobalAllocCount(JNIEnv* env, jclass clazz) { return 0; } static jlong android_os_Parcel_getBlobAshmemSize(jlong nativePtr) { return 0; } #pragma clang diagnostic pop TEST(JniSafeRegisterNativeMethods, ParcelExample) { // Test a wide range of automatic signature inferencing. // This is taken from real code in android_os_Parcel.cpp. const JNINativeMethod gParcelMethods[] = { // @CriticalNative MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG( "nativeDataSize", android_os_Parcel_dataSize), // @CriticalNative MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG( "nativeDataAvail", android_os_Parcel_dataAvail), // @CriticalNative MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG( "nativeDataPosition", android_os_Parcel_dataPosition), // @CriticalNative MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG( "nativeDataCapacity", android_os_Parcel_dataCapacity), // @FastNative MAKE_JNI_FAST_NATIVE_METHOD_AUTOSIG( "nativeSetDataSize", android_os_Parcel_setDataSize), // @CriticalNative MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG( "nativeSetDataPosition", android_os_Parcel_setDataPosition), // @FastNative MAKE_JNI_FAST_NATIVE_METHOD_AUTOSIG( "nativeSetDataCapacity", android_os_Parcel_setDataCapacity), // @CriticalNative MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG( "nativePushAllowFds", android_os_Parcel_pushAllowFds), // @CriticalNative MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG( "nativeRestoreAllowFds", android_os_Parcel_restoreAllowFds), MAKE_JNI_NATIVE_METHOD_AUTOSIG( "nativeWriteByteArray", android_os_Parcel_writeByteArray), MAKE_JNI_NATIVE_METHOD_AUTOSIG( "nativeWriteBlob", android_os_Parcel_writeBlob), // @FastNative MAKE_JNI_FAST_NATIVE_METHOD_AUTOSIG( "nativeWriteInt", android_os_Parcel_writeInt), // @FastNative MAKE_JNI_FAST_NATIVE_METHOD_AUTOSIG( "nativeWriteLong", android_os_Parcel_writeLong), // @FastNative MAKE_JNI_FAST_NATIVE_METHOD_AUTOSIG( "nativeWriteFloat", android_os_Parcel_writeFloat), // @FastNative MAKE_JNI_FAST_NATIVE_METHOD_AUTOSIG( "nativeWriteDouble", android_os_Parcel_writeDouble), MAKE_JNI_NATIVE_METHOD_AUTOSIG( "nativeWriteString", android_os_Parcel_writeString), MAKE_JNI_NATIVE_METHOD( "nativeWriteStrongBinder", "(JLandroid/os/IBinder;)V", android_os_Parcel_writeStrongBinder), MAKE_JNI_NATIVE_METHOD( "nativeWriteFileDescriptor", "(JLjava/io/FileDescriptor;)J", android_os_Parcel_writeFileDescriptor), MAKE_JNI_NATIVE_METHOD_AUTOSIG( "nativeCreateByteArray", android_os_Parcel_createByteArray), MAKE_JNI_NATIVE_METHOD_AUTOSIG( "nativeReadByteArray", android_os_Parcel_readByteArray), MAKE_JNI_NATIVE_METHOD_AUTOSIG( "nativeReadBlob", android_os_Parcel_readBlob), // @CriticalNative MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG( "nativeReadInt", android_os_Parcel_readInt), // @CriticalNative MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG( "nativeReadLong", android_os_Parcel_readLong), // @CriticalNative MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG( "nativeReadFloat", android_os_Parcel_readFloat), // @CriticalNative MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG( "nativeReadDouble", android_os_Parcel_readDouble), MAKE_JNI_NATIVE_METHOD_AUTOSIG( "nativeReadString", android_os_Parcel_readString), MAKE_JNI_NATIVE_METHOD( "nativeReadStrongBinder", "(J)Landroid/os/IBinder;", android_os_Parcel_readStrongBinder), MAKE_JNI_NATIVE_METHOD( "nativeReadFileDescriptor", "(J)Ljava/io/FileDescriptor;", android_os_Parcel_readFileDescriptor), MAKE_JNI_NATIVE_METHOD( "openFileDescriptor", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", android_os_Parcel_openFileDescriptor), MAKE_JNI_NATIVE_METHOD( "dupFileDescriptor", "(Ljava/io/FileDescriptor;)Ljava/io/FileDescriptor;", android_os_Parcel_dupFileDescriptor), MAKE_JNI_NATIVE_METHOD( "closeFileDescriptor", "(Ljava/io/FileDescriptor;)V", android_os_Parcel_closeFileDescriptor), MAKE_JNI_NATIVE_METHOD( "clearFileDescriptor", "(Ljava/io/FileDescriptor;)V", android_os_Parcel_clearFileDescriptor), MAKE_JNI_NATIVE_METHOD_AUTOSIG( "nativeCreate", android_os_Parcel_create), MAKE_JNI_NATIVE_METHOD_AUTOSIG( "nativeFreeBuffer", android_os_Parcel_freeBuffer), MAKE_JNI_NATIVE_METHOD_AUTOSIG( "nativeDestroy", android_os_Parcel_destroy), MAKE_JNI_NATIVE_METHOD_AUTOSIG( "nativeMarshall", android_os_Parcel_marshall), MAKE_JNI_NATIVE_METHOD_AUTOSIG( "nativeUnmarshall", android_os_Parcel_unmarshall), MAKE_JNI_NATIVE_METHOD_AUTOSIG( "nativeCompareData", android_os_Parcel_compareData), MAKE_JNI_NATIVE_METHOD_AUTOSIG( "nativeAppendFrom", android_os_Parcel_appendFrom), // @CriticalNative MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG( "nativeHasFileDescriptors", android_os_Parcel_hasFileDescriptors), MAKE_JNI_NATIVE_METHOD_AUTOSIG( "nativeWriteInterfaceToken", android_os_Parcel_writeInterfaceToken), MAKE_JNI_NATIVE_METHOD_AUTOSIG( "nativeEnforceInterface", android_os_Parcel_enforceInterface), MAKE_JNI_NATIVE_METHOD_AUTOSIG( "getGlobalAllocSize", android_os_Parcel_getGlobalAllocSize), MAKE_JNI_NATIVE_METHOD_AUTOSIG( "getGlobalAllocCount", android_os_Parcel_getGlobalAllocCount), // @CriticalNative MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG( "nativeGetBlobAshmemSize", android_os_Parcel_getBlobAshmemSize), }; const JNINativeMethod gParcelMethodsExpected[] = { // @CriticalNative {"nativeDataSize", "(J)I", (void*)android_os_Parcel_dataSize}, // @CriticalNative {"nativeDataAvail", "(J)I", (void*)android_os_Parcel_dataAvail}, // @CriticalNative {"nativeDataPosition", "(J)I", (void*)android_os_Parcel_dataPosition}, // @CriticalNative {"nativeDataCapacity", "(J)I", (void*)android_os_Parcel_dataCapacity}, // @FastNative {"nativeSetDataSize", "(JI)J", (void*)android_os_Parcel_setDataSize}, // @CriticalNative {"nativeSetDataPosition", "(JI)V", (void*)android_os_Parcel_setDataPosition}, // @FastNative {"nativeSetDataCapacity", "(JI)V", (void*)android_os_Parcel_setDataCapacity}, // @CriticalNative {"nativePushAllowFds", "(JZ)Z", (void*)android_os_Parcel_pushAllowFds}, // @CriticalNative {"nativeRestoreAllowFds", "(JZ)V", (void*)android_os_Parcel_restoreAllowFds}, {"nativeWriteByteArray", "(J[BII)V", (void*)android_os_Parcel_writeByteArray}, {"nativeWriteBlob", "(J[BII)V", (void*)android_os_Parcel_writeBlob}, // @FastNative {"nativeWriteInt", "(JI)V", (void*)android_os_Parcel_writeInt}, // @FastNative {"nativeWriteLong", "(JJ)V", (void*)android_os_Parcel_writeLong}, // @FastNative {"nativeWriteFloat", "(JF)V", (void*)android_os_Parcel_writeFloat}, // @FastNative {"nativeWriteDouble", "(JD)V", (void*)android_os_Parcel_writeDouble}, {"nativeWriteString", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeString}, {"nativeWriteStrongBinder", "(JLandroid/os/IBinder;)V", (void*)android_os_Parcel_writeStrongBinder}, {"nativeWriteFileDescriptor", "(JLjava/io/FileDescriptor;)J", (void*)android_os_Parcel_writeFileDescriptor}, {"nativeCreateByteArray", "(J)[B", (void*)android_os_Parcel_createByteArray}, {"nativeReadByteArray", "(J[BI)Z", (void*)android_os_Parcel_readByteArray}, {"nativeReadBlob", "(J)[B", (void*)android_os_Parcel_readBlob}, // @CriticalNative {"nativeReadInt", "(J)I", (void*)android_os_Parcel_readInt}, // @CriticalNative {"nativeReadLong", "(J)J", (void*)android_os_Parcel_readLong}, // @CriticalNative {"nativeReadFloat", "(J)F", (void*)android_os_Parcel_readFloat}, // @CriticalNative {"nativeReadDouble", "(J)D", (void*)android_os_Parcel_readDouble}, {"nativeReadString", "(J)Ljava/lang/String;", (void*)android_os_Parcel_readString}, {"nativeReadStrongBinder", "(J)Landroid/os/IBinder;", (void*)android_os_Parcel_readStrongBinder}, {"nativeReadFileDescriptor", "(J)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_readFileDescriptor}, {"openFileDescriptor", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_openFileDescriptor}, {"dupFileDescriptor", "(Ljava/io/FileDescriptor;)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_dupFileDescriptor}, {"closeFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_closeFileDescriptor}, {"clearFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_clearFileDescriptor}, {"nativeCreate", "()J", (void*)android_os_Parcel_create}, {"nativeFreeBuffer", "(J)J", (void*)android_os_Parcel_freeBuffer}, {"nativeDestroy", "(J)V", (void*)android_os_Parcel_destroy}, {"nativeMarshall", "(J)[B", (void*)android_os_Parcel_marshall}, {"nativeUnmarshall", "(J[BII)J", (void*)android_os_Parcel_unmarshall}, {"nativeCompareData", "(JJ)I", (void*)android_os_Parcel_compareData}, {"nativeAppendFrom", "(JJII)J", (void*)android_os_Parcel_appendFrom}, // @CriticalNative {"nativeHasFileDescriptors", "(J)Z", (void*)android_os_Parcel_hasFileDescriptors}, {"nativeWriteInterfaceToken", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeInterfaceToken}, {"nativeEnforceInterface", "(JLjava/lang/String;)V", (void*)android_os_Parcel_enforceInterface}, {"getGlobalAllocSize", "()J", (void*)android_os_Parcel_getGlobalAllocSize}, {"getGlobalAllocCount", "()J", (void*)android_os_Parcel_getGlobalAllocCount}, // @CriticalNative {"nativeGetBlobAshmemSize", "(J)J", (void*)android_os_Parcel_getBlobAshmemSize}, }; ASSERT_EQ(sizeof(gParcelMethodsExpected)/sizeof(JNINativeMethod), sizeof(gParcelMethods)/sizeof(JNINativeMethod)); for (size_t i = 0; i < sizeof(gParcelMethods) / sizeof(JNINativeMethod); ++i) { const JNINativeMethod& actual = gParcelMethods[i]; const JNINativeMethod& expected = gParcelMethodsExpected[i]; EXPECT_STREQ(expected.name, actual.name); EXPECT_STREQ(expected.signature, actual.signature) << expected.name; EXPECT_EQ(expected.fnPtr, actual.fnPtr) << expected.name; } } TEST(JniSafeRegisterNativeMethods, JniMacros) { JNINativeMethod tmp_native_method; // shadow variable check. (void)tmp_native_method; using Infer_t = int; // shadow using check. Infer_t unused; (void)unused; MAKE_JNI_CRITICAL_NATIVE_METHOD("v_lib", "(JIZ)V", TestJniMacros_v_lib); MAKE_JNI_CRITICAL_NATIVE_METHOD_AUTOSIG("v_lib", TestJniMacros_v_lib); CRITICAL_NATIVE_METHOD(TestJniMacros, v_lib, "(JIZ)V"); OVERLOADED_CRITICAL_NATIVE_METHOD(TestJniMacros, v_lib, "(JIZ)V", v_lib_od); CRITICAL_NATIVE_METHOD_AUTOSIG(TestJniMacros, v_lib); MAKE_JNI_FAST_NATIVE_METHOD("v_eolib", "(JIZ)V", TestJniMacros_v_eolib); MAKE_JNI_FAST_NATIVE_METHOD_AUTOSIG("v_eolib", TestJniMacros_v_eolib); FAST_NATIVE_METHOD(TestJniMacros, v_eolib, "(JIZ)V"); OVERLOADED_FAST_NATIVE_METHOD(TestJniMacros, v_eolib, "(JIZ)V", v_eolib_od); FAST_NATIVE_METHOD_AUTOSIG(TestJniMacros, v_eolib); MAKE_JNI_NATIVE_METHOD("v_eolib", "(JIZ)V", TestJniMacros_v_eolib); MAKE_JNI_NATIVE_METHOD_AUTOSIG("v_eolib", TestJniMacros_v_eolib); NATIVE_METHOD(TestJniMacros, v_eolib, "(JIZ)V"); OVERLOADED_NATIVE_METHOD(TestJniMacros, v_eolib, "(JIZ)V", v_eolib_od); NATIVE_METHOD_AUTOSIG(TestJniMacros, v_eolib); _NATIVEHELPER_JNI_MAKE_METHOD_OLD(kNormalNative, "v_eolib", "(JIZ)V", TestJniMacros_v_eolib); tmp_native_method = _NATIVEHELPER_JNI_MAKE_METHOD_OLD(kNormalNative, "v_eolib", "(JIZ)V", TestJniMacros_v_eolib); } tests/jni_gtest/0040755 0000000 0000000 00000000000 13756723631 012755 5ustar000000000 0000000 tests/jni_gtest/Android.bp0100644 0000000 0000000 00000000442 13756723631 014655 0ustar000000000 0000000 // Do not use directly. Use the defaults below. cc_library_headers { name: "jni_gtest_headers", host_supported: true, export_include_dirs: ["base"], } cc_defaults { name: "jni_gtest_defaults", header_libs: ["jni_gtest_headers"], shared_libs: ["libnativehelper"], } tests/jni_gtest/base/0040755 0000000 0000000 00000000000 13756723631 013667 5ustar000000000 0000000 tests/jni_gtest/base/nativehelper/0040755 0000000 0000000 00000000000 13756723631 016355 5ustar000000000 0000000 tests/jni_gtest/base/nativehelper/jni_gtest.h0100644 0000000 0000000 00000005637 13756723631 020524 0ustar000000000 0000000 /* * Copyright (C) 2018 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. */ #ifndef LIBNATIVEHELPER_TESTS_JNI_GTEST_H #define LIBNATIVEHELPER_TESTS_JNI_GTEST_H #include #include #include #include namespace android { // Example test setup following googletest docs: // // template // class TemplatedTest : public JNITestBase { // ... // } // // typedef ::testing::Types Providers; // TYPED_TEST_CASE(TemplatedTest, Providers); // // TYPED_TEST() { // // Test code. Use "this->" to access TemplatedTest members. // } // Provider is a concept that must follow this structure: // // class JNIProvider { // public: // JNIProvider(); // // void SetUp(); // JNIEnv* CreateJNIEnv(); // // void DestroyJNIEnv(JNIEnv* env); // void TearDown(); // } template class JNITestBase : public Test { protected: JNITestBase() : provider_(), env_(nullptr), java_vm_(nullptr) { } void SetUp() override { Test::SetUp(); provider_.SetUp(); env_ = provider_.CreateJNIEnv(); ASSERT_TRUE(env_ != nullptr); } void TearDown() override { provider_->DestroyJNIEnv(env_); provider_->TearDown(); Test::TearDown(); } protected: Provider provider_; JNIEnv* env_; JavaVM* java_vm_; }; // A mockable implementation of the Provider concept. It is the responsibility // of the test to stub out any needed functions (all function pointers will be // null initially). // // TODO: Consider googlemock. class MockJNIProvider { public: MockJNIProvider() { } void SetUp() { // Nothing to here. } // TODO: Spawn threads to allow more envs? JNIEnv* CreateJNIEnv() { return CreateMockedJNIEnv().release(); } void DestroyJNIEnv(JNIEnv* env) { delete env->functions; delete env; } void TearDown() { // Nothing to do here. } protected: std::unique_ptr CreateMockedJNIEnv() { JNINativeInterface* inf = new JNINativeInterface(); memset(inf, 0, sizeof(JNINativeInterface)); std::unique_ptr ret(new JNIEnv{0}); ret->functions = inf; return ret; } }; } // namespace android #endif // LIBNATIVEHELPER_TESTS_JNI_GTEST_H tests/libnativehelper_api_test.c0100644 0000000 0000000 00000001653 13756723631 016202 0ustar000000000 0000000 /* * Copyright (C) 2019 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. */ // All header files with MODULE_API decorated function declarations. #include "nativehelper/JNIHelp.h" #include "nativehelper/JniInvocation.h" #include "nativehelper/toStringArray.h" int main() { // The test here is that the headers are properly guarded to support // compilation with a C compiler. return 0; } toStringArray.cpp0100644 0000000 0000000 00000003037 13756723631 013141 0ustar000000000 0000000 /* * Copyright (C) 2011 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. */ #include #include "JniConstants.h" namespace { struct ArrayCounter { const char* const* strings; explicit ArrayCounter(const char* const* strings) : strings(strings) {} size_t operator()() { size_t count = 0; while (strings[count] != nullptr) { ++count; } return count; } }; struct ArrayGetter { const char* const* strings; explicit ArrayGetter(const char* const* strings) : strings(strings) {} const char* operator()(size_t i) { return strings[i]; } }; } // namespace MODULE_API jobjectArray newStringArray(JNIEnv* env, size_t count) { return env->NewObjectArray(count, JniConstants::GetStringClass(env), nullptr); } MODULE_API jobjectArray toStringArray(JNIEnv* env, const char* const* strings) { ArrayCounter counter(strings); ArrayGetter getter(strings); return toStringArray(env, &counter, &getter); }