diff --git a/CMakeLists.txt b/CMakeLists.txt index df206fe..cd73c5d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,24 @@ project(jnipp) find_package(JNI REQUIRED) include(CTest) +option( + JNIPP_USE_EXCEPTION_HANDLING + "Enable exception handling in JNIPP." + ON +) + add_library(jnipp jnipp.cpp) + +if(NOT JNIPP_USE_EXCEPTION_HANDLING) + target_compile_definitions(jnipp PUBLIC JNIPP_USE_EXCEPTION=0) + + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(jnipp PRIVATE -fno-exceptions) + endif() +else() + target_compile_definitions(jnipp PUBLIC JNIPP_USE_EXCEPTION=1) +endif() + target_include_directories( jnipp PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/README.md b/README.md index 178708b..225871e 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,10 @@ extern "C" void Java_com_example_Demo_run(jni::JNIEnv* env, jni::jobject obj) ## Configuration -By default, *jnipp* uses std::runtime_error as the base exception class. If you wish, +By default, *jnipp* uses `std::runtime_error` as the base exception class. If you wish, you can define `JNIPP_EXCEPTION_CLASS` to be the exception class you wish to use, before including `jnipp.h`. It just needs a `const char*` constructor. + +### Disabling exceptions + +*jnipp* supports a no-exception mode, in which all `throw` expressions will be replaced with calls to `std::terminate()`. It can be enabled by compiling the library with `JNIPP_USE_EXCEPTION` defined to value `0`. This is also controlled by the CMake option called `JNIPP_USE_EXCEPTION_HANDLING`, which is `ON` by default (i.e. exceptions will by default be *enabled*). diff --git a/jnipp.cpp b/jnipp.cpp index 45a8e9d..e8d0042 100644 --- a/jnipp.cpp +++ b/jnipp.cpp @@ -20,6 +20,13 @@ // Local Dependencies #include "jnipp.h" +#if JNIPP_USE_EXCEPTION +#include +#define JNIPP_THROW(...) do { throw __VA_ARGS__; } while(0) +#else +#define JNIPP_THROW(...) do { std::terminate(); } while(0) +#endif + namespace jni { // Static Variables @@ -65,7 +72,7 @@ namespace jni return; if (vm == nullptr) - throw InitializationException("JNI not initialized"); + JNIPP_THROW(InitializationException("JNI not initialized")); if (!getEnv(vm, &_env)) { @@ -74,7 +81,7 @@ namespace jni #else if (vm->AttachCurrentThread((void**)&_env, nullptr) != 0) #endif - throw InitializationException("Could not attach JNI to thread"); + JNIPP_THROW(InitializationException("Could not attach JNI to thread")); _attached = true; } @@ -182,7 +189,7 @@ namespace jni if (ref == nullptr) { env()->ExceptionClear(); - throw NameResolutionException(name); + JNIPP_THROW(NameResolutionException(name)); } return ref; @@ -200,7 +207,7 @@ namespace jni env->ExceptionClear(); std::string msg = obj.call("toString"); - throw InvocationException(msg.c_str()); + JNIPP_THROW(InvocationException(msg.c_str())); } } @@ -258,7 +265,7 @@ namespace jni if (isVm.compare_exchange_strong(expected, true)) { if (javaVm == nullptr && env->GetJavaVM(&javaVm) != 0) - throw InitializationException("Could not acquire Java VM"); + JNIPP_THROW(InitializationException("Could not acquire Java VM")); } } @@ -668,7 +675,7 @@ namespace jni jfieldID id = env()->GetFieldID(getHandle(), name, signature); if (id == nullptr) - throw NameResolutionException(name); + JNIPP_THROW(NameResolutionException(name)); return id; } @@ -678,7 +685,7 @@ namespace jni jfieldID id = env()->GetStaticFieldID(getHandle(), name, signature); if (id == nullptr) - throw NameResolutionException(name); + JNIPP_THROW(NameResolutionException(name)); return id; } @@ -688,7 +695,7 @@ namespace jni jmethodID id = env()->GetMethodID(getHandle(), name, signature); if (id == nullptr) - throw NameResolutionException(name); + JNIPP_THROW(NameResolutionException(name)); return id; } @@ -703,7 +710,7 @@ namespace jni return getMethod(std::string(nameAndSignature, sig - nameAndSignature).c_str(), sig); if (id == nullptr) - throw NameResolutionException(nameAndSignature); + JNIPP_THROW(NameResolutionException(nameAndSignature)); return id; } @@ -713,7 +720,7 @@ namespace jni jmethodID id = env()->GetStaticMethodID(getHandle(), name, signature); if (id == nullptr) - throw NameResolutionException(name); + JNIPP_THROW(NameResolutionException(name)); return id; } @@ -727,7 +734,7 @@ namespace jni return getStaticMethod(std::string(nameAndSignature, sig - nameAndSignature).c_str(), sig); if (id == nullptr) - throw NameResolutionException(nameAndSignature); + JNIPP_THROW(NameResolutionException(nameAndSignature)); return id; } @@ -1445,9 +1452,9 @@ namespace jni std::string path = path_ ? path_ : detectJvmPath(); if (path.length() == 0) - throw InitializationException("Could not locate Java Virtual Machine"); + JNIPP_THROW(InitializationException("Could not locate Java Virtual Machine")); if (!isVm.compare_exchange_strong(expected, true)) - throw InitializationException("Java Virtual Machine already initialized"); + JNIPP_THROW(InitializationException("Java Virtual Machine already initialized")); if (javaVm == nullptr) { @@ -1485,7 +1492,7 @@ namespace jni if (lib == NULL) { isVm.store(false); - throw InitializationException("Could not load JVM library"); + JNIPP_THROW(InitializationException("Could not load JVM library")); } CreateVm_t JNI_CreateJavaVM = (CreateVm_t) ::dlsym(lib, "JNI_CreateJavaVM"); @@ -1494,7 +1501,7 @@ namespace jni { isVm.store(false); ::dlclose(lib); - throw InitializationException("Java Virtual Machine failed during creation"); + JNIPP_THROW(InitializationException("Java Virtual Machine failed during creation")); } #endif // _WIN32 diff --git a/jnipp.h b/jnipp.h index 017bdd9..0bef032 100644 --- a/jnipp.h +++ b/jnipp.h @@ -3,9 +3,19 @@ // Standard Dependencies #include -#include // For std::runtime_error #include +// JNIPP by default uses exceptions to communicate errors to the consumers. +// The preprocessor directive should be set by the build script, but in case +// it wasn't, define it here to its expected default value. +#ifndef JNIPP_USE_EXCEPTION +#define JNIPP_USE_EXCEPTION 1 +#endif + +#if JNIPP_USE_EXCEPTION +#include // For std::runtime_error +#endif + // Forward Declarations struct JNIEnv_; struct _JNIEnv; @@ -55,6 +65,7 @@ namespace jni */ typedef unsigned char byte_t; +#if JNIPP_USE_EXCEPTION #ifdef JNIPP_EXCEPTION_CLASS /** @@ -70,6 +81,7 @@ namespace jni typedef std::runtime_error Exception; #endif // JNIPP_EXCEPTION_CLASS +#endif // JNIPP_USE_EXCEPTION // Foward Declarations class Object; @@ -986,6 +998,7 @@ namespace jni ~Vm(); }; +#if JNIPP_USE_EXCEPTION /** A Java method call threw an Exception. */ @@ -1024,6 +1037,7 @@ namespace jni */ InitializationException(const char* msg) : Exception(msg) {} }; +#endif /* Array Implementation diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b17c12d..c89fff3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -6,6 +6,12 @@ add_executable(main_test main.cpp testing.h) target_link_libraries(main_test PRIVATE jnipp) add_test(NAME main_test COMMAND main_test) +if(NOT JNIPP_USE_EXCEPTION_HANDLING) + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(main_test PRIVATE -fno-exceptions) + endif() +endif() + add_executable(external_create external_create.cpp testing.h) target_link_libraries(external_create PUBLIC jnipp ${JNI_LIBRARIES}) target_include_directories(external_create PUBLIC ${JNI_INCLUDE_DIRS}) diff --git a/tests/main.cpp b/tests/main.cpp index 7c1adfc..8139c35 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -11,7 +11,7 @@ jni::Vm Tests */ - +#if JNIPP_USE_EXCEPTION == 1 TEST(Vm_detectsJreInstall) { try @@ -42,6 +42,7 @@ TEST(Vm_notAllowedMultipleVms) ASSERT(0); } +#endif /* jni::Class Tests @@ -55,6 +56,7 @@ TEST(Class_findByName_success) ASSERT(!cls.isNull()); } +#if JNIPP_USE_EXCEPTION TEST(Class_findByName_failure) { try @@ -69,6 +71,7 @@ TEST(Class_findByName_failure) ASSERT(0); } +#endif TEST(Class_getName) { @@ -430,6 +433,7 @@ TEST(Array_getElement_defaultValue) ASSERT(s.getElement(0).length() == 0); } +#if JNIPP_USE_EXCEPTION TEST(Array_getElement_indexException) { jni::Array a(10); @@ -444,6 +448,7 @@ TEST(Array_getElement_indexException) ASSERT(1); } } +#endif TEST(Array_setElement_basicType) { @@ -467,6 +472,7 @@ TEST(Array_setElement_string) ASSERT(a.getElement(i) == std::to_wstring(i)); } +#if JNIPP_USE_EXCEPTION TEST(Array_setElement_indexException) { jni::Array s(10); @@ -481,6 +487,7 @@ TEST(Array_setElement_indexException) ASSERT(1); } } +#endif /* Argument Type Tests @@ -560,15 +567,20 @@ TEST(Arg_ObjectPtr) int main() { // jni::Vm Tests + +#if JNIPP_USE_EXCEPTION RUN_TEST(Vm_detectsJreInstall); RUN_TEST(Vm_notAllowedMultipleVms); +#endif { jni::Vm vm; // jni::Class Tests RUN_TEST(Class_findByName_success); +#if JNIPP_USE_EXCEPTION RUN_TEST(Class_findByName_failure); +#endif RUN_TEST(Class_getName); RUN_TEST(Class_getParent); RUN_TEST(Class_newInstance); @@ -606,10 +618,14 @@ int main() RUN_TEST(Array_copyConstructor); RUN_TEST(Array_moveConstructor); RUN_TEST(Array_getElement_defaultValue); +#if JNIPP_USE_EXCEPTION RUN_TEST(Array_getElement_indexException); +#endif RUN_TEST(Array_setElement_basicType); RUN_TEST(Array_setElement_string); +#if JNIPP_USE_EXCEPTION RUN_TEST(Array_setElement_indexException); +#endif // Argument Type Tests RUN_TEST(Arg_bool);