refactor(android): move djinni jni files to thrid_party
This commit is contained in:
		
							parent
							
								
									d6ff3470f1
								
							
						
					
					
						commit
						5eb3174edb
					
				| @ -8,11 +8,17 @@ cmake_minimum_required(VERSION 3.4.1) | ||||
| get_filename_component(MYNTETE_ROOT "${PROJECT_SOURCE_DIR}/../../../.." ABSOLUTE) | ||||
| message(STATUS "MYNTETE_ROOT: ${MYNTETE_ROOT}") | ||||
| 
 | ||||
| get_filename_component(PRO_ROOT "${PROJECT_SOURCE_DIR}/.." ABSOLUTE) | ||||
| message(STATUS "PRO_ROOT: ${PRO_ROOT}") | ||||
| 
 | ||||
| set(LIB_ROOT "${PROJECT_SOURCE_DIR}") | ||||
| message(STATUS "LIB_ROOT: ${LIB_ROOT}") | ||||
| 
 | ||||
| if(NOT DJINNI_DIR) | ||||
|   if(DEFINED ENV{DJINNI_DIR}) | ||||
|     set(DJINNI_DIR $ENV{DJINNI_DIR}) | ||||
|   else() | ||||
|     set(DJINNI_DIR "$ENV{HOME}/Workspace/Fever/Dropbox/djinni") | ||||
|     set(DJINNI_DIR "${PRO_ROOT}/third_party/djinni") | ||||
|   endif() | ||||
| endif() | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										33
									
								
								wrappers/android/mynteye/third_party/djinni/support-lib/djinni_common.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								wrappers/android/mynteye/third_party/djinni/support-lib/djinni_common.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | ||||
| //
 | ||||
| // Copyright 2015 Dropbox, Inc.
 | ||||
| //
 | ||||
| // 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 _MSC_VER | ||||
|     #define DJINNI_WEAK_DEFINITION // weak attribute not supported by MSVC
 | ||||
|     #define DJINNI_NORETURN_DEFINITION __declspec(noreturn) | ||||
|     #if _MSC_VER < 1900 // snprintf not implemented prior to VS2015
 | ||||
|         #define DJINNI_SNPRINTF snprintf | ||||
|         #define noexcept _NOEXCEPT // work-around for missing noexcept VS2015
 | ||||
|         #define constexpr // work-around for missing constexpr VS2015
 | ||||
|     #else | ||||
|         #define DJINNI_SNPRINTF _snprintf | ||||
|     #endif | ||||
| #else | ||||
|     #define DJINNI_WEAK_DEFINITION __attribute__((weak)) | ||||
|     #define DJINNI_NORETURN_DEFINITION __attribute__((noreturn)) | ||||
|     #define DJINNI_SNPRINTF snprintf | ||||
| #endif | ||||
							
								
								
									
										536
									
								
								wrappers/android/mynteye/third_party/djinni/support-lib/jni/Marshal.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										536
									
								
								wrappers/android/mynteye/third_party/djinni/support-lib/jni/Marshal.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,536 @@ | ||||
| //
 | ||||
| // Copyright 2014 Dropbox, Inc.
 | ||||
| //
 | ||||
| // 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 "djinni_support.hpp" | ||||
| #include <cassert> | ||||
| #include <chrono> | ||||
| #include <cstdint> | ||||
| #include <string> | ||||
| #include <unordered_map> | ||||
| #include <unordered_set> | ||||
| #include <vector> | ||||
| 
 | ||||
| namespace djinni | ||||
| { | ||||
|     template<class Self, class CppT, class JniT> | ||||
|     class Primitive | ||||
|     { | ||||
|     public: | ||||
|         using CppType = CppT; | ||||
|         using JniType = JniT; | ||||
| 
 | ||||
|         static CppType toCpp(JNIEnv* /*jniEnv*/, JniType j) noexcept { return static_cast<CppType>(j); } | ||||
|         static JniType fromCpp(JNIEnv* /*jniEnv*/, CppType c) noexcept { return static_cast<JniType>(c); } | ||||
| 
 | ||||
|         struct Boxed | ||||
|         { | ||||
|             using JniType = jobject; | ||||
|             static CppType toCpp(JNIEnv* jniEnv, JniType j) | ||||
|             { | ||||
|                 assert(j != nullptr); | ||||
|                 const auto& data = JniClass<Self>::get(); | ||||
|                 assert(jniEnv->IsInstanceOf(j, data.clazz.get())); | ||||
|                 auto ret = Primitive::toCpp(jniEnv, Self::unbox(jniEnv, data.method_unbox, j)); | ||||
|                 jniExceptionCheck(jniEnv); | ||||
|                 return ret; | ||||
|             } | ||||
|             static LocalRef<JniType> fromCpp(JNIEnv* jniEnv, CppType c) | ||||
|             { | ||||
|                 const auto& data = JniClass<Self>::get(); | ||||
|                 auto ret = jniEnv->CallStaticObjectMethod(data.clazz.get(), data.method_box, Primitive::fromCpp(jniEnv, c)); | ||||
|                 jniExceptionCheck(jniEnv); | ||||
|                 return {jniEnv, ret}; | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|     protected: | ||||
|         Primitive(const char* javaClassSpec, | ||||
|                   const char* staticBoxMethod, | ||||
|                   const char* staticBoxMethodSignature, | ||||
|                   const char* unboxMethod, | ||||
|                   const char* unboxMethodSignature) | ||||
|         : clazz(jniFindClass(javaClassSpec)) | ||||
|         , method_box(jniGetStaticMethodID(clazz.get(), staticBoxMethod, staticBoxMethodSignature)) | ||||
|         , method_unbox(jniGetMethodID(clazz.get(), unboxMethod, unboxMethodSignature)) | ||||
|         {} | ||||
| 
 | ||||
|     private: | ||||
|         const GlobalRef<jclass> clazz; | ||||
|         const jmethodID method_box; | ||||
|         const jmethodID method_unbox; | ||||
|     }; | ||||
| 
 | ||||
|     class Bool : public Primitive<Bool, bool, jboolean> | ||||
|     { | ||||
|         Bool() : Primitive("java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", "booleanValue", "()Z") {} | ||||
|         friend JniClass<Bool>; | ||||
|         friend Primitive<Bool, bool, jboolean>; | ||||
|         static JniType unbox(JNIEnv* jniEnv, jmethodID method, jobject j) { | ||||
|             auto result = jniEnv->CallBooleanMethod(j, method); | ||||
|             jniExceptionCheck(jniEnv); | ||||
|             return result; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     class I8 : public Primitive<I8, int8_t, jbyte> | ||||
|     { | ||||
|         I8() : Primitive("java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", "byteValue", "()B") {} | ||||
|         friend JniClass<I8>; | ||||
|         friend Primitive<I8, int8_t, jbyte>; | ||||
|         static JniType unbox(JNIEnv* jniEnv, jmethodID method, jobject j) { | ||||
|             auto result = jniEnv->CallByteMethod(j, method); | ||||
|             jniExceptionCheck(jniEnv); | ||||
|             return result; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     class I16 : public Primitive<I16, int16_t, jshort> | ||||
|     { | ||||
|         I16() : Primitive("java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", "shortValue", "()S") {} | ||||
|         friend JniClass<I16>; | ||||
|         friend Primitive<I16, int16_t, jshort>; | ||||
|         static JniType unbox(JNIEnv* jniEnv, jmethodID method, jobject j) { | ||||
|             auto result = jniEnv->CallShortMethod(j, method); | ||||
|             jniExceptionCheck(jniEnv); | ||||
|             return result; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     class I32 : public Primitive<I32, int32_t, jint> | ||||
|     { | ||||
|         I32() : Primitive("java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", "intValue", "()I") {} | ||||
|         friend JniClass<I32>; | ||||
|         friend Primitive<I32, int32_t, jint>; | ||||
|         static JniType unbox(JNIEnv* jniEnv, jmethodID method, jobject j) { | ||||
|             auto result = jniEnv->CallIntMethod(j, method); | ||||
|             jniExceptionCheck(jniEnv); | ||||
|             return result; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     class I64 : public Primitive<I64, int64_t, jlong> | ||||
|     { | ||||
|         I64() : Primitive("java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", "longValue", "()J") {} | ||||
|         friend JniClass<I64>; | ||||
|         friend Primitive<I64, int64_t, jlong>; | ||||
|         static JniType unbox(JNIEnv* jniEnv, jmethodID method, jobject j) { | ||||
|             auto result = jniEnv->CallLongMethod(j, method); | ||||
|             jniExceptionCheck(jniEnv); | ||||
|             return result; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     class F32 : public Primitive<F32, float, jfloat> | ||||
|     { | ||||
|         F32() : Primitive("java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", "floatValue", "()F") {} | ||||
|         friend JniClass<F32>; | ||||
|         friend Primitive<F32, float, jfloat>; | ||||
|         static JniType unbox(JNIEnv* jniEnv, jmethodID method, jobject j) { | ||||
|             auto result = jniEnv->CallFloatMethod(j, method); | ||||
|             jniExceptionCheck(jniEnv); | ||||
|             return result; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     class F64 : public Primitive<F64, double, jdouble> | ||||
|     { | ||||
|         F64() : Primitive("java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", "doubleValue", "()D") {} | ||||
|         friend JniClass<F64>; | ||||
|         friend Primitive<F64, double, jdouble>; | ||||
|         static JniType unbox(JNIEnv* jniEnv, jmethodID method, jobject j) { | ||||
|             auto result = jniEnv->CallDoubleMethod(j, method); | ||||
|             jniExceptionCheck(jniEnv); | ||||
|             return result; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     struct String | ||||
|     { | ||||
|         using CppType = std::string; | ||||
|         using JniType = jstring; | ||||
| 
 | ||||
|         using Boxed = String; | ||||
| 
 | ||||
|         static CppType toCpp(JNIEnv* jniEnv, JniType j) | ||||
|         { | ||||
|             assert(j != nullptr); | ||||
|             return jniUTF8FromString(jniEnv, j); | ||||
|         } | ||||
| 
 | ||||
|         static LocalRef<JniType> fromCpp(JNIEnv* jniEnv, const CppType& c) | ||||
|         { | ||||
|             return {jniEnv, jniStringFromUTF8(jniEnv, c)}; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     struct WString | ||||
|     { | ||||
|         using CppType = std::wstring; | ||||
|         using JniType = jstring; | ||||
| 
 | ||||
|         using Boxed = WString; | ||||
| 
 | ||||
|         static CppType toCpp(JNIEnv* jniEnv, JniType j) | ||||
|         { | ||||
|             assert(j != nullptr); | ||||
|             return jniWStringFromString(jniEnv, j); | ||||
|         } | ||||
| 
 | ||||
|         static LocalRef<JniType> fromCpp(JNIEnv* jniEnv, const CppType& c) | ||||
|         { | ||||
|             return {jniEnv, jniStringFromWString(jniEnv, c)}; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     struct Binary | ||||
|     { | ||||
|         using CppType = std::vector<uint8_t>; | ||||
|         using JniType = jbyteArray; | ||||
| 
 | ||||
|         using Boxed = Binary; | ||||
| 
 | ||||
|         static CppType toCpp(JNIEnv* jniEnv, JniType j) | ||||
|         { | ||||
|             assert(j != nullptr); | ||||
| 
 | ||||
|             std::vector<uint8_t> ret; | ||||
|             jsize length = jniEnv->GetArrayLength(j); | ||||
|             jniExceptionCheck(jniEnv); | ||||
| 
 | ||||
|             if (!length) { | ||||
|                 return ret; | ||||
|             } | ||||
| 
 | ||||
|             { | ||||
|                 auto deleter = [jniEnv, j] (void* c) { | ||||
|                     if (c) { | ||||
|                         jniEnv->ReleasePrimitiveArrayCritical(j, c, JNI_ABORT); | ||||
|                     } | ||||
|                 }; | ||||
| 
 | ||||
|                 std::unique_ptr<uint8_t, decltype(deleter)> ptr( | ||||
|                     reinterpret_cast<uint8_t*>(jniEnv->GetPrimitiveArrayCritical(j, nullptr)), | ||||
|                     deleter | ||||
|                 ); | ||||
| 
 | ||||
|                 if (ptr) { | ||||
|                     // Construct and then move-assign. This copies the elements only once,
 | ||||
|                     // and avoids having to initialize before filling (as with resize())
 | ||||
|                     ret = std::vector<uint8_t>{ptr.get(), ptr.get() + length}; | ||||
|                 } else { | ||||
|                     // Something failed...
 | ||||
|                     jniExceptionCheck(jniEnv); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return ret; | ||||
|         } | ||||
| 
 | ||||
|         static LocalRef<JniType> fromCpp(JNIEnv* jniEnv, const CppType& c) | ||||
|         { | ||||
|             assert(c.size() <= std::numeric_limits<jsize>::max()); | ||||
|             auto j = LocalRef<jbyteArray>(jniEnv, jniEnv->NewByteArray(static_cast<jsize>(c.size()))); | ||||
|             jniExceptionCheck(jniEnv); | ||||
|             // Using .data() on an empty vector is UB
 | ||||
|             if(!c.empty()) | ||||
|             { | ||||
|                 jniEnv->SetByteArrayRegion(j.get(), 0, jsize(c.size()), reinterpret_cast<const jbyte*>(c.data())); | ||||
|             } | ||||
|             return j; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     struct Date | ||||
|     { | ||||
|         using CppType = std::chrono::system_clock::time_point; | ||||
|         using JniType = jobject; | ||||
| 
 | ||||
|         using Boxed = Date; | ||||
| 
 | ||||
|         static CppType toCpp(JNIEnv* jniEnv, JniType j) | ||||
|         { | ||||
|             static const auto POSIX_EPOCH = std::chrono::system_clock::from_time_t(0); | ||||
|             assert(j != nullptr); | ||||
|             const auto & data = JniClass<Date>::get(); | ||||
|             assert(jniEnv->IsInstanceOf(j, data.clazz.get())); | ||||
|             auto time_millis = jniEnv->CallLongMethod(j, data.method_get_time); | ||||
|             jniExceptionCheck(jniEnv); | ||||
|             return POSIX_EPOCH + std::chrono::milliseconds{time_millis}; | ||||
|         } | ||||
| 
 | ||||
|         static LocalRef<JniType> fromCpp(JNIEnv* jniEnv, const CppType& c) | ||||
|         { | ||||
|             static const auto POSIX_EPOCH = std::chrono::system_clock::from_time_t(0); | ||||
|             const auto & data = JniClass<Date>::get(); | ||||
|             const auto cpp_millis = std::chrono::duration_cast<std::chrono::milliseconds>(c - POSIX_EPOCH); | ||||
|             const jlong millis = static_cast<jlong>(cpp_millis.count()); | ||||
|             auto j = LocalRef<jobject>(jniEnv, jniEnv->NewObject(data.clazz.get(), data.constructor, millis)); | ||||
|             jniExceptionCheck(jniEnv); | ||||
|             return j; | ||||
|         } | ||||
| 
 | ||||
|     private: | ||||
|         Date() = default; | ||||
|         friend ::djinni::JniClass<Date>; | ||||
| 
 | ||||
|         const GlobalRef<jclass> clazz { jniFindClass("java/util/Date") }; | ||||
|         const jmethodID constructor { jniGetMethodID(clazz.get(), "<init>", "(J)V") }; | ||||
|         const jmethodID method_get_time { jniGetMethodID(clazz.get(), "getTime", "()J") }; | ||||
|     }; | ||||
| 
 | ||||
|     template <template <class> class OptionalType, class T> | ||||
|     struct Optional | ||||
|     { | ||||
|         // SFINAE helper: if C::CppOptType exists, opt_type<T>(nullptr) will return
 | ||||
|         // that type. If not, it returns OptionalType<C::CppType>. This is necessary
 | ||||
|         // because we special-case optional interfaces to be represented as a nullable
 | ||||
|         // std::shared_ptr<T>, not optional<shared_ptr<T>> or optional<nn<shared_ptr<T>>>.
 | ||||
|         template <typename C> static OptionalType<typename C::CppType> opt_type(...); | ||||
|         template <typename C> static typename C::CppOptType opt_type(typename C::CppOptType *); | ||||
|         using CppType = decltype(opt_type<T>(nullptr)); | ||||
| 
 | ||||
|         using JniType = typename T::Boxed::JniType; | ||||
| 
 | ||||
|         using Boxed = Optional; | ||||
| 
 | ||||
|         static CppType toCpp(JNIEnv* jniEnv, JniType j) | ||||
|         { | ||||
|             if (j) { | ||||
|                 return T::Boxed::toCpp(jniEnv, j); | ||||
|             } else { | ||||
|                 return CppType(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         static LocalRef<JniType> fromCpp(JNIEnv* jniEnv, const OptionalType<typename T::CppType> &c) | ||||
|         { | ||||
|             return c ? T::Boxed::fromCpp(jniEnv, *c) : LocalRef<JniType>{}; | ||||
|         } | ||||
| 
 | ||||
|         // fromCpp used for nullable shared_ptr
 | ||||
|         template <typename C = T> | ||||
|         static LocalRef<JniType> fromCpp(JNIEnv* jniEnv, const typename C::CppOptType & cppOpt) { | ||||
|             return T::Boxed::fromCppOpt(jniEnv, cppOpt); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     struct ListJniInfo | ||||
|     { | ||||
|         const GlobalRef<jclass> clazz { jniFindClass("java/util/ArrayList") }; | ||||
|         const jmethodID constructor { jniGetMethodID(clazz.get(), "<init>", "(I)V") }; | ||||
|         const jmethodID method_add { jniGetMethodID(clazz.get(), "add", "(Ljava/lang/Object;)Z") }; | ||||
|         const jmethodID method_get { jniGetMethodID(clazz.get(), "get", "(I)Ljava/lang/Object;") }; | ||||
|         const jmethodID method_size { jniGetMethodID(clazz.get(), "size", "()I") }; | ||||
|     }; | ||||
| 
 | ||||
|     template <class T> | ||||
|     class List | ||||
|     { | ||||
|         using ECppType = typename T::CppType; | ||||
|         using EJniType = typename T::Boxed::JniType; | ||||
| 
 | ||||
|     public: | ||||
|         using CppType = std::vector<ECppType>; | ||||
|         using JniType = jobject; | ||||
| 
 | ||||
|         using Boxed = List; | ||||
| 
 | ||||
|         static CppType toCpp(JNIEnv* jniEnv, JniType j) | ||||
|         { | ||||
|             assert(j != nullptr); | ||||
|             const auto& data = JniClass<ListJniInfo>::get(); | ||||
|             assert(jniEnv->IsInstanceOf(j, data.clazz.get())); | ||||
|             auto size = jniEnv->CallIntMethod(j, data.method_size); | ||||
|             jniExceptionCheck(jniEnv); | ||||
|             auto c = CppType(); | ||||
|             c.reserve(size); | ||||
|             for(jint i = 0; i < size; ++i) | ||||
|             { | ||||
|                 auto je = LocalRef<jobject>(jniEnv, jniEnv->CallObjectMethod(j, data.method_get, i)); | ||||
|                 jniExceptionCheck(jniEnv); | ||||
|                 c.push_back(T::Boxed::toCpp(jniEnv, static_cast<EJniType>(je.get()))); | ||||
|             } | ||||
|             return c; | ||||
|         } | ||||
| 
 | ||||
|         static LocalRef<JniType> fromCpp(JNIEnv* jniEnv, const CppType& c) | ||||
|         { | ||||
|             const auto& data = JniClass<ListJniInfo>::get(); | ||||
|             assert(c.size() <= std::numeric_limits<jint>::max()); | ||||
|             auto size = static_cast<jint>(c.size()); | ||||
|             auto j = LocalRef<jobject>(jniEnv, jniEnv->NewObject(data.clazz.get(), data.constructor, size)); | ||||
|             jniExceptionCheck(jniEnv); | ||||
|             for(const auto& ce : c) | ||||
|             { | ||||
|                 auto je = T::Boxed::fromCpp(jniEnv, ce); | ||||
|                 jniEnv->CallBooleanMethod(j, data.method_add, get(je)); | ||||
|                 jniExceptionCheck(jniEnv); | ||||
|             } | ||||
|             return j; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     struct IteratorJniInfo | ||||
|     { | ||||
|         const GlobalRef<jclass> clazz { jniFindClass("java/util/Iterator") }; | ||||
|         const jmethodID method_next { jniGetMethodID(clazz.get(), "next", "()Ljava/lang/Object;") }; | ||||
|     }; | ||||
| 
 | ||||
|     struct SetJniInfo | ||||
|     { | ||||
|         const GlobalRef<jclass> clazz { jniFindClass("java/util/HashSet") }; | ||||
|         const jmethodID constructor { jniGetMethodID(clazz.get(), "<init>", "()V") }; | ||||
|         const jmethodID method_add { jniGetMethodID(clazz.get(), "add", "(Ljava/lang/Object;)Z") }; | ||||
|         const jmethodID method_size { jniGetMethodID(clazz.get(), "size", "()I") }; | ||||
|         const jmethodID method_iterator { jniGetMethodID(clazz.get(), "iterator", "()Ljava/util/Iterator;") }; | ||||
|     }; | ||||
| 
 | ||||
|     template <class T> | ||||
|     class Set | ||||
|     { | ||||
|         using ECppType = typename T::CppType; | ||||
|         using EJniType = typename T::Boxed::JniType; | ||||
| 
 | ||||
|     public: | ||||
|         using CppType = std::unordered_set<ECppType>; | ||||
|         using JniType = jobject; | ||||
| 
 | ||||
|         using Boxed = Set; | ||||
| 
 | ||||
|         static CppType toCpp(JNIEnv* jniEnv, JniType j) | ||||
|         { | ||||
|             assert(j != nullptr); | ||||
|             const auto& data = JniClass<SetJniInfo>::get(); | ||||
|             const auto& iteData = JniClass<IteratorJniInfo>::get(); | ||||
|             assert(jniEnv->IsInstanceOf(j, data.clazz.get())); | ||||
|             auto size = jniEnv->CallIntMethod(j, data.method_size); | ||||
|             jniExceptionCheck(jniEnv); | ||||
|             auto c = CppType(); | ||||
|             auto it = LocalRef<jobject>(jniEnv, jniEnv->CallObjectMethod(j, data.method_iterator)); | ||||
|             jniExceptionCheck(jniEnv); | ||||
|             for(jint i = 0; i < size; ++i) | ||||
|             { | ||||
|                 auto je = LocalRef<jobject>(jniEnv, jniEnv->CallObjectMethod(it, iteData.method_next)); | ||||
|                 jniExceptionCheck(jniEnv); | ||||
|                 c.insert(T::Boxed::toCpp(jniEnv, static_cast<EJniType>(je.get()))); | ||||
|             } | ||||
|             return c; | ||||
|         } | ||||
| 
 | ||||
|         static LocalRef<JniType> fromCpp(JNIEnv* jniEnv, const CppType& c) | ||||
|         { | ||||
|             const auto& data = JniClass<SetJniInfo>::get(); | ||||
|             assert(c.size() <= std::numeric_limits<jint>::max()); | ||||
|             auto size = static_cast<jint>(c.size()); | ||||
|             auto j = LocalRef<jobject>(jniEnv, jniEnv->NewObject(data.clazz.get(), data.constructor, size)); | ||||
|             jniExceptionCheck(jniEnv); | ||||
|             for(const auto& ce : c) | ||||
|             { | ||||
|                 auto je = T::Boxed::fromCpp(jniEnv, ce); | ||||
|                 jniEnv->CallBooleanMethod(j, data.method_add, get(je)); | ||||
|                 jniExceptionCheck(jniEnv); | ||||
|             } | ||||
|             return j; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     struct MapJniInfo | ||||
|     { | ||||
|         const GlobalRef<jclass> clazz { jniFindClass("java/util/HashMap") }; | ||||
|         const jmethodID constructor { jniGetMethodID(clazz.get(), "<init>", "()V") }; | ||||
|         const jmethodID method_put { jniGetMethodID(clazz.get(), "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;") }; | ||||
|         const jmethodID method_size { jniGetMethodID(clazz.get(), "size", "()I") }; | ||||
|         const jmethodID method_entrySet { jniGetMethodID(clazz.get(), "entrySet", "()Ljava/util/Set;") }; | ||||
|     }; | ||||
| 
 | ||||
|     struct EntrySetJniInfo | ||||
|     { | ||||
|         const GlobalRef<jclass> clazz { jniFindClass("java/util/Set") }; | ||||
|         const jmethodID method_iterator { jniGetMethodID(clazz.get(), "iterator", "()Ljava/util/Iterator;") }; | ||||
|     }; | ||||
| 
 | ||||
|     struct EntryJniInfo | ||||
|     { | ||||
|         const GlobalRef<jclass> clazz { jniFindClass("java/util/Map$Entry") }; | ||||
|         const jmethodID method_getKey { jniGetMethodID(clazz.get(), "getKey", "()Ljava/lang/Object;") }; | ||||
|         const jmethodID method_getValue { jniGetMethodID(clazz.get(), "getValue", "()Ljava/lang/Object;") }; | ||||
|     }; | ||||
| 
 | ||||
|     template <class Key, class Value> | ||||
|     class Map | ||||
|     { | ||||
|         using CppKeyType = typename Key::CppType; | ||||
|         using CppValueType = typename Value::CppType; | ||||
|         using JniKeyType = typename Key::Boxed::JniType; | ||||
|         using JniValueType = typename Value::Boxed::JniType; | ||||
| 
 | ||||
|     public: | ||||
|         using CppType = std::unordered_map<CppKeyType, CppValueType>; | ||||
|         using JniType = jobject; | ||||
| 
 | ||||
|         using Boxed = Map; | ||||
| 
 | ||||
|         static CppType toCpp(JNIEnv* jniEnv, JniType j) | ||||
|         { | ||||
|             assert(j != nullptr); | ||||
|             const auto& data = JniClass<MapJniInfo>::get(); | ||||
|             const auto& entrySetData = JniClass<EntrySetJniInfo>::get(); | ||||
|             const auto& entryData = JniClass<EntryJniInfo>::get(); | ||||
|             const auto& iteData = JniClass<IteratorJniInfo>::get(); | ||||
|             assert(jniEnv->IsInstanceOf(j, data.clazz.get())); | ||||
|             auto size = jniEnv->CallIntMethod(j, data.method_size); | ||||
|             jniExceptionCheck(jniEnv); | ||||
|             auto entrySet = LocalRef<jobject>(jniEnv, jniEnv->CallObjectMethod(j, data.method_entrySet)); | ||||
|             jniExceptionCheck(jniEnv); | ||||
|             auto c = CppType(); | ||||
|             c.reserve(size); | ||||
|             auto it = LocalRef<jobject>(jniEnv, jniEnv->CallObjectMethod(entrySet, entrySetData.method_iterator)); | ||||
|             jniExceptionCheck(jniEnv); | ||||
|             for(jint i = 0; i < size; ++i) | ||||
|             { | ||||
|                 auto je = LocalRef<jobject>(jniEnv, jniEnv->CallObjectMethod(it, iteData.method_next)); | ||||
|                 jniExceptionCheck(jniEnv); | ||||
|                 auto jKey = LocalRef<jobject>(jniEnv, jniEnv->CallObjectMethod(je, entryData.method_getKey)); | ||||
|                 jniExceptionCheck(jniEnv); | ||||
|                 auto jValue = LocalRef<jobject>(jniEnv, jniEnv->CallObjectMethod(je, entryData.method_getValue)); | ||||
|                 jniExceptionCheck(jniEnv); | ||||
|                 c.emplace(Key::Boxed::toCpp(jniEnv, static_cast<JniKeyType>(jKey.get())), | ||||
|                           Value::Boxed::toCpp(jniEnv, static_cast<JniValueType>(jValue.get()))); | ||||
|             } | ||||
|             return c; | ||||
|         } | ||||
| 
 | ||||
|         static LocalRef<JniType> fromCpp(JNIEnv* jniEnv, const CppType& c) | ||||
|         { | ||||
|             const auto& data = JniClass<MapJniInfo>::get(); | ||||
|             assert(c.size() <= std::numeric_limits<jint>::max()); | ||||
|             auto size = c.size(); | ||||
|             auto j = LocalRef<jobject>(jniEnv, jniEnv->NewObject(data.clazz.get(), data.constructor, size)); | ||||
|             jniExceptionCheck(jniEnv); | ||||
|             for(const auto& ce : c) | ||||
|             { | ||||
|                 auto jKey = Key::Boxed::fromCpp(jniEnv, ce.first); | ||||
|                 auto jValue = Value::Boxed::fromCpp(jniEnv, ce.second); | ||||
|                 jniEnv->CallObjectMethod(j, data.method_put, get(jKey), get(jValue)); | ||||
|                 jniExceptionCheck(jniEnv); | ||||
|             } | ||||
|             return j; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
| } // namespace djinni
 | ||||
							
								
								
									
										31
									
								
								wrappers/android/mynteye/third_party/djinni/support-lib/jni/djinni_main.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								wrappers/android/mynteye/third_party/djinni/support-lib/jni/djinni_main.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| //
 | ||||
| // Copyright 2014 Dropbox, Inc.
 | ||||
| //
 | ||||
| // 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.
 | ||||
| //
 | ||||
| 
 | ||||
| // This provides a minimal JNI_OnLoad and JNI_OnUnload implementation - include it if your
 | ||||
| // app doesn't use JNI except through Djinni.
 | ||||
| 
 | ||||
| #include "djinni_support.hpp" | ||||
| 
 | ||||
| // Called when library is loaded by the first class which uses it.
 | ||||
| CJNIEXPORT jint JNICALL JNI_OnLoad(JavaVM * jvm, void * /*reserved*/) { | ||||
|     djinni::jniInit(jvm); | ||||
|     return JNI_VERSION_1_6; | ||||
| } | ||||
| 
 | ||||
| // (Potentially) called when library is about to be unloaded.
 | ||||
| CJNIEXPORT void JNICALL JNI_OnUnload(JavaVM * /*jvm*/, void * /*reserved*/) { | ||||
|     djinni::jniShutdown(); | ||||
| } | ||||
							
								
								
									
										686
									
								
								wrappers/android/mynteye/third_party/djinni/support-lib/jni/djinni_support.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										686
									
								
								wrappers/android/mynteye/third_party/djinni/support-lib/jni/djinni_support.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,686 @@ | ||||
| //
 | ||||
| // Copyright 2014 Dropbox, Inc.
 | ||||
| //
 | ||||
| // 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 "../djinni_common.hpp" | ||||
| #include "djinni_support.hpp" | ||||
| #include "../proxy_cache_impl.hpp" | ||||
| #include <cassert> | ||||
| #include <cstdlib> | ||||
| #include <cstring> | ||||
| 
 | ||||
| static_assert(sizeof(jlong) >= sizeof(void*), "must be able to fit a void* into a jlong"); | ||||
| 
 | ||||
| namespace djinni { | ||||
| 
 | ||||
| // Set only once from JNI_OnLoad before any other JNI calls, so no lock needed.
 | ||||
| static JavaVM * g_cachedJVM; | ||||
| 
 | ||||
| /*static*/ | ||||
| JniClassInitializer::registration_vec & JniClassInitializer::get_vec() { | ||||
|     static JniClassInitializer::registration_vec m; | ||||
|     return m; | ||||
| } | ||||
| 
 | ||||
| /*static*/ | ||||
| std::mutex & JniClassInitializer::get_mutex() { | ||||
|     static std::mutex mtx; | ||||
|     return mtx; | ||||
| } | ||||
| 
 | ||||
| /*static*/ | ||||
| JniClassInitializer::registration_vec JniClassInitializer::get_all() { | ||||
|     const std::lock_guard<std::mutex> lock(get_mutex()); | ||||
|     return get_vec(); | ||||
| } | ||||
| 
 | ||||
| JniClassInitializer::JniClassInitializer(std::function<void()> init) { | ||||
|     const std::lock_guard<std::mutex> lock(get_mutex()); | ||||
|     get_vec().push_back(std::move(init)); | ||||
| } | ||||
| 
 | ||||
| void jniInit(JavaVM * jvm) { | ||||
|     g_cachedJVM = jvm; | ||||
| 
 | ||||
|     try { | ||||
|         for (const auto & initializer : JniClassInitializer::get_all()) { | ||||
|             initializer(); | ||||
|         } | ||||
|     } catch (const std::exception &) { | ||||
|         // Default exception handling only, since non-default might not be safe if init
 | ||||
|         // is incomplete.
 | ||||
|         jniDefaultSetPendingFromCurrent(jniGetThreadEnv(), __func__); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void jniShutdown() { | ||||
|     g_cachedJVM = nullptr; | ||||
| } | ||||
| 
 | ||||
| JNIEnv * jniGetThreadEnv() { | ||||
|     assert(g_cachedJVM); | ||||
|     JNIEnv * env = nullptr; | ||||
|     jint get_res = g_cachedJVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6); | ||||
|     #ifdef EXPERIMENTAL_AUTO_CPP_THREAD_ATTACH | ||||
|     if (get_res == JNI_EDETACHED) { | ||||
|         get_res = g_cachedJVM->AttachCurrentThread(&env, nullptr); | ||||
|         thread_local struct DetachOnExit { | ||||
|             ~DetachOnExit() { | ||||
|                 g_cachedJVM->DetachCurrentThread(); | ||||
|             } | ||||
|         } detachOnExit; | ||||
|     } | ||||
|     #endif | ||||
|     if (get_res != 0 || !env) { | ||||
|         // :(
 | ||||
|         std::abort(); | ||||
|     } | ||||
| 
 | ||||
|     return env; | ||||
| } | ||||
| 
 | ||||
| static JNIEnv * getOptThreadEnv() { | ||||
|     if (!g_cachedJVM) { | ||||
|         return nullptr; | ||||
|     } | ||||
| 
 | ||||
|     // Special case: this allows us to ignore GlobalRef deletions that happen after this
 | ||||
|     // thread has been detached. (This is known to happen during process shutdown, when
 | ||||
|     // there's no need to release the ref anyway.)
 | ||||
|     JNIEnv * env = nullptr; | ||||
|     const jint get_res = g_cachedJVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6); | ||||
| 
 | ||||
|     if (get_res == JNI_EDETACHED) { | ||||
|         return nullptr; | ||||
|     } | ||||
| 
 | ||||
|     // Still bail on any other error.
 | ||||
|     if (get_res != 0 || !env) { | ||||
|         // :(
 | ||||
|         std::abort(); | ||||
|     } | ||||
| 
 | ||||
|     return env; | ||||
| } | ||||
| 
 | ||||
| void GlobalRefDeleter::operator() (jobject globalRef) noexcept { | ||||
|     if (globalRef) { | ||||
|         if (JNIEnv * env = getOptThreadEnv()) { | ||||
|             env->DeleteGlobalRef(globalRef); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void LocalRefDeleter::operator() (jobject localRef) noexcept { | ||||
|     if (localRef) { | ||||
|         jniGetThreadEnv()->DeleteLocalRef(localRef); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void jni_exception::set_as_pending(JNIEnv * env) const noexcept { | ||||
|     assert(env); | ||||
|     env->Throw(java_exception()); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void jniExceptionCheck(JNIEnv * env) { | ||||
|     if (!env) { | ||||
|         abort(); | ||||
|     } | ||||
|     const LocalRef<jthrowable> e(env->ExceptionOccurred()); | ||||
|     if (e) { | ||||
|         env->ExceptionClear(); | ||||
|         jniThrowCppFromJavaException(env, e.get()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| DJINNI_WEAK_DEFINITION | ||||
| DJINNI_NORETURN_DEFINITION | ||||
| void jniThrowCppFromJavaException(JNIEnv * env, jthrowable java_exception) { | ||||
|     throw jni_exception { env, java_exception }; | ||||
| } | ||||
| 
 | ||||
| namespace { // anonymous namespace to guard the struct below
 | ||||
| struct SystemClassInfo { | ||||
|     // This is a singleton class - an instance will be constructed by
 | ||||
|     // JniClassInitializer::init_all() at library init time.
 | ||||
|     const GlobalRef<jclass> clazz { jniFindClass("java/lang/System") }; | ||||
|     const jmethodID staticmethIdentityHashCode { jniGetStaticMethodID(clazz.get(), | ||||
|             "identityHashCode", "(Ljava/lang/Object;)I") }; | ||||
| }; | ||||
| } // namespace
 | ||||
| 
 | ||||
| /*
 | ||||
|  * Hasher and comparator based on Java object identity. | ||||
|  */ | ||||
| struct JavaIdentityHash { size_t operator() (jobject obj) const; }; | ||||
| struct JavaIdentityEquals { bool operator() (jobject obj1, jobject obj2) const; }; | ||||
| 
 | ||||
| size_t JavaIdentityHash::operator() (jobject obj) const { | ||||
|     JNIEnv * const env = jniGetThreadEnv(); | ||||
|     const SystemClassInfo & sys = JniClass<SystemClassInfo>::get(); | ||||
|     jint res = env->CallStaticIntMethod(sys.clazz.get(), sys.staticmethIdentityHashCode, obj); | ||||
|     jniExceptionCheck(env); | ||||
|     return res; | ||||
| } | ||||
| bool JavaIdentityEquals::operator() (jobject obj1, jobject obj2) const { | ||||
|     JNIEnv * const env = jniGetThreadEnv(); | ||||
|     const bool res = env->IsSameObject(obj1, obj2); | ||||
|     jniExceptionCheck(env); | ||||
|     return res; | ||||
| } | ||||
| 
 | ||||
| void jniThrowAssertionError(JNIEnv * env, const char * file, int line, const char * check) { | ||||
|     // basename() exists, but is bad (it's allowed to modify its input).
 | ||||
|     const char * slash = strrchr(file, '/'); | ||||
|     const char * file_basename = slash ? slash + 1 : file; | ||||
| 
 | ||||
|     char buf[256]; | ||||
|     DJINNI_SNPRINTF(buf, sizeof buf, "djinni (%s:%d): %s", file_basename, line, check); | ||||
| 
 | ||||
|     const jclass cassert = env->FindClass("java/lang/Error"); | ||||
|     assert(cassert); | ||||
|     env->ThrowNew(cassert, buf); | ||||
|     assert(env->ExceptionCheck()); | ||||
|     const jthrowable e = env->ExceptionOccurred(); | ||||
|     assert(e); | ||||
|     env->ExceptionClear(); | ||||
| 
 | ||||
|     env->DeleteLocalRef(cassert); | ||||
| 
 | ||||
|     jniThrowCppFromJavaException(env, e); | ||||
| } | ||||
| 
 | ||||
| GlobalRef<jclass> jniFindClass(const char * name) { | ||||
|     JNIEnv * env = jniGetThreadEnv(); | ||||
|     DJINNI_ASSERT(name, env); | ||||
|     GlobalRef<jclass> guard(env, LocalRef<jclass>(env, env->FindClass(name)).get()); | ||||
|     jniExceptionCheck(env); | ||||
|     if (!guard) { | ||||
|         jniThrowAssertionError(env, __FILE__, __LINE__, "FindClass returned null"); | ||||
|     } | ||||
|     return guard; | ||||
| } | ||||
| 
 | ||||
| jmethodID jniGetStaticMethodID(jclass clazz, const char * name, const char * sig) { | ||||
|     JNIEnv * env = jniGetThreadEnv(); | ||||
|     DJINNI_ASSERT(clazz, env); | ||||
|     DJINNI_ASSERT(name, env); | ||||
|     DJINNI_ASSERT(sig, env); | ||||
|     jmethodID id = env->GetStaticMethodID(clazz, name, sig); | ||||
|     jniExceptionCheck(env); | ||||
|     if (!id) { | ||||
|         jniThrowAssertionError(env, __FILE__, __LINE__, "GetStaticMethodID returned null"); | ||||
|     } | ||||
|     return id; | ||||
| } | ||||
| 
 | ||||
| jmethodID jniGetMethodID(jclass clazz, const char * name, const char * sig) { | ||||
|     JNIEnv * env = jniGetThreadEnv(); | ||||
|     DJINNI_ASSERT(clazz, env); | ||||
|     DJINNI_ASSERT(name, env); | ||||
|     DJINNI_ASSERT(sig, env); | ||||
|     jmethodID id = env->GetMethodID(clazz, name, sig); | ||||
|     jniExceptionCheck(env); | ||||
|     if (!id) { | ||||
|         jniThrowAssertionError(env, __FILE__, __LINE__, "GetMethodID returned null"); | ||||
|     } | ||||
|     return id; | ||||
| } | ||||
| 
 | ||||
| jfieldID jniGetFieldID(jclass clazz, const char * name, const char * sig) { | ||||
|     JNIEnv * env = jniGetThreadEnv(); | ||||
|     DJINNI_ASSERT(clazz, env); | ||||
|     DJINNI_ASSERT(name, env); | ||||
|     DJINNI_ASSERT(sig, env); | ||||
|     jfieldID id = env->GetFieldID(clazz, name, sig); | ||||
|     jniExceptionCheck(env); | ||||
|     if (!id) { | ||||
|         jniThrowAssertionError(env, __FILE__, __LINE__, "GetFieldID returned null"); | ||||
|     } | ||||
|     return id; | ||||
| } | ||||
| 
 | ||||
| JniEnum::JniEnum(const std::string & name) | ||||
|     : m_clazz { jniFindClass(name.c_str()) }, | ||||
|       m_staticmethValues { jniGetStaticMethodID(m_clazz.get(), "values", ("()[L" + name + ";").c_str()) }, | ||||
|       m_methOrdinal { jniGetMethodID(m_clazz.get(), "ordinal", "()I") } | ||||
|     {} | ||||
| 
 | ||||
| jint JniEnum::ordinal(JNIEnv * env, jobject obj) const { | ||||
|     DJINNI_ASSERT(obj, env); | ||||
|     const jint res = env->CallIntMethod(obj, m_methOrdinal); | ||||
|     jniExceptionCheck(env); | ||||
|     return res; | ||||
| } | ||||
| 
 | ||||
| LocalRef<jobject> JniEnum::create(JNIEnv * env, jint value) const { | ||||
|     LocalRef<jobject> values(env, env->CallStaticObjectMethod(m_clazz.get(), m_staticmethValues)); | ||||
|     jniExceptionCheck(env); | ||||
|     DJINNI_ASSERT(values, env); | ||||
|     LocalRef<jobject> result(env, | ||||
|                              env->GetObjectArrayElement(static_cast<jobjectArray>(values.get()), | ||||
|                                                         value)); | ||||
|     jniExceptionCheck(env); | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| JniFlags::JniFlags(const std::string & name) | ||||
|     : JniEnum { name } | ||||
|     {} | ||||
| 
 | ||||
| unsigned JniFlags::flags(JNIEnv * env, jobject obj) const { | ||||
|     DJINNI_ASSERT(obj && env->IsInstanceOf(obj, m_clazz.get()), env); | ||||
|     auto size = env->CallIntMethod(obj, m_methSize); | ||||
|     jniExceptionCheck(env); | ||||
|     unsigned flags = 0; | ||||
|     auto it = LocalRef<jobject>(env, env->CallObjectMethod(obj, m_methIterator)); | ||||
|     jniExceptionCheck(env); | ||||
|     for(jint i = 0; i < size; ++i) { | ||||
|         auto jf = LocalRef<jobject>(env, env->CallObjectMethod(it, m_iterator.methNext)); | ||||
|         jniExceptionCheck(env); | ||||
|         flags |= (1u << static_cast<unsigned>(ordinal(env, jf))); | ||||
|     } | ||||
|     return flags; | ||||
| } | ||||
| 
 | ||||
| LocalRef<jobject> JniFlags::create(JNIEnv * env, unsigned flags, int bits) const { | ||||
|     auto j = LocalRef<jobject>(env, env->CallStaticObjectMethod(m_clazz.get(), m_methNoneOf, enumClass())); | ||||
|     jniExceptionCheck(env); | ||||
|     unsigned mask = 1; | ||||
|     for(int i = 0; i < bits; ++i, mask <<= 1) { | ||||
|         if((flags & mask) != 0) { | ||||
|             auto jf = create(env, static_cast<jint>(i)); | ||||
|             jniExceptionCheck(env); | ||||
|             env->CallBooleanMethod(j, m_methAdd, jf.get()); | ||||
|             jniExceptionCheck(env); | ||||
|         } | ||||
|     } | ||||
|     return j; | ||||
| } | ||||
| 
 | ||||
| JniLocalScope::JniLocalScope(JNIEnv* p_env, jint capacity, bool throwOnError) | ||||
|     : m_env(p_env) | ||||
|     , m_success(_pushLocalFrame(m_env, capacity)) { | ||||
|     if (throwOnError) { | ||||
|         DJINNI_ASSERT(m_success, m_env); | ||||
|     } | ||||
| } | ||||
| JniLocalScope::~JniLocalScope() { | ||||
|     if (m_success) { | ||||
|         _popLocalFrame(m_env, nullptr); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool JniLocalScope::_pushLocalFrame(JNIEnv* const env, jint capacity) { | ||||
|     DJINNI_ASSERT(capacity >= 0, env); | ||||
|     const jint push_res = env->PushLocalFrame(capacity); | ||||
|     return 0 == push_res; | ||||
| } | ||||
| 
 | ||||
| void JniLocalScope::_popLocalFrame(JNIEnv* const env, jobject returnRef) { | ||||
|     env->PopLocalFrame(returnRef); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * UTF-8 and UTF-16 conversion functions from miniutf: https://github.com/dropbox/miniutf
 | ||||
|  */ | ||||
| 
 | ||||
| struct offset_pt { | ||||
|     int offset; | ||||
|     char32_t pt; | ||||
| }; | ||||
| 
 | ||||
| static constexpr const offset_pt invalid_pt = { -1, 0 }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Decode a codepoint starting at str[i], and return the number of code units (bytes, for | ||||
|  * UTF-8) consumed and the result. If no valid codepoint is at str[i], return invalid_pt. | ||||
|  */ | ||||
| static offset_pt utf8_decode_check(const std::string & str, std::string::size_type i) { | ||||
|     uint32_t b0, b1, b2, b3; | ||||
| 
 | ||||
|     b0 = static_cast<unsigned char>(str[i]); | ||||
| 
 | ||||
|     if (b0 < 0x80) { | ||||
|         // 1-byte character
 | ||||
|         return { 1, b0 }; | ||||
|     } else if (b0 < 0xC0) { | ||||
|         // Unexpected continuation byte
 | ||||
|         return invalid_pt; | ||||
|     } else if (b0 < 0xE0) { | ||||
|         // 2-byte character
 | ||||
|         if (((b1 = str[i+1]) & 0xC0) != 0x80) | ||||
|             return invalid_pt; | ||||
| 
 | ||||
|         char32_t pt = (b0 & 0x1F) << 6 | (b1 & 0x3F); | ||||
|         if (pt < 0x80) | ||||
|             return invalid_pt; | ||||
| 
 | ||||
|         return { 2, pt }; | ||||
|     } else if (b0 < 0xF0) { | ||||
|         // 3-byte character
 | ||||
|         if (((b1 = str[i+1]) & 0xC0) != 0x80) | ||||
|             return invalid_pt; | ||||
|         if (((b2 = str[i+2]) & 0xC0) != 0x80) | ||||
|             return invalid_pt; | ||||
| 
 | ||||
|         char32_t pt = (b0 & 0x0F) << 12 | (b1 & 0x3F) << 6 | (b2 & 0x3F); | ||||
|         if (pt < 0x800) | ||||
|             return invalid_pt; | ||||
| 
 | ||||
|         return { 3, pt }; | ||||
|     } else if (b0 < 0xF8) { | ||||
|         // 4-byte character
 | ||||
|         if (((b1 = str[i+1]) & 0xC0) != 0x80) | ||||
|             return invalid_pt; | ||||
|         if (((b2 = str[i+2]) & 0xC0) != 0x80) | ||||
|             return invalid_pt; | ||||
|         if (((b3 = str[i+3]) & 0xC0) != 0x80) | ||||
|             return invalid_pt; | ||||
| 
 | ||||
|         char32_t pt = (b0 & 0x0F) << 18 | (b1 & 0x3F) << 12 | ||||
|                     | (b2 & 0x3F) << 6  | (b3 & 0x3F); | ||||
|         if (pt < 0x10000 || pt >= 0x110000) | ||||
|             return invalid_pt; | ||||
| 
 | ||||
|         return { 4, pt }; | ||||
|     } else { | ||||
|         // Codepoint out of range
 | ||||
|         return invalid_pt; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static char32_t utf8_decode(const std::string & str, std::string::size_type & i) { | ||||
|     offset_pt res = utf8_decode_check(str, i); | ||||
|     if (res.offset < 0) { | ||||
|         i += 1; | ||||
|         return 0xFFFD; | ||||
|     } else { | ||||
|         i += res.offset; | ||||
|         return res.pt; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void utf16_encode(char32_t pt, std::u16string & out) { | ||||
|     if (pt < 0x10000) { | ||||
|         out += static_cast<char16_t>(pt); | ||||
|     } else if (pt < 0x110000) { | ||||
|         out += { static_cast<char16_t>(((pt - 0x10000) >> 10) + 0xD800), | ||||
|                  static_cast<char16_t>((pt & 0x3FF) + 0xDC00) }; | ||||
|     } else { | ||||
|         out += 0xFFFD; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| jstring jniStringFromUTF8(JNIEnv * env, const std::string & str) { | ||||
| 
 | ||||
|     std::u16string utf16; | ||||
|     utf16.reserve(str.length()); // likely overallocate
 | ||||
|     for (std::string::size_type i = 0; i < str.length(); ) | ||||
|         utf16_encode(utf8_decode(str, i), utf16); | ||||
| 
 | ||||
|     jstring res = env->NewString( | ||||
|         reinterpret_cast<const jchar *>(utf16.data()), jsize(utf16.length())); | ||||
|     DJINNI_ASSERT(res, env); | ||||
|     return res; | ||||
| } | ||||
| 
 | ||||
| template<int wcharTypeSize> | ||||
| static std::u16string implWStringToUTF16(std::wstring::const_iterator, std::wstring::const_iterator) | ||||
| { | ||||
|     static_assert(wcharTypeSize == 2 || wcharTypeSize == 4, "wchar_t must be represented by UTF-16 or UTF-32 encoding"); | ||||
|     return {}; // unreachable
 | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| inline std::u16string implWStringToUTF16<2>(std::wstring::const_iterator begin, std::wstring::const_iterator end) { | ||||
|     // case when wchar_t is represented by utf-16 encoding
 | ||||
|     return std::u16string(begin, end); | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| inline std::u16string implWStringToUTF16<4>(std::wstring::const_iterator begin, std::wstring::const_iterator end) { | ||||
|     // case when wchar_t is represented by utf-32 encoding
 | ||||
|     std::u16string utf16; | ||||
|     utf16.reserve(std::distance(begin, end)); | ||||
|     for(; begin != end; ++begin) | ||||
|         utf16_encode(static_cast<char32_t>(*begin), utf16); | ||||
|     return utf16; | ||||
| } | ||||
| 
 | ||||
| inline std::u16string wstringToUTF16(const std::wstring & str) { | ||||
|     // hide "defined but not used" warnings
 | ||||
|     (void)implWStringToUTF16<2>; | ||||
|     (void)implWStringToUTF16<4>; | ||||
|     // Note: The template helper operates on iterators to work around a compiler issue we saw on Mac.
 | ||||
|     // It triggered undefined symbols if wstring methods were called directly in the template function.
 | ||||
|     return implWStringToUTF16<sizeof(wchar_t)>(str.cbegin(), str.cend()); | ||||
| } | ||||
| 
 | ||||
| jstring jniStringFromWString(JNIEnv * env, const std::wstring & str) { | ||||
|     std::u16string utf16 = wstringToUTF16(str); | ||||
|     jstring res = env->NewString( | ||||
|         reinterpret_cast<const jchar *>(utf16.data()), utf16.length()); | ||||
|     DJINNI_ASSERT(res, env); | ||||
|     return res; | ||||
| } | ||||
| 
 | ||||
| // UTF-16 decode helpers.
 | ||||
| static inline bool is_high_surrogate(char16_t c) { return (c >= 0xD800) && (c < 0xDC00); } | ||||
| static inline bool is_low_surrogate(char16_t c)  { return (c >= 0xDC00) && (c < 0xE000); } | ||||
| 
 | ||||
| /*
 | ||||
|  * Like utf8_decode_check, but for UTF-16. | ||||
|  */ | ||||
| static offset_pt utf16_decode_check(const char16_t * str, std::u16string::size_type i) { | ||||
|     if (is_high_surrogate(str[i]) && is_low_surrogate(str[i+1])) { | ||||
|         // High surrogate followed by low surrogate
 | ||||
|         char32_t pt = (((str[i] - 0xD800) << 10) | (str[i+1] - 0xDC00)) + 0x10000; | ||||
|         return { 2, pt }; | ||||
|     } else if (is_high_surrogate(str[i]) || is_low_surrogate(str[i])) { | ||||
|         // High surrogate *not* followed by low surrogate, or unpaired low surrogate
 | ||||
|         return invalid_pt; | ||||
|     } else { | ||||
|         return { 1, str[i] }; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static char32_t utf16_decode(const char16_t * str, std::u16string::size_type & i) { | ||||
|     offset_pt res = utf16_decode_check(str, i); | ||||
|     if (res.offset < 0) { | ||||
|         i += 1; | ||||
|         return 0xFFFD; | ||||
|     } else { | ||||
|         i += res.offset; | ||||
|         return res.pt; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void utf8_encode(char32_t pt, std::string & out) { | ||||
|     if (pt < 0x80) { | ||||
|         out += static_cast<char>(pt); | ||||
|     } else if (pt < 0x800) { | ||||
|         out += { static_cast<char>((pt >> 6)   | 0xC0), | ||||
|                  static_cast<char>((pt & 0x3F) | 0x80) }; | ||||
|     } else if (pt < 0x10000) { | ||||
|         out += { static_cast<char>((pt >> 12)         | 0xE0), | ||||
|                  static_cast<char>(((pt >> 6) & 0x3F) | 0x80), | ||||
|                  static_cast<char>((pt & 0x3F)        | 0x80) }; | ||||
|     } else if (pt < 0x110000) { | ||||
|         out += { static_cast<char>((pt >> 18)          | 0xF0), | ||||
|                  static_cast<char>(((pt >> 12) & 0x3F) | 0x80), | ||||
|                  static_cast<char>(((pt >> 6)  & 0x3F) | 0x80), | ||||
|                  static_cast<char>((pt & 0x3F)         | 0x80) }; | ||||
|     } else { | ||||
|         out += { static_cast<char>(0xEF), | ||||
|                  static_cast<char>(0xBF), | ||||
|                  static_cast<char>(0xBD) }; // U+FFFD
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| std::string jniUTF8FromString(JNIEnv * env, const jstring jstr) { | ||||
|     DJINNI_ASSERT(jstr, env); | ||||
|     const jsize length = env->GetStringLength(jstr); | ||||
|     jniExceptionCheck(env); | ||||
| 
 | ||||
|     const auto deleter = [env, jstr] (const jchar * c) { env->ReleaseStringChars(jstr, c); }; | ||||
|     std::unique_ptr<const jchar, decltype(deleter)> ptr(env->GetStringChars(jstr, nullptr), | ||||
|                                                         deleter); | ||||
| 
 | ||||
|     std::u16string str(reinterpret_cast<const char16_t *>(ptr.get()), length); | ||||
|     std::string out; | ||||
|     out.reserve(str.length() * 3 / 2); // estimate
 | ||||
|     for (std::u16string::size_type i = 0; i < str.length(); ) | ||||
|         utf8_encode(utf16_decode(str.data(), i), out); | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| template<int wcharTypeSize> | ||||
| static std::wstring implUTF16ToWString(const char16_t * /*data*/, size_t /*length*/) | ||||
| { | ||||
|     static_assert(wcharTypeSize == 2 || wcharTypeSize == 4, "wchar_t must be represented by UTF-16 or UTF-32 encoding"); | ||||
|     return {}; // unreachable
 | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| inline std::wstring implUTF16ToWString<2>(const char16_t * data, size_t length) { | ||||
|     // case when wchar_t is represented by utf-16 encoding
 | ||||
|     return std::wstring(data, data + length); | ||||
| } | ||||
| 
 | ||||
| template<> | ||||
| inline std::wstring implUTF16ToWString<4>(const char16_t * data, size_t length) { | ||||
|     // case when wchar_t is represented by utf-32 encoding
 | ||||
|     std::wstring result; | ||||
|     result.reserve(length); | ||||
|     for (size_t i = 0; i < length; ) | ||||
|         result += static_cast<wchar_t>(utf16_decode(data, i)); | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| inline std::wstring UTF16ToWString(const char16_t * data, size_t length) { | ||||
|     // hide "defined but not used" warnings
 | ||||
|     (void)implUTF16ToWString<2>; | ||||
|     (void)implUTF16ToWString<4>; | ||||
|     return implUTF16ToWString<sizeof(wchar_t)>(data, length); | ||||
| } | ||||
| 
 | ||||
| std::wstring jniWStringFromString(JNIEnv * env, const jstring jstr) { | ||||
|     DJINNI_ASSERT(jstr, env); | ||||
|     const jsize length = env->GetStringLength(jstr); | ||||
|     jniExceptionCheck(env); | ||||
| 
 | ||||
|     const auto deleter = [env, jstr] (const jchar * c) { env->ReleaseStringChars(jstr, c); }; | ||||
|     std::unique_ptr<const jchar, decltype(deleter)> ptr(env->GetStringChars(jstr, nullptr), | ||||
|                                                         deleter); | ||||
|     const char16_t * data = reinterpret_cast<const char16_t *>(ptr.get()); | ||||
|     return UTF16ToWString(data, length); | ||||
| } | ||||
| 
 | ||||
| DJINNI_WEAK_DEFINITION | ||||
| void jniSetPendingFromCurrent(JNIEnv * env, const char * ctx) noexcept { | ||||
|     jniDefaultSetPendingFromCurrent(env, ctx); | ||||
| } | ||||
| 
 | ||||
| void jniDefaultSetPendingFromCurrentImpl(JNIEnv * env) { | ||||
|     assert(env); | ||||
|     try { | ||||
|         throw; | ||||
|     } catch (const jni_exception & e) { | ||||
|         e.set_as_pending(env); | ||||
|         return; | ||||
|     } catch (const std::exception & e) { | ||||
|         env->ThrowNew(env->FindClass("java/lang/RuntimeException"), e.what()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void jniDefaultSetPendingFromCurrent(JNIEnv * env, const char * /*ctx*/) noexcept { | ||||
|   | ||||
|     /* It is necessary to go through a layer of indirection here because this
 | ||||
|     function is marked noexcept, but the implementation may still throw.  | ||||
|     Any exceptions which are not caught (i.e. exceptions which aren't  | ||||
|     std::exception subclasses) will result in a call to terminate() since this | ||||
|     function is marked noexcept */ | ||||
| 	 | ||||
| 	jniDefaultSetPendingFromCurrentImpl(env); | ||||
| } | ||||
| 
 | ||||
| template class ProxyCache<JavaProxyCacheTraits>; | ||||
| 
 | ||||
| CppProxyClassInfo::CppProxyClassInfo(const char * className) | ||||
|     : clazz(jniFindClass(className)), | ||||
|       constructor(jniGetMethodID(clazz.get(), "<init>", "(J)V")), | ||||
|       idField(jniGetFieldID(clazz.get(), "nativeRef", "J")) { | ||||
| } | ||||
| 
 | ||||
| CppProxyClassInfo::CppProxyClassInfo() : constructor{}, idField{} { | ||||
| } | ||||
| 
 | ||||
| CppProxyClassInfo::~CppProxyClassInfo() { | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Wrapper around Java WeakReference objects. (We can't use JNI NewWeakGlobalRef() because | ||||
|  * it doesn't have the right semantics - see comment in djinni_support.hpp.) | ||||
|  */ | ||||
| class JavaWeakRef { | ||||
| private: | ||||
|     struct JniInfo { | ||||
|     public: | ||||
|         const GlobalRef<jclass> clazz { jniFindClass("java/lang/ref/WeakReference") }; | ||||
|         const jmethodID constructor { jniGetMethodID(clazz.get(), "<init>", "(Ljava/lang/Object;)V") }; | ||||
|         const jmethodID method_get { jniGetMethodID(clazz.get(), "get", "()Ljava/lang/Object;") }; | ||||
|     }; | ||||
| 
 | ||||
|     // Helper used by constructor
 | ||||
|     static GlobalRef<jobject> create(JNIEnv * jniEnv, jobject obj) { | ||||
|         const JniInfo & weakRefClass = JniClass<JniInfo>::get(); | ||||
|         LocalRef<jobject> weakRef(jniEnv, jniEnv->NewObject(weakRefClass.clazz.get(), weakRefClass.constructor, obj)); | ||||
|         // DJINNI_ASSERT performs an exception check before anything else, so we don't need
 | ||||
|         // a separate jniExceptionCheck call.
 | ||||
|         DJINNI_ASSERT(weakRef, jniEnv); | ||||
|         return GlobalRef<jobject>(jniEnv, weakRef); | ||||
|     } | ||||
| 
 | ||||
| public: | ||||
|     // Constructor
 | ||||
|     JavaWeakRef(jobject obj) : JavaWeakRef(jniGetThreadEnv(), obj) {} | ||||
|     JavaWeakRef(JNIEnv * jniEnv, jobject obj) : m_weakRef(create(jniEnv, obj)) {} | ||||
| 
 | ||||
|     // Get the object pointed to if it's still strongly reachable or, return null if not.
 | ||||
|     // (Analogous to weak_ptr::lock.) Returns a local reference.
 | ||||
|     jobject lock() const { | ||||
|         const auto & jniEnv = jniGetThreadEnv(); | ||||
|         const JniInfo & weakRefClass = JniClass<JniInfo>::get(); | ||||
|         LocalRef<jobject> javaObj(jniEnv->CallObjectMethod(m_weakRef.get(), weakRefClass.method_get)); | ||||
|         jniExceptionCheck(jniEnv); | ||||
|         return javaObj.release(); | ||||
|     } | ||||
| 
 | ||||
|     // Java WeakReference objects don't have a way to check whether they're expired except
 | ||||
|     // by upgrading them to a strong ref.
 | ||||
|     bool expired() const { | ||||
|         LocalRef<jobject> javaObj { lock() }; | ||||
|         return !javaObj; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     GlobalRef<jobject> m_weakRef; | ||||
| }; | ||||
| 
 | ||||
| template class ProxyCache<JniCppProxyCacheTraits>; | ||||
| 
 | ||||
| } // namespace djinni
 | ||||
							
								
								
									
										658
									
								
								wrappers/android/mynteye/third_party/djinni/support-lib/jni/djinni_support.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										658
									
								
								wrappers/android/mynteye/third_party/djinni/support-lib/jni/djinni_support.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,658 @@ | ||||
| //
 | ||||
| // Copyright 2014 Dropbox, Inc.
 | ||||
| //
 | ||||
| // 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 <cassert> | ||||
| #include <exception> | ||||
| #include <memory> | ||||
| #include <mutex> | ||||
| #include <string> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "../proxy_cache_interface.hpp" | ||||
| #include "../djinni_common.hpp" | ||||
| #include <jni.h> | ||||
| 
 | ||||
| /*
 | ||||
|  * Djinni support library | ||||
|  */ | ||||
| 
 | ||||
| // jni.h should really put extern "C" in JNIEXPORT, but it doesn't. :(
 | ||||
| #define CJNIEXPORT extern "C" JNIEXPORT | ||||
| 
 | ||||
| namespace djinni { | ||||
| 
 | ||||
| /*
 | ||||
|  * Global initialization and shutdown. Call these from JNI_OnLoad and JNI_OnUnload. | ||||
|  */ | ||||
| void jniInit(JavaVM * jvm); | ||||
| void jniShutdown(); | ||||
| 
 | ||||
| /*
 | ||||
|  * Get the JNIEnv for the invoking thread. Should only be called on Java-created threads. | ||||
|  */ | ||||
| JNIEnv * jniGetThreadEnv(); | ||||
| 
 | ||||
| /*
 | ||||
|  * Global and local reference guard objects. | ||||
|  * | ||||
|  * A GlobalRef<T> is constructed with a local reference; the constructor upgrades the local | ||||
|  * reference to a global reference, and the destructor deletes the local ref. | ||||
|  * | ||||
|  * A LocalRef<T> should be constructed with a new local reference. The local reference will | ||||
|  * be deleted when the LocalRef is deleted. | ||||
|  */ | ||||
| struct GlobalRefDeleter { void operator() (jobject globalRef) noexcept; }; | ||||
| 
 | ||||
| template <typename PointerType> | ||||
| class GlobalRef : public std::unique_ptr<typename std::remove_pointer<PointerType>::type, | ||||
|                                          GlobalRefDeleter> { | ||||
| public: | ||||
|     GlobalRef() {} | ||||
|     GlobalRef(GlobalRef && obj) | ||||
|         : std::unique_ptr<typename std::remove_pointer<PointerType>::type, ::djinni::GlobalRefDeleter>( | ||||
|             std::move(obj) | ||||
|         ) {} | ||||
|     GlobalRef(JNIEnv * env, PointerType localRef) | ||||
|         : std::unique_ptr<typename std::remove_pointer<PointerType>::type, ::djinni::GlobalRefDeleter>( | ||||
|             static_cast<PointerType>(env->NewGlobalRef(localRef)), | ||||
|             ::djinni::GlobalRefDeleter{} | ||||
|         ) {} | ||||
| }; | ||||
| 
 | ||||
| struct LocalRefDeleter { void operator() (jobject localRef) noexcept; }; | ||||
| 
 | ||||
| template <typename PointerType> | ||||
| class LocalRef : public std::unique_ptr<typename std::remove_pointer<PointerType>::type, | ||||
|                                         LocalRefDeleter> { | ||||
| public: | ||||
|     LocalRef() {} | ||||
|     LocalRef(JNIEnv * /*env*/, PointerType localRef) | ||||
|         : std::unique_ptr<typename std::remove_pointer<PointerType>::type, ::djinni::LocalRefDeleter>( | ||||
|             localRef) {} | ||||
|     explicit LocalRef(PointerType localRef) | ||||
|         : std::unique_ptr<typename std::remove_pointer<PointerType>::type, LocalRefDeleter>( | ||||
|             localRef) {} | ||||
|     // Allow implicit conversion to PointerType so it can be passed
 | ||||
|     // as argument to JNI functions expecting PointerType.
 | ||||
|     // All functions creating new local references should return LocalRef instead of PointerType
 | ||||
|     operator PointerType() const & { return this->get(); } | ||||
|     operator PointerType() && = delete; | ||||
| }; | ||||
| 
 | ||||
| template<class T> | ||||
| const T& get(const T& x) noexcept { return x; } | ||||
| template<class T> | ||||
| typename LocalRef<T>::pointer get(const LocalRef<T>& x) noexcept { return x.get(); } | ||||
| 
 | ||||
| template<class T> | ||||
| const T& release(const T& x) noexcept { return x; } | ||||
| template<class T> | ||||
| typename LocalRef<T>::pointer release(LocalRef<T>& x) noexcept { return x.release(); } | ||||
| template<class T> | ||||
| typename LocalRef<T>::pointer release(LocalRef<T>&& x) noexcept { return x.release(); } | ||||
| 
 | ||||
| /*
 | ||||
|  * Exception to indicate that a Java exception is pending in the JVM. | ||||
|  */ | ||||
| class jni_exception : public std::exception { | ||||
|     GlobalRef<jthrowable> m_java_exception; | ||||
| public: | ||||
|     jni_exception(JNIEnv * env, jthrowable java_exception) | ||||
|         : m_java_exception(env, java_exception) { | ||||
|         assert(java_exception); | ||||
|     } | ||||
|     jthrowable java_exception() const { return m_java_exception.get(); } | ||||
| 
 | ||||
|     /*
 | ||||
|      * Sets the pending JNI exception using this Java exception. | ||||
|      */ | ||||
|     void set_as_pending(JNIEnv * env) const noexcept; | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Throw if any Java exception is pending in the JVM. | ||||
|  * | ||||
|  * If an exception is pending, this function will clear the | ||||
|  * pending state, and pass the exception to | ||||
|  * jniThrowCppFromJavaException(). | ||||
|  */ | ||||
| void jniExceptionCheck(JNIEnv * env); | ||||
| 
 | ||||
| /*
 | ||||
|  * Throws a C++ exception based on the given Java exception. | ||||
|  * | ||||
|  * java_exception is a local reference to a Java throwable, which | ||||
|  * must not be null, and should no longer set as "pending" in the JVM. | ||||
|  * This is called to handle errors in other JNI processing, including | ||||
|  * by jniExceptionCheck(). | ||||
|  * | ||||
|  * The default implementation is defined with __attribute__((weak)) so you | ||||
|  * can replace it by defining your own version.  The default implementation | ||||
|  * will throw a jni_exception containing the given jthrowable. | ||||
|  */ | ||||
| DJINNI_NORETURN_DEFINITION | ||||
| void jniThrowCppFromJavaException(JNIEnv * env, jthrowable java_exception); | ||||
| 
 | ||||
| /*
 | ||||
|  * Set an AssertionError in env with message message, and then throw via jniExceptionCheck. | ||||
|  */ | ||||
| DJINNI_NORETURN_DEFINITION | ||||
| void jniThrowAssertionError(JNIEnv * env, const char * file, int line, const char * check); | ||||
| 
 | ||||
| #define DJINNI_ASSERT_MSG(check, env, message) \ | ||||
|     do { \ | ||||
|         ::djinni::jniExceptionCheck(env); \ | ||||
|         const bool check__res = bool(check); \ | ||||
|         ::djinni::jniExceptionCheck(env); \ | ||||
|         if (!check__res) { \ | ||||
|             ::djinni::jniThrowAssertionError(env, __FILE__, __LINE__, message); \ | ||||
|         } \ | ||||
|     } while(false) | ||||
| #define DJINNI_ASSERT(check, env) DJINNI_ASSERT_MSG(check, env, #check) | ||||
| 
 | ||||
| /*
 | ||||
|  * Helper for JniClass. (This can't be a subclass because it needs to not be templatized.) | ||||
|  */ | ||||
| class JniClassInitializer { | ||||
| 
 | ||||
|     using registration_vec = std::vector<std::function<void()>>; | ||||
|     static registration_vec get_all(); | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
|     JniClassInitializer(std::function<void()> init); | ||||
| 
 | ||||
|     template <class C> friend class JniClass; | ||||
|     friend void jniInit(JavaVM *); | ||||
| 
 | ||||
|     static registration_vec & get_vec(); | ||||
|     static std::mutex       & get_mutex(); | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Each instantiation of this template produces a singleton object of type C which | ||||
|  * will be initialized by djinni::jniInit(). For example: | ||||
|  * | ||||
|  * struct JavaFooInfo { | ||||
|  *     jmethodID foo; | ||||
|  *     JavaFooInfo() // initialize clazz and foo from jniGetThreadEnv
 | ||||
|  * } | ||||
|  * | ||||
|  * To use this in a JNI function or callback, invoke: | ||||
|  * | ||||
|  *     CallVoidMethod(object, JniClass<JavaFooInfo>::get().foo, ...); | ||||
|  * | ||||
|  * This uses C++'s template instantiation behavior to guarantee that any T for which | ||||
|  * JniClass<T>::get() is *used* anywhere in the program will be *initialized* by init_all(). | ||||
|  * Therefore, it's always safe to compile in wrappers for all known Java types - the library | ||||
|  * will only depend on the presence of those actually needed. | ||||
|  */ | ||||
| template <class C> | ||||
| class JniClass { | ||||
| public: | ||||
|     static const C & get() { | ||||
|         (void)s_initializer; // ensure that initializer is actually instantiated
 | ||||
|         assert(s_singleton); | ||||
|         return *s_singleton; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     static const JniClassInitializer s_initializer; | ||||
|     static std::unique_ptr<C> s_singleton; | ||||
| 
 | ||||
|     static void allocate() { | ||||
|         // We can't use make_unique here, because C will have a private constructor and
 | ||||
|         // list JniClass as a friend; so we have to allocate it by hand.
 | ||||
|         s_singleton = std::unique_ptr<C>(new C()); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| template <class C> | ||||
| const JniClassInitializer JniClass<C>::s_initializer ( allocate ); | ||||
| 
 | ||||
| template <class C> | ||||
| std::unique_ptr<C> JniClass<C>::s_singleton; | ||||
| 
 | ||||
| /*
 | ||||
|  * Exception-checking helpers. These will throw if an exception is pending. | ||||
|  */ | ||||
| GlobalRef<jclass> jniFindClass(const char * name); | ||||
| jmethodID jniGetStaticMethodID(jclass clazz, const char * name, const char * sig); | ||||
| jmethodID jniGetMethodID(jclass clazz, const char * name, const char * sig); | ||||
| jfieldID jniGetFieldID(jclass clazz, const char * name, const char * sig); | ||||
| 
 | ||||
| /*
 | ||||
|  * Helper for maintaining shared_ptrs to wrapped Java objects. | ||||
|  * | ||||
|  * This is used for automatically wrapping a Java object that exposes some interface | ||||
|  * with a C++ object that calls back into the JVM, such as a listener. Calling | ||||
|  * get_java_proxy<T>(obj) the first time will construct a T and return a shared_ptr to it, and | ||||
|  * also save a weak_ptr to the new object internally. The constructed T contains a strong | ||||
|  * GlobalRef to jobj. As long as something in C++ maintains a strong reference to the wrapper, | ||||
|  * future calls to get(jobj) will return the *same* wrapper object. | ||||
|  * | ||||
|  *        Java            |           C++ | ||||
|  *                        |        ________________________                ___________ | ||||
|  *   _____________        |       |                        |              |           | | ||||
|  *  |             |       |       |   JniImplFooListener   | <=========== |    Foo    | | ||||
|  *  | FooListener | <============ |  : public FooListener, |  shared_ptr  |___________| | ||||
|  *  |_____________|   GlobalRef   |    JavaProxyCacheEntry | | ||||
|  *                        |       |________________________| | ||||
|  *                        |                 ^             ______________________ | ||||
|  *                        |                 \            |                      | | ||||
|  *                        |                  - - - - - - |    JavaProxyCache    | | ||||
|  *                        |                   weak_ptr   |______________________| | ||||
|  * | ||||
|  * As long as the C++ FooListener has references, the Java FooListener is kept alive. | ||||
|  * | ||||
|  * We use a custom unordered_map with Java objects (jobject) as keys, and JNI object | ||||
|  * identity and hashing functions. This means that as long as a key is in the map, | ||||
|  * we must have some other GlobalRef keeping it alive. To ensure safety, the Entry | ||||
|  * destructor removes *itself* from the map - destruction order guarantees that this | ||||
|  * will happen before the contained global reference becomes invalid (by destruction of | ||||
|  * the GlobalRef). | ||||
|  */ | ||||
| struct JavaIdentityHash; | ||||
| struct JavaIdentityEquals; | ||||
| struct JavaProxyCacheTraits { | ||||
|     using UnowningImplPointer = jobject; | ||||
|     using OwningImplPointer = jobject; | ||||
|     using OwningProxyPointer = std::shared_ptr<void>; | ||||
|     using WeakProxyPointer = std::weak_ptr<void>; | ||||
|     using UnowningImplPointerHash = JavaIdentityHash; | ||||
|     using UnowningImplPointerEqual = JavaIdentityEquals; | ||||
| }; | ||||
| extern template class ProxyCache<JavaProxyCacheTraits>; | ||||
| using JavaProxyCache = ProxyCache<JavaProxyCacheTraits>; | ||||
| template <typename T> using JavaProxyHandle = JavaProxyCache::Handle<GlobalRef<jobject>, T>; | ||||
| 
 | ||||
| /*
 | ||||
|  * Cache for CppProxy objects. This is the inverse of the JavaProxyCache mechanism above, | ||||
|  * ensuring that each time we pass an interface from Java to C++, we get the *same* CppProxy | ||||
|  * object on the Java side: | ||||
|  * | ||||
|  *      Java               |            C++ | ||||
|  *                         | | ||||
|  *    ______________       |         ________________                  ___________ | ||||
|  *   |              |      |        |                |                |           | | ||||
|  *   | Foo.CppProxy | ------------> | CppProxyHandle | =============> |    Foo    | | ||||
|  *   |______________|   (jlong)     |      <Foo>     |  (shared_ptr)  |___________| | ||||
|  *           ^             |        |________________| | ||||
|  *            \            | | ||||
|  *        _________        |                     __________________ | ||||
|  *       |         |       |                    |                  | | ||||
|  *       | WeakRef | <------------------------- | jniCppProxyCache | | ||||
|  *       |_________|  (GlobalRef)               |__________________| | ||||
|  *                         | | ||||
|  * | ||||
|  * We don't use JNI WeakGlobalRef objects, because they last longer than is safe - a | ||||
|  * WeakGlobalRef can still be upgraded to a strong reference even during finalization, which | ||||
|  * leads to use-after-free. Java WeakRefs provide the right lifetime guarantee. | ||||
|  */ | ||||
| class JavaWeakRef; | ||||
| struct JniCppProxyCacheTraits { | ||||
|     using UnowningImplPointer = void *; | ||||
|     using OwningImplPointer = std::shared_ptr<void>; | ||||
|     using OwningProxyPointer = jobject; | ||||
|     using WeakProxyPointer = JavaWeakRef; | ||||
|     using UnowningImplPointerHash = std::hash<void *>; | ||||
|     using UnowningImplPointerEqual = std::equal_to<void *>; | ||||
| }; | ||||
| extern template class ProxyCache<JniCppProxyCacheTraits>; | ||||
| using JniCppProxyCache = ProxyCache<JniCppProxyCacheTraits>; | ||||
| template <class T> using CppProxyHandle = JniCppProxyCache::Handle<std::shared_ptr<T>>; | ||||
| 
 | ||||
| template <class T> | ||||
| static const std::shared_ptr<T> & objectFromHandleAddress(jlong handle) { | ||||
|     assert(handle); | ||||
|     assert(handle > 4096); | ||||
|     // Below line segfaults gcc-4.8. Using a temporary variable hides the bug.
 | ||||
|     //const auto & ret = reinterpret_cast<const CppProxyHandle<T> *>(handle)->get();
 | ||||
|     const CppProxyHandle<T> *proxy_handle = | ||||
|         reinterpret_cast<const CppProxyHandle<T> *>(handle); | ||||
|     const auto & ret = proxy_handle->get(); | ||||
|     assert(ret); | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Information needed to use a CppProxy class. | ||||
|  * | ||||
|  * In an ideal world, this object would be properly always-valid RAII, and we'd use an | ||||
|  * optional<CppProxyClassInfo> where needed. Unfortunately we don't want to depend on optional | ||||
|  * here, so this object has an invalid state and default constructor. | ||||
|  */ | ||||
| struct CppProxyClassInfo { | ||||
|     const GlobalRef<jclass> clazz; | ||||
|     const jmethodID constructor; | ||||
|     const jfieldID idField; | ||||
| 
 | ||||
|     CppProxyClassInfo(const char * className); | ||||
|     CppProxyClassInfo(); | ||||
|     ~CppProxyClassInfo(); | ||||
| 
 | ||||
|     // Validity check
 | ||||
|     explicit operator bool() const { return bool(clazz); } | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Base class for Java <-> C++ interface adapters. | ||||
|  * | ||||
|  * I is the C++ base class (interface) being adapted; Self is the interface adapter class | ||||
|  * derived from JniInterface (using CRTP). For example: | ||||
|  * | ||||
|  *     class NativeToken final : djinni::JniInterface<Token, NativeToken> { ... } | ||||
|  */ | ||||
| template <class I, class Self> | ||||
| class JniInterface { | ||||
| public: | ||||
|     /*
 | ||||
|      * Given a C++ object, find or create a Java version. The cases here are: | ||||
|      * 1. Null | ||||
|      * 2. The provided C++ object is actually a JavaProxy (C++-side proxy for Java impl) | ||||
|      * 3. The provided C++ object has an existing CppProxy (Java-side proxy for C++ impl) | ||||
|      * 4. The provided C++ object needs a new CppProxy allocated | ||||
|      */ | ||||
|     jobject _toJava(JNIEnv* jniEnv, const std::shared_ptr<I> & c) const { | ||||
|         // Case 1 - null
 | ||||
|         if (!c) { | ||||
|             return nullptr; | ||||
|         } | ||||
| 
 | ||||
|         // Case 2 - already a JavaProxy. Only possible if Self::JavaProxy exists.
 | ||||
|         if (jobject impl = _unwrapJavaProxy<Self>(&c)) { | ||||
|             return jniEnv->NewLocalRef(impl); | ||||
|         } | ||||
| 
 | ||||
|         // Cases 3 and 4.
 | ||||
|         assert(m_cppProxyClass); | ||||
|         return JniCppProxyCache::get(typeid(c), c, &newCppProxy); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /*
 | ||||
|      * Given a Java object, find or create a C++ version. The cases here are: | ||||
|      * 1. Null | ||||
|      * 2. The provided Java object is actually a CppProxy (Java-side proxy for a C++ impl) | ||||
|      * 3. The provided Java object has an existing JavaProxy (C++-side proxy for a Java impl) | ||||
|      * 4. The provided Java object needs a new JavaProxy allocated | ||||
|      */ | ||||
|     std::shared_ptr<I> _fromJava(JNIEnv* jniEnv, jobject j) const { | ||||
|         // Case 1 - null
 | ||||
|         if (!j) { | ||||
|             return nullptr; | ||||
|         } | ||||
| 
 | ||||
|         // Case 2 - already a Java proxy; we just need to pull the C++ impl out. (This case
 | ||||
|         // is only possible if we were constructed with a cppProxyClassName parameter.)
 | ||||
|         if (m_cppProxyClass | ||||
|                 && jniEnv->IsSameObject(jniEnv->GetObjectClass(j), m_cppProxyClass.clazz.get())) { | ||||
|             jlong handle = jniEnv->GetLongField(j, m_cppProxyClass.idField); | ||||
|             jniExceptionCheck(jniEnv); | ||||
|             return objectFromHandleAddress<I>(handle); | ||||
|         } | ||||
| 
 | ||||
|         // Cases 3 and 4 - see _getJavaProxy helper below. JavaProxyCache is responsible for
 | ||||
|         // distinguishing between the two cases. Only possible if Self::JavaProxy exists.
 | ||||
|         return _getJavaProxy<Self>(j); | ||||
|     } | ||||
| 
 | ||||
|     // Constructor for interfaces for which a Java-side CppProxy class exists
 | ||||
|     JniInterface(const char * cppProxyClassName) : m_cppProxyClass(cppProxyClassName) {} | ||||
| 
 | ||||
|     // Constructor for interfaces without a Java proxy class
 | ||||
|     JniInterface() : m_cppProxyClass{} {} | ||||
| 
 | ||||
| private: | ||||
|     /*
 | ||||
|      * Helpers for _toJava above. The possibility that an object is already a C++-side proxy | ||||
|      * only exists if the code generator emitted one (if Self::JavaProxy exists). | ||||
|      */ | ||||
|     template <typename S, typename JavaProxy = typename S::JavaProxy> | ||||
|     jobject _unwrapJavaProxy(const std::shared_ptr<I> * c) const { | ||||
|         if (auto proxy = dynamic_cast<JavaProxy *>(c->get())) { | ||||
|             return proxy->JavaProxyHandle<JavaProxy>::get().get(); | ||||
|         } else { | ||||
|             return nullptr; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     template <typename S> | ||||
|     jobject _unwrapJavaProxy(...) const { | ||||
|         return nullptr; | ||||
|     } | ||||
| 
 | ||||
|     /*
 | ||||
|      * Helper for _toJava above: given a C++ object, allocate a CppProxy on the Java side for | ||||
|      * it. This is actually called by jniCppProxyCacheGet, which holds a lock on the global | ||||
|      * C++-to-Java proxy map object. | ||||
|      */ | ||||
|     static std::pair<jobject, void*> newCppProxy(const std::shared_ptr<void> & cppObj) { | ||||
|         const auto & data = JniClass<Self>::get(); | ||||
|         const auto & jniEnv = jniGetThreadEnv(); | ||||
|         std::unique_ptr<CppProxyHandle<I>> to_encapsulate( | ||||
|                 new CppProxyHandle<I>(std::static_pointer_cast<I>(cppObj))); | ||||
|         jlong handle = static_cast<jlong>(reinterpret_cast<uintptr_t>(to_encapsulate.get())); | ||||
|         jobject cppProxy = jniEnv->NewObject(data.m_cppProxyClass.clazz.get(), | ||||
|                                              data.m_cppProxyClass.constructor, | ||||
|                                              handle); | ||||
|         jniExceptionCheck(jniEnv); | ||||
|         to_encapsulate.release(); | ||||
|         return { cppProxy, cppObj.get() }; | ||||
|     } | ||||
| 
 | ||||
|     /*
 | ||||
|      * Helpers for _fromJava above. We can only produce a C++-side proxy if the code generator | ||||
|      * emitted one (if Self::JavaProxy exists). | ||||
|      */ | ||||
|     template <typename S, typename JavaProxy = typename S::JavaProxy> | ||||
|     std::shared_ptr<I> _getJavaProxy(jobject j) const { | ||||
|         static_assert(std::is_base_of<JavaProxyHandle<JavaProxy>, JavaProxy>::value, | ||||
|             "JavaProxy must derive from JavaProxyCacheEntry"); | ||||
| 
 | ||||
|         return std::static_pointer_cast<JavaProxy>(JavaProxyCache::get( | ||||
|             typeid(JavaProxy), j, | ||||
|             [] (const jobject & obj) -> std::pair<std::shared_ptr<void>, jobject> { | ||||
|                 auto ret = std::make_shared<JavaProxy>(obj); | ||||
|                 return { ret, ret->JavaProxyHandle<JavaProxy>::get().get() }; | ||||
|             } | ||||
|         )); | ||||
|     } | ||||
| 
 | ||||
|     template <typename S> | ||||
|     std::shared_ptr<I> _getJavaProxy(...) const { | ||||
|         assert(false); | ||||
|         return nullptr; | ||||
|     } | ||||
| 
 | ||||
|     const CppProxyClassInfo m_cppProxyClass; | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * Guard object which automatically begins and ends a JNI local frame when | ||||
|  * it is created and destroyed, using PushLocalFrame and PopLocalFrame. | ||||
|  * | ||||
|  * Local frame creation can fail. The throwOnError parameter specifies how | ||||
|  * errors are reported: | ||||
|  * - true (default): throws on failure | ||||
|  * - false: queues a JNI exception on failure; the user must call checkSuccess() | ||||
|  * | ||||
|  * The JNIEnv given at construction is expected to still be valid at | ||||
|  * destruction, so this class isn't suitable for use across threads. | ||||
|  * It is intended for use on the stack. | ||||
|  * | ||||
|  * All JNI local references created within the defined scope will be | ||||
|  * released at the end of the scope.  This class doesn't support | ||||
|  * the jobject return value supported by PopLocalFrame(), because | ||||
|  * the destructor cannot return the new reference value for the parent | ||||
|  * frame. | ||||
|  */ | ||||
| class JniLocalScope { | ||||
| public: | ||||
|     /*
 | ||||
|      * Create the guard object and begin the local frame. | ||||
|      * | ||||
|      * @param p_env the JNIEnv for the current thread. | ||||
|      * @param capacity the initial number of local references to | ||||
|      *  allocate. | ||||
|      */ | ||||
|     JniLocalScope(JNIEnv* p_env, jint capacity, bool throwOnError = true); | ||||
|     bool checkSuccess() const { return m_success; } | ||||
|     ~JniLocalScope(); | ||||
| private: | ||||
|     JniLocalScope(const JniLocalScope& other); | ||||
|     JniLocalScope& operator=(const JniLocalScope& other); | ||||
| 
 | ||||
|     static bool _pushLocalFrame(JNIEnv* const env, jint capacity); | ||||
|     static void _popLocalFrame(JNIEnv* const env, jobject returnRef); | ||||
| 
 | ||||
|     JNIEnv* const m_env; | ||||
|     const bool m_success; | ||||
| }; | ||||
| 
 | ||||
| jstring jniStringFromUTF8(JNIEnv * env, const std::string & str); | ||||
| std::string jniUTF8FromString(JNIEnv * env, const jstring jstr); | ||||
| 
 | ||||
| jstring jniStringFromWString(JNIEnv * env, const std::wstring & str); | ||||
| std::wstring jniWStringFromString(JNIEnv * env, const jstring jstr); | ||||
| 
 | ||||
| class JniEnum { | ||||
| public: | ||||
|     /*
 | ||||
|      * Given a Java object, find its numeric value. This returns a jint, which the caller can | ||||
|      * static_cast<> into the necessary C++ enum type. | ||||
|      */ | ||||
|     jint ordinal(JNIEnv * env, jobject obj) const; | ||||
| 
 | ||||
|     /*
 | ||||
|      * Create a Java value of the wrapped class with the given value. | ||||
|      */ | ||||
|     LocalRef<jobject> create(JNIEnv * env, jint value) const; | ||||
| 
 | ||||
| protected: | ||||
|     JniEnum(const std::string & name); | ||||
|     jclass enumClass() const { return m_clazz.get(); } | ||||
| 
 | ||||
| private: | ||||
|     const GlobalRef<jclass> m_clazz; | ||||
|     const jmethodID m_staticmethValues; | ||||
|     const jmethodID m_methOrdinal; | ||||
| }; | ||||
| 
 | ||||
| class JniFlags : private JniEnum { | ||||
| public: | ||||
|     /*
 | ||||
|      * Given a Java EnumSet convert it to the corresponding bit pattern | ||||
|      * which can then be static_cast<> to the actual enum. | ||||
|      */ | ||||
|     unsigned flags(JNIEnv * env, jobject obj) const; | ||||
| 
 | ||||
|     /*
 | ||||
|      * Create a Java EnumSet of the specified flags considering the given number of active bits. | ||||
|      */ | ||||
|     LocalRef<jobject> create(JNIEnv * env, unsigned flags, int bits) const; | ||||
| 
 | ||||
|     using JniEnum::create; | ||||
| 
 | ||||
| protected: | ||||
|     JniFlags(const std::string & name); | ||||
| 
 | ||||
| private: | ||||
|     const GlobalRef<jclass> m_clazz { jniFindClass("java/util/EnumSet") }; | ||||
|     const jmethodID m_methNoneOf { jniGetStaticMethodID(m_clazz.get(), "noneOf", "(Ljava/lang/Class;)Ljava/util/EnumSet;") }; | ||||
|     const jmethodID m_methAdd { jniGetMethodID(m_clazz.get(), "add", "(Ljava/lang/Object;)Z") }; | ||||
|     const jmethodID m_methIterator { jniGetMethodID(m_clazz.get(), "iterator", "()Ljava/util/Iterator;") }; | ||||
|     const jmethodID m_methSize { jniGetMethodID(m_clazz.get(), "size", "()I") }; | ||||
| 
 | ||||
|     struct { | ||||
|         const GlobalRef<jclass> clazz { jniFindClass("java/util/Iterator") }; | ||||
|         const jmethodID methNext { jniGetMethodID(clazz.get(), "next", "()Ljava/lang/Object;") }; | ||||
|     } m_iterator; | ||||
| }; | ||||
| 
 | ||||
| #define DJINNI_FUNCTION_PROLOGUE0(env_) | ||||
| #define DJINNI_FUNCTION_PROLOGUE1(env_, arg1_) | ||||
| 
 | ||||
| /*
 | ||||
|  * Helper for JNI_TRANSLATE_EXCEPTIONS_RETURN. | ||||
|  * | ||||
|  * Must be called in a catch block.  Responsible for setting the pending | ||||
|  * exception in JNI based on the current C++ exception. | ||||
|  * | ||||
|  * The default implementation is defined with __attribute__((weak)) so you | ||||
|  * can replace it by defining your own version.  The default implementation | ||||
|  * will call jniDefaultSetPendingFromCurrent(), which will propagate a | ||||
|  * jni_exception directly into Java, or throw a RuntimeException for any | ||||
|  * other std::exception. | ||||
|  */ | ||||
| void jniSetPendingFromCurrent(JNIEnv * env, const char * ctx) noexcept; | ||||
| 
 | ||||
| /*
 | ||||
|  * Helper for JNI_TRANSLATE_EXCEPTIONS_RETURN. | ||||
|  * | ||||
|  * Must be called in a catch block.  Responsible for setting the pending | ||||
|  * exception in JNI based on the current C++ exception. | ||||
|  * | ||||
|  * This will call jniSetPendingFrom(env, jni_exception) if the current exception | ||||
|  * is a jni_exception, or otherwise will set a RuntimeException from any | ||||
|  * other std::exception.  Any non-std::exception will result in a call | ||||
|  * to terminate(). | ||||
|  * | ||||
|  * This is called by the default implementation of jniSetPendingFromCurrent. | ||||
|  */ | ||||
| void jniDefaultSetPendingFromCurrent(JNIEnv * env, const char * ctx) noexcept; | ||||
| 
 | ||||
| /* Catch C++ exceptions and translate them to Java exceptions.
 | ||||
|  * | ||||
|  * All functions called by Java must be fully wrapped by an outer try...catch block like so: | ||||
|  * | ||||
|  * try { | ||||
|  *     ... | ||||
|  * } JNI_TRANSLATE_EXCEPTIONS_RETURN(env, 0) | ||||
|  * ... or JNI_TRANSLATE_EXCEPTIONS_RETURN(env, ) for functions returning void | ||||
|  * | ||||
|  * The second parameter is a default return value to be used if an exception is caught and | ||||
|  * converted. (For JNI outer-layer calls, this result will always be ignored by JNI, so | ||||
|  * it can safely be 0 for any function with a non-void return value.) | ||||
|  */ | ||||
| #define JNI_TRANSLATE_EXCEPTIONS_RETURN(env, ret) \ | ||||
|     catch (const std::exception &) { \ | ||||
|         ::djinni::jniSetPendingFromCurrent(env, __func__); \ | ||||
|         return ret; \ | ||||
|     } | ||||
| 
 | ||||
| /* Catch jni_exception and translate it back to a Java exception, without catching
 | ||||
|  * any other C++ exceptions.  Can be used to wrap code which might cause JNI | ||||
|  * exceptions like so: | ||||
|  * | ||||
|  * try { | ||||
|  *     ... | ||||
|  * } JNI_TRANSLATE_JAVA_EXCEPTIONS_RETURN(env, 0) | ||||
|  * ... or JNI_TRANSLATE_JAVA_EXCEPTIONS_RETURN(env, ) for functions returning void | ||||
|  * | ||||
|  * The second parameter is a default return value to be used if an exception is caught and | ||||
|  * converted. (For JNI outer-layer calls, this result will always be ignored by JNI, so | ||||
|  * it can safely be 0 for any function with a non-void return value.) | ||||
|  */ | ||||
| #define JNI_TRANSLATE_JNI_EXCEPTIONS_RETURN(env, ret) \ | ||||
|     catch (const ::djinni::jni_exception & e) { \ | ||||
|         e.set_as_pending(env); \ | ||||
|         return ret; \ | ||||
|     } | ||||
| 
 | ||||
| } // namespace djinni
 | ||||
							
								
								
									
										176
									
								
								wrappers/android/mynteye/third_party/djinni/support-lib/proxy_cache_impl.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								wrappers/android/mynteye/third_party/djinni/support-lib/proxy_cache_impl.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,176 @@ | ||||
| //
 | ||||
| // Copyright 2015 Dropbox, Inc.
 | ||||
| //
 | ||||
| // 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 "proxy_cache_interface.hpp" | ||||
| #include <functional> | ||||
| #include <mutex> | ||||
| #include <unordered_map> | ||||
| 
 | ||||
| // """
 | ||||
| //    This place is not a place of honor.
 | ||||
| //    No highly esteemed deed is commemorated here.
 | ||||
| //    Nothing valued is here.
 | ||||
| //    This place is a message and part of a system of messages.
 | ||||
| //    Pay attention to it!
 | ||||
| //    Sending this message was important to us.
 | ||||
| //    We considered ourselves to be a powerful culture.
 | ||||
| // """
 | ||||
| //
 | ||||
| // From "Expert Judgment on Markers to Deter Inadvertent Human Intrusion into the Waste
 | ||||
| // Isolation Pilot Plant", Sandia National Laboratories report SAND92-1382 / UC-721, p. F-49
 | ||||
| 
 | ||||
| namespace djinni { | ||||
| 
 | ||||
| // See comment on `get_unowning()` in proxy_cache_interface.hpp.
 | ||||
| template <typename T> static inline auto upgrade_weak(const T & ptr) -> decltype(ptr.lock()) { | ||||
|     return ptr.lock(); | ||||
| } | ||||
| template <typename T> static inline T * upgrade_weak(T* ptr) { return ptr; } | ||||
| template <typename T> static inline bool is_expired(const T & ptr) { return ptr.expired(); } | ||||
| template <typename T> static inline bool is_expired(T* ptr) { return !ptr; } | ||||
| 
 | ||||
| /*
 | ||||
|  * Generic proxy cache. | ||||
|  * | ||||
|  * This provides a general-purpose mechanism for proxies to be re-used. When we pass an object | ||||
|  * across the language boundary from A to B, we must create a proxy object within language B | ||||
|  * that passes calls back to language A. For example, if have a C++ object that is passed into | ||||
|  * Java, we would create a Java object that owns a `shared_ptr` and has a set of native methods | ||||
|  * that call in to C++. | ||||
|  * | ||||
|  * When we create such an object, we also want to cache a weak reference to it, so that if we | ||||
|  * later pass the *same* object across the boundary, the same proxy will be returned. This is | ||||
|  * necessary for correctness in some situations: for example, in the case of an `add_listener` | ||||
|  * and `remove_listener` pattern. | ||||
|  * | ||||
|  * To reduce code size, only one GenericProxyCache need be instantiated for each language | ||||
|  * boundary direction. The pointer types passed to this function can be generic, e.g. `id`, | ||||
|  * `shared_ptr<void>`, `jobject`, etc. | ||||
|  * | ||||
|  * In the types below, "Impl" refers to some interface that is being wrapped, and Proxy refers | ||||
|  * to the generated other-language object that wraps it. | ||||
|  */ | ||||
| template <typename Traits> | ||||
| class ProxyCache<Traits>::Pimpl { | ||||
|     using Key = std::pair<std::type_index, UnowningImplPointer>; | ||||
| 
 | ||||
| public: | ||||
|     /*
 | ||||
|      * Look up an object in the proxy cache, and create a new one if not found. | ||||
|      * | ||||
|      * This takes a function pointer, not an arbitrary functor, because we want to minimize | ||||
|      * code size: this function should only be instantiated *once* per langauge direction. | ||||
|      */ | ||||
|     OwningProxyPointer get(const std::type_index & tag, | ||||
|                            const OwningImplPointer & impl, | ||||
|                            AllocatorFunction * alloc) { | ||||
|         std::unique_lock<std::mutex> lock(m_mutex); | ||||
|         UnowningImplPointer ptr = get_unowning(impl); | ||||
|         auto existing_proxy_iter = m_mapping.find({tag, ptr}); | ||||
|         if (existing_proxy_iter != m_mapping.end()) { | ||||
|             OwningProxyPointer existing_proxy = upgrade_weak(existing_proxy_iter->second); | ||||
|             if (existing_proxy) { | ||||
|                 return existing_proxy; | ||||
|             } else { | ||||
|                 // The weak reference is expired, so prune it from the map eagerly.
 | ||||
|                 m_mapping.erase(existing_proxy_iter); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         auto alloc_result = alloc(impl); | ||||
|         m_mapping.emplace(Key{tag, alloc_result.second}, alloc_result.first); | ||||
|         return alloc_result.first; | ||||
|     } | ||||
| 
 | ||||
|     /*
 | ||||
|      * Erase an object from the proxy cache. | ||||
|      */ | ||||
|     void remove(const std::type_index & tag, const UnowningImplPointer & impl_unowning) { | ||||
|         std::unique_lock<std::mutex> lock(m_mutex); | ||||
|         auto it = m_mapping.find({tag, impl_unowning}); | ||||
|         if (it != m_mapping.end()) { | ||||
|             // The entry in the map should already be expired: this is called from Handle's
 | ||||
|             // destructor, so the proxy must already be gone. However, remove() does not
 | ||||
|             // happen atomically with the proxy object becoming weakly reachable. It's
 | ||||
|             // possible that during the window between when the weak-ref holding this proxy
 | ||||
|             // expires and when we enter remove() and take m_mutex, another thread could have
 | ||||
|             // created a new proxy for the same original object and added it to the map. In
 | ||||
|             // that case, `it->second` will contain a live pointer to a different proxy object,
 | ||||
|             // not an expired weak pointer to the Handle currently being destructed. We only
 | ||||
|             // remove the map entry if its pointer is already expired.
 | ||||
|             if (is_expired(it->second)) { | ||||
|                 m_mapping.erase(it); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     struct KeyHash { | ||||
|         std::size_t operator()(const Key & k) const { | ||||
|             return k.first.hash_code() ^ UnowningImplPointerHash{}(k.second); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     struct KeyEqual { | ||||
|         bool operator()(const Key & lhs, const Key & rhs) const { | ||||
|             return lhs.first == rhs.first | ||||
|                 && UnowningImplPointerEqual{}(lhs.second, rhs.second); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     std::unordered_map<Key, WeakProxyPointer, KeyHash, KeyEqual> m_mapping; | ||||
|     std::mutex m_mutex; | ||||
| 
 | ||||
|     // Only ProxyCache<Traits>::get_base() can allocate these objects.
 | ||||
|     Pimpl() = default; | ||||
|     friend class ProxyCache<Traits>; | ||||
| }; | ||||
| 
 | ||||
| template <typename Traits> | ||||
| void ProxyCache<Traits>::cleanup(const std::shared_ptr<Pimpl> & base, | ||||
|                                  const std::type_index & tag, | ||||
|                                  UnowningImplPointer ptr) { | ||||
|     base->remove(tag, ptr); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Magic-static singleton. | ||||
|  * | ||||
|  * It's possible for someone to hold Djinni-static objects in a global (like a shared_ptr | ||||
|  * at namespace scope), which can cause problems at static destruction time: if the proxy | ||||
|  * cache itself is destroyed before the other global, use-of-destroyed-object will result. | ||||
|  * To fix this, we make it possible to take a shared_ptr to the GenericProxyCache instance, | ||||
|  * so it will only be destroyed once all references are gone. | ||||
|  */ | ||||
| template <typename Traits> | ||||
| auto ProxyCache<Traits>::get_base() -> const std::shared_ptr<Pimpl> & { | ||||
|     static const std::shared_ptr<Pimpl> instance(new Pimpl); | ||||
|     // Return by const-ref. This is safe to call any time except during static destruction.
 | ||||
|     // Returning by reference lets us avoid touching the refcount unless needed.
 | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| template <typename Traits> | ||||
| auto ProxyCache<Traits>::get(const std::type_index & tag, | ||||
|                              const OwningImplPointer & impl, | ||||
|                              AllocatorFunction * alloc) | ||||
|         -> OwningProxyPointer { | ||||
|     return get_base()->get(tag, impl, alloc); | ||||
| } | ||||
| 
 | ||||
| } // namespace djinni
 | ||||
							
								
								
									
										185
									
								
								wrappers/android/mynteye/third_party/djinni/support-lib/proxy_cache_interface.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								wrappers/android/mynteye/third_party/djinni/support-lib/proxy_cache_interface.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,185 @@ | ||||
| //
 | ||||
| // Copyright 2015 Dropbox, Inc.
 | ||||
| //
 | ||||
| // 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 <memory> | ||||
| #include <functional> | ||||
| #include <typeindex> | ||||
| 
 | ||||
| namespace djinni { | ||||
| 
 | ||||
| /*
 | ||||
|  * The template parameters we receive here can be a number of different types: C++ smart | ||||
|  * pointers, custom wrappers, or language-specific types (like ObjC's `id` / `__weak id`). | ||||
|  * If custom wrapper types are used, like the `JavaWeakRef` type in the JNI library, then | ||||
|  * they must implement `.get()` or `.lock()` by analogy with C++'s smart pointers. | ||||
|  * | ||||
|  * We assume that built-in types are pointer-compatible. This is the case with ObjC: for | ||||
|  * example, __weak id pointers can be implicitly converted to __strong id, and so on. | ||||
|  * | ||||
|  * (The helper for .lock() is only used by proxy_cache_impl.hpp, so it's defined there.) | ||||
|  */ | ||||
| template <typename T> static inline auto get_unowning(const T & ptr) -> decltype(ptr.get()) { | ||||
|     return ptr.get(); | ||||
| } | ||||
| template <typename T> static inline T * get_unowning(T * ptr) { return ptr; } | ||||
| 
 | ||||
| /*
 | ||||
|  * ProxyCache provides a mechanism for re-using proxy objects generated in one language | ||||
|  * that wrap around implementations in a different language. This is for correctness, not | ||||
|  * just performance: when we pass the same object across a language boundary twice, we want | ||||
|  * to get the same proxy object on the other side each time, so that identity semantics | ||||
|  * behave as expected. | ||||
|  * | ||||
|  * ProxyCache is instantiated with a Traits class that must contain the following typedefs. | ||||
|  * Examples refer to the use of ProxyCache to cache C++ wrappers around ObjC objects. | ||||
|  * ProxyCache itself is generic (type-erased), though Handle is not, so we use e.g. `id` | ||||
|  * and `shared_ptr<void>` rather than any specific types. | ||||
|  * | ||||
|  * - UnowningImplPointer: | ||||
|  *       a non-owning pointer to an object being wrapped, e.g. __unsafe_unretained id | ||||
|  * - OwningImplPointer: | ||||
|  *       a strong owning pointer to an object being wrapped, e.g. __strong id | ||||
|  * - OwningProxyPointer: | ||||
|  *       a strong owning pointer to a wrapper, e.g. std::shared_ptr<void> | ||||
|  * - WeakProxyPointer: | ||||
|  *       a safe weak pointer to a wrapper, e.g. std::weak_ptr<void> | ||||
|  * - UnowningImplPointerHash: | ||||
|  *       a hasher for UnowningImplPointer, usually std::hash<UnowningImplPointer>, unless | ||||
|  *       std::hash doesn't work with UnowningImplPointer in which case a custom type can be | ||||
|  *       provided. | ||||
|  * - UnowningImplPointerEqual: | ||||
|  *       an equality predicate for UnowningImplPointer, like std::equal_to<UnowningImplPointer>. | ||||
|  *       In some cases (e.g. Java) a custom equality predicate may be needed. | ||||
|  * | ||||
|  * Generally, ProxyCache will be explicitly instantiated in one source file with C++11's | ||||
|  * `extern template` mechanism. The WeakProxyPointer, UnowningImplPointerHash, and | ||||
|  * UnowningImplPointerEqual types can be incomplete except for where the explicit | ||||
|  * instantiation is actually defined. | ||||
|  * | ||||
|  * Here's an overview of the structure: | ||||
|  * | ||||
|  *                           ______________  std::pair<ImplType, | ||||
|  *          WeakProxyPonter |              | UnowningImplPointer> | ||||
|  *           - - - - - - - -|  ProxyCache  |- - - - - - - - - - | ||||
|  *          |               |              |                   | | ||||
|  *          |               |______________|                   | | ||||
|  *          |                                                  | | ||||
|  *      ____v____        ______________          ______________v__________ | ||||
|  *     |         |      |              |        |                         | | ||||
|  *     | (Proxy  | ===> | ProxyCache:: | =====> | (Impl object providing  | | ||||
|  *     | object) |  ^   |   Handle<T>  |   T    |  actual functionality)  | | ||||
|  *     |_________|  .   |______________|   ^    |_________________________| | ||||
|  *                  .                     . | ||||
|  *     ( can be member, base, )       ( T is a generally a specific   ) | ||||
|  *     ( or cross-language    )       ( owning type like id<Foo>,     ) | ||||
|  *     ( reference like jlong )       ( shared_ptr<Foo>, or GlobalRef ) | ||||
|  * | ||||
|  * The cache contains a map from pair<ImplType, UnowningImplPointer> | ||||
|  * to WeakProxyPointer, allowing it to answer the question: "given this | ||||
|  * impl, do we already have a proxy in existence?" | ||||
|  * | ||||
|  * We use one map for all translated types, rather than a separate one for each type, | ||||
|  * to minimize duplication of code and make it so the unordered_map is as contained as | ||||
|  * possible. | ||||
|  */ | ||||
| template <typename Traits> | ||||
| class ProxyCache { | ||||
| public: | ||||
|     using UnowningImplPointer = typename Traits::UnowningImplPointer; | ||||
|     using OwningImplPointer = typename Traits::OwningImplPointer; | ||||
|     using OwningProxyPointer = typename Traits::OwningProxyPointer; | ||||
|     using WeakProxyPointer = typename Traits::WeakProxyPointer; | ||||
|     using UnowningImplPointerHash = typename Traits::UnowningImplPointerHash; | ||||
|     using UnowningImplPointerEqual = typename Traits::UnowningImplPointerEqual; | ||||
|     class Pimpl; | ||||
| 
 | ||||
|     /*
 | ||||
|      * Each proxy object must own a Handle. The Handle carries a strong reference to whatever | ||||
|      * the proxy wraps. When `ProxyCache::get()` creates a proxy, it also adds the proxy to | ||||
|      * the global proxy cache; Handle::~Handle() removes the reference from the cache. | ||||
|      * | ||||
|      * The Handle can be held by the proxy in any of a number of ways: as a C++ member or | ||||
|      * base, as an ObjC instance variable, or across an FFI boundary (a Java object might | ||||
|      * contain the address of a Handle as a `long` and delete it in the destructor.) | ||||
|      * | ||||
|      * T is generally a more-specialized version of OwningImplPointer. For example, when | ||||
|      * managing C++ proxies for ObjC objects, OwningImplPointer would be `id`, and the C++ | ||||
|      * proxy class `MyInterface` which wraps `@protocol DBMyInterface` would contain a | ||||
|      * `Handle<id<DBMyInterface>>`. | ||||
|      * | ||||
|      * TagType should be the same type that was passed in to `get()` when this handle was | ||||
|      * created. Normally this is the same as T (a specialized OwningImplPointer), but in | ||||
|      * cases like Java where all object types are uniformly represented as `jobject` in C++, | ||||
|      * another type may be used. | ||||
|      */ | ||||
|     template <typename T, typename TagType = T> | ||||
|     class Handle { | ||||
|     public: | ||||
|         template <typename... Args> Handle(Args &&... args) | ||||
|             : m_cache(get_base()), m_obj(std::forward<Args>(args)...) {} | ||||
|         Handle(const Handle &) = delete; | ||||
|         Handle & operator=(const Handle &) = delete; | ||||
|         ~Handle() { if (m_obj) cleanup(m_cache, typeid(TagType), get_unowning(m_obj)); } | ||||
| 
 | ||||
|         void assign(const T & obj) { m_obj = obj; } | ||||
| 
 | ||||
|         const T & get() const & noexcept { return m_obj; } | ||||
| 
 | ||||
|     private: | ||||
|         const std::shared_ptr<Pimpl> m_cache; | ||||
|         T m_obj; | ||||
|     }; | ||||
| 
 | ||||
|     /*
 | ||||
|      * Function typedef for helpers passed in to allocate new objects. | ||||
|      * To reduce code size, the proxy cache type-erases the objects inside it. | ||||
|      * | ||||
|      * An allocator function takes an OwningImplPointer to the source language object, | ||||
|      * and returns a newly-created proxy. | ||||
|      * | ||||
|      * In Java, an OwningImplPointer does not provide the same identity semantics as the | ||||
|      * underlying object. (A JNI 'jobject' can be one of a few different types of reference, | ||||
|      * and JNI is structured to allow copying GCs, so an object's address might change over | ||||
|      * time.) This is why ProxyCache takes hasher and comparator paramters. In particular, | ||||
|      * the OwningImplPointer passed into the allocator might be a JNI local reference. The | ||||
|      * allocator will create a GlobalRef to the impl object and store it in the returned proxy. | ||||
|      * | ||||
|      * Because we don't constrain how Handle objects are held, there's no generic way for the | ||||
|      * proxy cache to get the GlobalRef out of the returned proxy object, so AllocatorFunction | ||||
|      * returns a pair: the first element is the newly created proxy, and the second is an | ||||
|      * UnowningImplPointer that will be used as a key in the map. | ||||
|      */ | ||||
|     using AllocatorFunction = | ||||
|         std::pair<OwningProxyPointer, UnowningImplPointer>(const OwningImplPointer &); | ||||
| 
 | ||||
|     /*
 | ||||
|      * Return the existing proxy for `impl`, if any. If not, create one by calling `alloc`, | ||||
|      * store a weak reference to it in the proxy cache, and return it. | ||||
|      */ | ||||
|     static OwningProxyPointer get(const std::type_index &, | ||||
|                                   const OwningImplPointer & impl, | ||||
|                                   AllocatorFunction * alloc); | ||||
| 
 | ||||
| private: | ||||
|     static void cleanup(const std::shared_ptr<Pimpl> &, | ||||
|                         const std::type_index &, | ||||
|                         UnowningImplPointer); | ||||
|     static const std::shared_ptr<Pimpl> & get_base(); | ||||
| }; | ||||
| 
 | ||||
| } // namespace djinni
 | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user