diff --git a/Makefile b/Makefile index 4a09217..db16ecd 100644 --- a/Makefile +++ b/Makefile @@ -2,10 +2,15 @@ VARIANT ?= android BUILD := build/$(VARIANT) COMPILER := $(shell CXX="${CXX}" misc/compiler.sh) +COMPILER_MAJOR_VERSION := $(shell CXX="${CXX}" misc/compiler-major-version.sh) + ifeq ($(COMPILER), clang) -CXXFLAGS_WARNINGS := -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-exit-time-destructors + CXXFLAGS_WARNINGS := -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-exit-time-destructors + ifeq ($(shell test $(COMPILER_MAJOR_VERSION) -gt 4; echo $$?),0) + CXXFLAGS_WARNINGS += -Wno-unused-template + endif else ifeq ($(COMPILER), gcc) -CXXFLAGS_WARNINGS := -Wall -Wextra -pedantic -Wno-unused-but-set-variable + CXXFLAGS_WARNINGS := -Wall -Wextra -pedantic -Wno-unused-but-set-variable endif CXXFLAGS := $(CXXFLAGS) --std=c++14 -fPIC -Iinclude $(CXXFLAGS_WARNINGS) -Werror diff --git a/include/jni/errors.hpp b/include/jni/errors.hpp index 7e0ce52..3d91f0d 100644 --- a/include/jni/errors.hpp +++ b/include/jni/errors.hpp @@ -60,13 +60,19 @@ namespace jni template < class R > R CheckJavaException(JNIEnv& env, R&& r) { - if (env.ExceptionCheck()) throw PendingJavaException(); + if (env.ExceptionCheck()) { + env.ExceptionDescribe(); + throw PendingJavaException(); + } return std::move(r); } inline void CheckJavaException(JNIEnv& env) { - if (env.ExceptionCheck()) throw PendingJavaException(); + if (env.ExceptionCheck()) { + env.ExceptionDescribe(); + throw PendingJavaException(); + } } inline void CheckJavaExceptionThenErrorCode(JNIEnv& env, jint err) diff --git a/include/jni/functions.hpp b/include/jni/functions.hpp index 60e8d67..b559d20 100644 --- a/include/jni/functions.hpp +++ b/include/jni/functions.hpp @@ -628,23 +628,28 @@ namespace jni } + namespace { + // Some implementations type the parameter as JNIEnv**, others as void**. + // See https://bugs.openjdk.java.net/browse/JDK-6569899 + struct JNIEnvCast + { + using FunVoid = jint (JavaVM::*)(void**, void*); + using FunEnv = jint (JavaVM::*)(JNIEnv**, void*); + + template ::value>> + void** operator()(JNIEnv** env, Fun) noexcept { + return reinterpret_cast(env); + } + + template ::value>> + JNIEnv** operator()(JNIEnv** env, Fun) noexcept { + return env; + } + }; + } + inline UniqueEnv AttachCurrentThread(JavaVM& vm) { - // Some implementations type the parameter as JNIEnv**, others as void**. - // See https://bugs.openjdk.java.net/browse/JDK-6569899 - struct JNIEnvCast - { - void** operator()(JNIEnv** env, jint (JavaVM::*)(void**, void*)) - { - return reinterpret_cast(env); - } - - JNIEnv** operator()(JNIEnv** env, jint (JavaVM::*)(JNIEnv**, void*)) - { - return env; - } - }; - JNIEnv* result; CheckErrorCode(vm.AttachCurrentThread(JNIEnvCast()(&result, &JavaVM::AttachCurrentThread), nullptr)); return UniqueEnv(result, JNIEnvDeleter(vm)); @@ -662,4 +667,18 @@ namespace jni CheckErrorCode(vm.GetEnv(reinterpret_cast(&env), Unwrap(version))); return *env; } + + inline UniqueEnv GetAttachedEnv(JavaVM& vm, version version = jni_version_1_1) + { + JNIEnv* env = nullptr; + auto code = vm.GetEnv(reinterpret_cast(&env), Unwrap(version)); + switch (code) + { + case JNI_OK: return UniqueEnv(env,JNIEnvDeleter(vm, false)); + case JNI_EDETACHED: return AttachCurrentThread(vm); + default: + CheckErrorCode(code); + return nullptr; + } + } } diff --git a/include/jni/native_method.hpp b/include/jni/native_method.hpp index 978b733..192e301 100644 --- a/include/jni/native_method.hpp +++ b/include/jni/native_method.hpp @@ -10,8 +10,6 @@ #include #include -#include - namespace jni { template < class M, class Enable = void > @@ -236,7 +234,10 @@ namespace jni { auto wrapper = [field] (JNIEnv& env, Object& obj, Args... args) { - return method(env, *reinterpret_cast(obj.Get(env, field)), args...); + auto ptr = reinterpret_cast(obj.Get(env, field)); + if (ptr) return method(env, *ptr, args...); + ThrowNew(env, jni::FindClass(env, "java/lang/IllegalStateException"), + "invalid native peer"); }; return MakeNativeMethod(name, wrapper); @@ -251,14 +252,13 @@ namespace jni return NativePeerFunctionPointerMethod(name); } - /// High-level peer, member function pointer - template < class M, M > + template < class M, M> class NativePeerMemberFunctionMethod; - template < class R, class P, class... Args, R (P::*method)(JNIEnv&, Args...) > - class NativePeerMemberFunctionMethod< R (P::*)(JNIEnv&, Args...), method > + template < class R, class P, class... Args, R (P::*method)(JNIEnv&, Args...)> + class NativePeerMemberFunctionMethod< R (P::*)(JNIEnv&, Args...), method> { private: const char* name; @@ -273,14 +273,16 @@ namespace jni { auto wrapper = [field] (JNIEnv& env, Object& obj, Args... args) { - return (reinterpret_cast(obj.Get(env, field))->*method)(env, args...); + auto ptr = reinterpret_cast(obj.Get(env, field)); + if (ptr) return (ptr->*method)(env, args...); + ThrowNew(env, jni::FindClass(env, "java/lang/IllegalStateException"), + "invalid native peer"); }; - return MakeNativeMethod(name, wrapper); } }; - template < class M, M method > + template < class M, M method> auto MakeNativePeerMethod(const char* name, std::enable_if_t< std::is_member_function_pointer::value >* = nullptr) { diff --git a/include/jni/object.hpp b/include/jni/object.hpp index cbc93bc..575a879 100644 --- a/include/jni/object.hpp +++ b/include/jni/object.hpp @@ -48,14 +48,14 @@ namespace jni using SuperType = typename TagTraits::SuperType; using UntaggedType = typename TagTraits::UntaggedType; - protected: - explicit Object(std::nullptr_t = nullptr) - {} - explicit Object(UntaggedType* p) : SuperType(p) {} + protected: + explicit Object(std::nullptr_t = nullptr) + {} + Object(const Object&) = delete; Object& operator=(const Object&) = delete; diff --git a/include/jni/ownership.hpp b/include/jni/ownership.hpp index 56241df..33a8f24 100644 --- a/include/jni/ownership.hpp +++ b/include/jni/ownership.hpp @@ -205,14 +205,15 @@ namespace jni { private: JavaVM* vm = nullptr; - + bool detach = true; + public: JNIEnvDeleter() = default; - JNIEnvDeleter(JavaVM& v) : vm(&v) {} + JNIEnvDeleter(JavaVM& v, bool d = true) : vm(&v), detach{d} {} void operator()(JNIEnv* p) const { - if (p) + if (p && detach) { assert(vm); vm->DetachCurrentThread(); diff --git a/include/jni/string.hpp b/include/jni/string.hpp index 3f805ef..5be4423 100644 --- a/include/jni/string.hpp +++ b/include/jni/string.hpp @@ -4,9 +4,7 @@ #include #include #include - -#include -#include +#include namespace jni { @@ -22,8 +20,7 @@ namespace jni inline std::string MakeAnything(ThingToMake, JNIEnv& env, const String& string) { - return std::wstring_convert, char16_t>() - .to_bytes(Make(env, string)); + return convertUTF16ToUTF8(Make(env, string)); } inline Local MakeAnything(ThingToMake, JNIEnv& env, const std::u16string& string) @@ -33,7 +30,6 @@ namespace jni inline Local MakeAnything(ThingToMake, JNIEnv& env, const std::string& string) { - return Make(env, std::wstring_convert, char16_t>() - .from_bytes(string)); + return Make(env, convertUTF8ToUTF16(string)); } } diff --git a/include/jni/string_conversion.hpp b/include/jni/string_conversion.hpp new file mode 100644 index 0000000..78974ef --- /dev/null +++ b/include/jni/string_conversion.hpp @@ -0,0 +1,21 @@ +#pragma once + +// If you want to supply your own UTF-8 <-> UTF-16 conversion routines, create a header file +// that can be found at and will be found first in the lookup chain. + +#include +#include +#include + +namespace jni + { + inline std::u16string convertUTF8ToUTF16(const std::string& string) + { + return std::wstring_convert, char16_t>().from_bytes(string); + } + + inline std::string convertUTF16ToUTF8(const std::u16string& string) + { + return std::wstring_convert, char16_t>().to_bytes(string); + } + } diff --git a/misc/compiler-major-version.sh b/misc/compiler-major-version.sh new file mode 100755 index 0000000..42e4cc4 --- /dev/null +++ b/misc/compiler-major-version.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -euo pipefail + +compiler=$(${CXX} -v 2>&1) + +case $compiler in + *clang*|*gcc*) + version=`${CXX} --version | grep -o '\([0-9]\)\+.\([0-9]\)\+.\([0-9]\)\+'` + echo ${version::1} + ;; + *) + echo + unknown + ;; +esac diff --git a/test/android/jni.h b/test/android/jni.h index cd23d87..658e069 100644 --- a/test/android/jni.h +++ b/test/android/jni.h @@ -532,7 +532,7 @@ struct _JNIEnv { { return functions->ExceptionOccurred(this); } void ExceptionDescribe() - { functions->ExceptionDescribe(this); } + { if (functions->ExceptionDescribe) functions->ExceptionDescribe(this); } void ExceptionClear() { functions->ExceptionClear(this); } diff --git a/test/openjdk/jni.h b/test/openjdk/jni.h index 2e83cb7..794d253 100644 --- a/test/openjdk/jni.h +++ b/test/openjdk/jni.h @@ -825,7 +825,7 @@ struct JNIEnv_ { return functions->ExceptionOccurred(this); } void ExceptionDescribe() { - functions->ExceptionDescribe(this); + if (functions->ExceptionDescribe) functions->ExceptionDescribe(this); } void ExceptionClear() { functions->ExceptionClear(this);