• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef JNI_ZERO_DEFAULT_CONVERSIONS_H_
6 #define JNI_ZERO_DEFAULT_CONVERSIONS_H_
7 
8 #include <optional>
9 #include <type_traits>
10 #include <vector>
11 
12 #include "third_party/jni_zero/common_apis.h"
13 #include "third_party/jni_zero/jni_zero.h"
14 
15 namespace jni_zero {
16 namespace internal {
17 template <typename T>
18 concept IsJavaRef = std::is_base_of_v<JavaRef<jobject>, T>;
19 
20 template <typename T>
requires(T t)21 concept HasReserve = requires(T t) { t.reserve(0); };
22 
23 template <typename T>
requires(T t,T::value_type v)24 concept HasPushBack = requires(T t, T::value_type v) { t.push_back(v); };
25 
26 template <typename T>
requires(T t,T::value_type v)27 concept HasInsert = requires(T t, T::value_type v) { t.insert(v); };
28 
29 template <typename T>
requires(T t)30 concept IsMap = requires(T t) {
31   typename T::key_type;
32   typename T::mapped_type;
33 };
34 
35 template <typename T>
requires(T t)36 concept IsContainer = requires(T t) {
37   !IsMap<T>;
38   typename T::value_type;
39   t.begin();
40   t.end();
41   t.size();
42 };
43 
44 template <typename T>
45 concept IsObjectContainer =
46     IsContainer<T> && !std::is_arithmetic_v<typename T::value_type>;
47 
48 template <typename T>
49 concept IsOptional = std::same_as<T, std::optional<typename T::value_type>>;
50 }  // namespace internal
51 
52 // Allow conversions using std::optional by wrapping non-optional conversions.
53 template <internal::IsOptional T>
FromJniType(JNIEnv * env,const JavaRef<jobject> & j_object)54 inline T FromJniType(JNIEnv* env, const JavaRef<jobject>& j_object) {
55   if (!j_object) {
56     return std::nullopt;
57   }
58   return FromJniType<typename T::value_type>(env, j_object);
59 }
60 
61 template <internal::IsOptional T>
ToJniType(JNIEnv * env,const T & opt_value)62 inline ScopedJavaLocalRef<jobject> ToJniType(JNIEnv* env, const T& opt_value) {
63   if (!opt_value) {
64     return nullptr;
65   }
66   return ToJniType(env, opt_value.value());
67 }
68 
69 // Convert Java array -> container type using FromJniType() on each element.
70 template <internal::IsObjectContainer ContainerType>
FromJniArray(JNIEnv * env,const JavaRef<jobject> & j_object)71 inline ContainerType FromJniArray(JNIEnv* env,
72                                   const JavaRef<jobject>& j_object) {
73   jobjectArray j_array = static_cast<jobjectArray>(j_object.obj());
74   using ElementType = std::remove_const_t<typename ContainerType::value_type>;
75   constexpr bool has_push_back = internal::HasPushBack<ContainerType>;
76   constexpr bool has_insert = internal::HasInsert<ContainerType>;
77   static_assert(has_push_back || has_insert, "Template type not supported.");
78   jsize array_jsize = env->GetArrayLength(j_array);
79 
80   ContainerType ret;
81   if constexpr (internal::HasReserve<ContainerType>) {
82     size_t array_size = static_cast<size_t>(array_jsize);
83     ret.reserve(array_size);
84   }
85   for (jsize i = 0; i < array_jsize; ++i) {
86     jobject j_element = env->GetObjectArrayElement(j_array, i);
87     // Do not call FromJni for jobject->jobject.
88     if constexpr (std::is_base_of_v<JavaRef<jobject>, ElementType>) {
89       if constexpr (has_push_back) {
90         ret.emplace_back(env, j_element);
91       } else if constexpr (has_insert) {
92         ret.emplace(env, j_element);
93       }
94     } else {
95       auto element = ScopedJavaLocalRef<jobject>::Adopt(env, j_element);
96       if constexpr (has_push_back) {
97         ret.push_back(FromJniType<ElementType>(env, element));
98       } else if constexpr (has_insert) {
99         ret.insert(FromJniType<ElementType>(env, element));
100       }
101     }
102   }
103   return ret;
104 }
105 
106 // Convert container type -> Java array using ToJniType() on each element.
107 template <internal::IsObjectContainer ContainerType>
108 inline ScopedJavaLocalRef<jobjectArray>
ToJniArray(JNIEnv * env,const ContainerType & collection,jclass clazz)109 ToJniArray(JNIEnv* env, const ContainerType& collection, jclass clazz) {
110   using ElementType = std::remove_const_t<typename ContainerType::value_type>;
111   size_t array_size = collection.size();
112   jsize array_jsize = static_cast<jsize>(array_size);
113   jobjectArray j_array = env->NewObjectArray(array_jsize, clazz, nullptr);
114   CheckException(env);
115 
116   jsize i = 0;
117   for (auto& value : collection) {
118     // Do not call ToJni for jobject->jobject.
119     if constexpr (std::is_base_of_v<JavaRef<jobject>, ElementType>) {
120       env->SetObjectArrayElement(j_array, i, value.obj());
121     } else {
122       ScopedJavaLocalRef<jobject> element = ToJniType(env, value);
123       env->SetObjectArrayElement(j_array, i, element.obj());
124     }
125     ++i;
126   }
127   return ScopedJavaLocalRef<jobjectArray>(env, j_array);
128 }
129 
130 #define DECLARE_PRIMITIVE_ARRAY_CONVERSIONS(T)                                 \
131   template <>                                                                  \
132   JNI_ZERO_COMPONENT_BUILD_EXPORT std::vector<T> FromJniArray<std::vector<T>>( \
133       JNIEnv * env, const JavaRef<jobject>& j_object);                         \
134   template <>                                                                  \
135   JNI_ZERO_COMPONENT_BUILD_EXPORT ScopedJavaLocalRef<jarray>                   \
136   ToJniArray<std::vector<T>>(JNIEnv * env, const std::vector<T>& vec);
137 
138 DECLARE_PRIMITIVE_ARRAY_CONVERSIONS(int64_t)
DECLARE_PRIMITIVE_ARRAY_CONVERSIONS(int32_t)139 DECLARE_PRIMITIVE_ARRAY_CONVERSIONS(int32_t)
140 DECLARE_PRIMITIVE_ARRAY_CONVERSIONS(int16_t)
141 DECLARE_PRIMITIVE_ARRAY_CONVERSIONS(uint16_t)
142 DECLARE_PRIMITIVE_ARRAY_CONVERSIONS(uint8_t)
143 DECLARE_PRIMITIVE_ARRAY_CONVERSIONS(bool)
144 DECLARE_PRIMITIVE_ARRAY_CONVERSIONS(float)
145 DECLARE_PRIMITIVE_ARRAY_CONVERSIONS(double)
146 
147 #undef DECLARE_PRIMITIVE_ARRAY_CONVERSIONS
148 
149 // Specialization for ByteArrayView.
150 template <>
151 inline ByteArrayView FromJniArray<ByteArrayView>(
152     JNIEnv* env,
153     const JavaRef<jobject>& j_object) {
154   jbyteArray j_array = static_cast<jbyteArray>(j_object.obj());
155   return ByteArrayView(env, j_array);
156 }
157 
158 // There is a circular dependency between common_apis.cc and here.
159 JNI_ZERO_COMPONENT_BUILD_EXPORT ScopedJavaLocalRef<jobjectArray>
160 CollectionToArray(JNIEnv* env, const JavaRef<jobject>& collection);
161 
162 JNI_ZERO_COMPONENT_BUILD_EXPORT ScopedJavaLocalRef<jobject> ArrayToList(
163     JNIEnv* env,
164     const JavaRef<jobjectArray>& array);
165 
166 template <internal::IsObjectContainer ContainerType>
FromJniCollection(JNIEnv * env,const JavaRef<jobject> & j_collection)167 inline ContainerType FromJniCollection(JNIEnv* env,
168                                        const JavaRef<jobject>& j_collection) {
169   ScopedJavaLocalRef<jobjectArray> arr = CollectionToArray(env, j_collection);
170   return FromJniArray<ContainerType>(env, arr);
171 }
172 
173 // Convert container type -> Java array using ToJniType() on each element.
174 template <internal::IsObjectContainer ContainerType>
ToJniList(JNIEnv * env,const ContainerType & collection)175 inline ScopedJavaLocalRef<jobject> ToJniList(JNIEnv* env,
176                                              const ContainerType& collection) {
177   ScopedJavaLocalRef<jobjectArray> arr =
178       ToJniArray(env, collection, g_object_class);
179   return ArrayToList(env, arr);
180 }
181 
182 JNI_ZERO_COMPONENT_BUILD_EXPORT ScopedJavaLocalRef<jobjectArray> MapToArray(
183     JNIEnv* env,
184     const JavaRef<jobject>& map);
185 JNI_ZERO_COMPONENT_BUILD_EXPORT ScopedJavaLocalRef<jobject> ArrayToMap(
186     JNIEnv* env,
187     const JavaRef<jobjectArray>& array);
188 
189 // Convert Map -> stl map type using FromJniType() on each key & value.
190 template <internal::IsMap ContainerType>
FromJniType(JNIEnv * env,const JavaRef<jobject> & j_object)191 inline ContainerType FromJniType(JNIEnv* env,
192                                  const JavaRef<jobject>& j_object) {
193   using KeyType = ContainerType::key_type;
194   using ValueType = ContainerType::mapped_type;
195 
196   ScopedJavaLocalRef<jobjectArray> j_array = MapToArray(env, j_object);
197   jsize array_jsize = env->GetArrayLength(j_array.obj());
198 
199   ContainerType ret;
200   if constexpr (internal::HasReserve<ContainerType>) {
201     size_t array_size = static_cast<size_t>(array_jsize);
202     ret.reserve(array_size / 2);
203   }
204   for (jsize i = 0; i < array_jsize; i += 2) {
205     // No need to call CheckException() since we know the array is of the
206     // correct size, since we are the ones who created it.
207     jobject j_key = env->GetObjectArrayElement(j_array.obj(), i);
208     jobject j_value = env->GetObjectArrayElement(j_array.obj(), i + 1);
209     // Do not call FromJni for jobject->jobject.
210     if constexpr (internal::IsJavaRef<KeyType> &&
211                   internal::IsJavaRef<ValueType>) {
212       ret.emplace(std::piecewise_construct, std::forward_as_tuple(env, j_key),
213                   std::forward_as_tuple(env, j_value));
214     } else if constexpr (internal::IsJavaRef<KeyType>) {
215       auto value = ScopedJavaLocalRef<jobject>::Adopt(env, j_value);
216       ret.emplace(std::piecewise_construct, std::forward_as_tuple(env, j_key),
217                   FromJniType<ValueType>(env, value));
218     } else if constexpr (internal::IsJavaRef<ValueType>) {
219       auto key = ScopedJavaLocalRef<jobject>::Adopt(env, j_key);
220       ret.emplace(std::piecewise_construct, FromJniType<KeyType>(env, key),
221                   std::forward_as_tuple(env, j_value));
222     } else {
223       auto key = ScopedJavaLocalRef<jobject>::Adopt(env, j_key);
224       auto value = ScopedJavaLocalRef<jobject>::Adopt(env, j_value);
225       ret.emplace(FromJniType<KeyType>(env, key),
226                   FromJniType<ValueType>(env, value));
227     }
228   }
229   return ret;
230 }
231 
232 // Convert stl map -> Map type using ToJniType() on each key & value.
233 template <internal::IsMap ContainerType>
ToJniType(JNIEnv * env,const ContainerType & map)234 inline ScopedJavaLocalRef<jobject> ToJniType(JNIEnv* env,
235                                              const ContainerType& map) {
236   using KeyType = ContainerType::key_type;
237   using ValueType = ContainerType::mapped_type;
238   jsize map_jsize = static_cast<jsize>(map.size());
239   jobjectArray j_array =
240       env->NewObjectArray(map_jsize * 2, g_object_class, nullptr);
241   CheckException(env);
242 
243   jsize i = 0;
244   for (auto const& [key, value] : map) {
245     // Do not call ToJni for jobject->jobject.
246     if constexpr (internal::IsJavaRef<KeyType>) {
247       env->SetObjectArrayElement(j_array, i, key.obj());
248     } else {
249       ScopedJavaLocalRef<jobject> j_key = ToJniType(env, key);
250       env->SetObjectArrayElement(j_array, i, j_key.obj());
251     }
252     ++i;
253 
254     if constexpr (internal::IsJavaRef<ValueType>) {
255       env->SetObjectArrayElement(j_array, i, value.obj());
256     } else {
257       ScopedJavaLocalRef<jobject> j_value = ToJniType(env, value);
258       env->SetObjectArrayElement(j_array, i, j_value.obj());
259     }
260     ++i;
261   }
262   auto array = ScopedJavaLocalRef<jobjectArray>::Adopt(env, j_array);
263   return ArrayToMap(env, array);
264 }
265 }  // namespace jni_zero
266 #endif  // JNI_ZERO_DEFAULT_CONVERSIONS_H_
267