33
44#include < jni.h>
55
6+ #include < cstddef>
7+
68#include " app/src/include/firebase/internal/type_traits.h"
79#include " firestore/src/jni/jni_fwd.h"
810
@@ -36,7 +38,8 @@ template <> struct IsReference<jobjectArray> : public true_type {};
3638// MARK: Type mapping
3739
3840// A compile-time map from C++ types to their JNI equivalents.
39- template <typename T> struct JniTypeMap {};
41+ template <typename T> struct JniTypeMap { using type = jobject; };
42+
4043template <> struct JniTypeMap <bool > { using type = jboolean; };
4144template <> struct JniTypeMap <uint8_t > { using type = jbyte; };
4245template <> struct JniTypeMap <uint16_t > { using type = jchar; };
@@ -47,12 +50,16 @@ template <> struct JniTypeMap<float> { using type = jfloat; };
4750template <> struct JniTypeMap <double > { using type = jdouble; };
4851template <> struct JniTypeMap <size_t > { using type = jsize; };
4952
50- template <> struct JniTypeMap <jobject> { using type = jobject; };
51- template <> struct JniTypeMap <jstring> { using type = jstring; };
52-
5353template <> struct JniTypeMap <Object> { using type = jobject; };
5454template <> struct JniTypeMap <String> { using type = jstring; };
5555
56+ template <typename T> struct JniTypeMap <Local<T>> {
57+ using type = typename JniTypeMap<T>::type;
58+ };
59+ template <typename T> struct JniTypeMap <Global<T>> {
60+ using type = typename JniTypeMap<T>::type;
61+ };
62+
5663template <typename T>
5764using JniType = typename JniTypeMap<decay_t <T>>::type;
5865
@@ -70,26 +77,83 @@ using EnableForReference =
7077
7178// MARK: Type converters
7279
73- // Converts C++ primitives to their equivalent JNI primitive types by casting.
74- template <typename T>
75- EnableForPrimitive<T, JniType<T>> ToJni (const T& value) {
80+ namespace internal {
81+
82+ /* *
83+ * An explicit ordering for overload resolution of JNI conversions. This allows
84+ * SFINAE without needing to make all the enable_if cases mutually exclusive.
85+ *
86+ * When finding a JNI converter, we try the following, in order:
87+ * * pass through, for JNI primitive types;
88+ * * static casts, for C++ primitive types;
89+ * * pass through, for JNI reference types like jobject;
90+ * * unwrapping, for JNI reference wrapper types like `Object` or
91+ * `Local<String>`.
92+ *
93+ * `ConverterChoice` is a recursive type, defined such that `ConverterChoice<0>`
94+ * is the most derived type, `ConverterChoice<1>` and beyond are progressively
95+ * less derived. This causes the compiler to prioritize the overloads with
96+ * lower-numbered `ConverterChoice`s first, allowing compilation to succeed even
97+ * if multiple unqualified overloads would match, and would otherwise fail due
98+ * to an ambiguity.
99+ */
100+ template <int I>
101+ struct ConverterChoice : public ConverterChoice <I + 1 > {};
102+
103+ template <>
104+ struct ConverterChoice <3 > {};
105+
106+ /* *
107+ * Converts JNI primitive types to themselves.
108+ */
109+ template <typename T,
110+ typename = typename enable_if<IsPrimitive<T>::value>::type>
111+ T RankedToJni (T value, ConverterChoice<0 >) {
112+ return value;
113+ }
114+
115+ /* *
116+ * Converts C++ primitive types to their equivalent JNI primitive types by
117+ * casting.
118+ */
119+ template <typename T,
120+ typename = typename enable_if<IsPrimitive<JniType<T>>::value>::type>
121+ JniType<T> RankedToJni (T value, ConverterChoice<1 >) {
76122 return static_cast <JniType<T>>(value);
77123}
78124
79- // Converts JNI wrapper reference types (like `const Object&`) and any ownership
80- // wrappers of those types to their underlying `jobject`-derived reference.
81- template <typename T>
82- EnableForReference<T, JniType<T>> ToJni (const T& value) {
83- return value.get ();
125+ /* *
126+ * Converts direct use of a JNI reference types to themselves.
127+ */
128+ template <typename T,
129+ typename = typename enable_if<IsReference<T>::value>::type>
130+ T RankedToJni (T value, ConverterChoice<2 >) {
131+ return value;
84132}
85- template <typename T, typename J = typename T::jni_type>
86- J ToJni (const T& value) {
133+
134+ #if defined(_STLPORT_VERSION)
135+ using nullptr_t = decltype (nullptr );
136+ #else
137+ using nullptr_t = std::nullptr_t ;
138+ #endif
139+
140+ inline jobject RankedToJni (nullptr_t , ConverterChoice<2 >) { return nullptr ; }
141+
142+ /* *
143+ * Converts wrapper types to JNI references by unwrapping them.
144+ */
145+ template <typename T, typename J = JniType<T>>
146+ J RankedToJni (const T& value, ConverterChoice<3 >) {
87147 return value.get ();
88148}
89149
90- // Preexisting JNI types can be passed directly. This makes incremental
91- // migration possible. Ideally this could eventually be removed.
92- inline jobject ToJni (jobject value) { return value; }
150+ } // namespace internal
151+
152+ template <typename T>
153+ auto ToJni (const T& value)
154+ -> decltype(internal::RankedToJni(value, internal::ConverterChoice<0 >{})) {
155+ return internal::RankedToJni (value, internal::ConverterChoice<0 >{});
156+ }
93157
94158} // namespace jni
95159} // namespace firestore
0 commit comments