3
3
4
4
#include < jni.h>
5
5
6
+ #include < cstddef>
7
+
6
8
#include " app/src/include/firebase/internal/type_traits.h"
7
9
#include " firestore/src/jni/jni_fwd.h"
8
10
@@ -36,7 +38,8 @@ template <> struct IsReference<jobjectArray> : public true_type {};
36
38
// MARK: Type mapping
37
39
38
40
// 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
+
40
43
template <> struct JniTypeMap <bool > { using type = jboolean; };
41
44
template <> struct JniTypeMap <uint8_t > { using type = jbyte; };
42
45
template <> struct JniTypeMap <uint16_t > { using type = jchar; };
@@ -47,12 +50,16 @@ template <> struct JniTypeMap<float> { using type = jfloat; };
47
50
template <> struct JniTypeMap <double > { using type = jdouble; };
48
51
template <> struct JniTypeMap <size_t > { using type = jsize; };
49
52
50
- template <> struct JniTypeMap <jobject> { using type = jobject; };
51
- template <> struct JniTypeMap <jstring> { using type = jstring; };
52
-
53
53
template <> struct JniTypeMap <Object> { using type = jobject; };
54
54
template <> struct JniTypeMap <String> { using type = jstring; };
55
55
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
+
56
63
template <typename T>
57
64
using JniType = typename JniTypeMap<decay_t <T>>::type;
58
65
@@ -70,26 +77,83 @@ using EnableForReference =
70
77
71
78
// MARK: Type converters
72
79
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 >) {
76
122
return static_cast <JniType<T>>(value);
77
123
}
78
124
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;
84
132
}
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 >) {
87
147
return value.get ();
88
148
}
89
149
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
+ }
93
157
94
158
} // namespace jni
95
159
} // namespace firestore
0 commit comments