1 // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2 
3 #pragma once
4 
5 #include "CppType.h"
6 
7 #include <jsi/jsi.h>
8 #include <fbjni/fbjni.h>
9 
10 namespace jni = facebook::jni;
11 namespace jsi = facebook::jsi;
12 
13 namespace expo {
14 class JSIInteropModuleRegistry;
15 class SingleType;
16 
17 /**
18  * A base interface for all frontend converter classes - converters that cast jsi values into JNI objects.
19  * Right now, we have two-step arguments conversion. Firstly, we unwrapped the JSI value into selected JNI objects (see CppType).
20  * Then, we do a more sophisticated conversion like creating records or mapping into enums.
21  * The second step lives in the Kotlin codebase.
22  */
23 class FrontendConverter {
24 public:
25   virtual ~FrontendConverter() = default;
26 
27   /**
28    * Checks if the provided value can be converted.
29    */
30   virtual bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const = 0;
31 
32   /**
33    * Converts the provided value.
34    */
35   virtual jobject convert(
36     jsi::Runtime &rt,
37     JNIEnv *env,
38     JSIInteropModuleRegistry *moduleRegistry,
39     const jsi::Value &value
40   ) const = 0;
41 };
42 
43 /**
44  * Converter from js number to [java.lang.Integer].
45  */
46 class IntegerFrontendConverter : public FrontendConverter {
47 public:
48   jobject convert(
49     jsi::Runtime &rt,
50     JNIEnv *env,
51     JSIInteropModuleRegistry *moduleRegistry,
52     const jsi::Value &value
53   ) const override;
54 
55   bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
56 };
57 
58 /**
59  * Converter from js number to [java.lang.Long].
60  */
61 class LongFrontendConverter : public FrontendConverter {
62 public:
63   jobject convert(
64     jsi::Runtime &rt,
65     JNIEnv *env,
66     JSIInteropModuleRegistry *moduleRegistry,
67     const jsi::Value &value
68   ) const override;
69 
70   bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
71 };
72 
73 /**
74  * Converter from js number to [java.lang.Float].
75  */
76 class FloatFrontendConverter : public FrontendConverter {
77 public:
78   jobject convert(
79     jsi::Runtime &rt,
80     JNIEnv *env,
81     JSIInteropModuleRegistry *moduleRegistry,
82     const jsi::Value &value
83   ) const override;
84 
85   bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
86 };
87 
88 /**
89  * Converter from js bool to [java.lang.Boolean].
90  */
91 class BooleanFrontendConverter : public FrontendConverter {
92 public:
93   jobject convert(
94     jsi::Runtime &rt,
95     JNIEnv *env,
96     JSIInteropModuleRegistry *moduleRegistry,
97     const jsi::Value &value
98   ) const override;
99 
100   bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
101 };
102 
103 /**
104  * Converter from js number to [java.lang.Double].
105  */
106 class DoubleFrontendConverter : public FrontendConverter {
107 public:
108   jobject convert(
109     jsi::Runtime &rt,
110     JNIEnv *env,
111     JSIInteropModuleRegistry *moduleRegistry,
112     const jsi::Value &value
113   ) const override;
114 
115   bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
116 };
117 
118 /**
119  * Converter from js string to [java.lang.String].
120  */
121 class StringFrontendConverter : public FrontendConverter {
122 public:
123   jobject convert(
124     jsi::Runtime &rt,
125     JNIEnv *env,
126     JSIInteropModuleRegistry *moduleRegistry,
127     const jsi::Value &value
128   ) const override;
129 
130   bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
131 };
132 
133 /**
134  * Converter from js array to [com.facebook.react.bridge.ReadableNativeArray].
135  */
136 class ReadableNativeArrayFrontendConverter : public FrontendConverter {
137 public:
138   jobject convert(
139     jsi::Runtime &rt,
140     JNIEnv *env,
141     JSIInteropModuleRegistry *moduleRegistry,
142     const jsi::Value &value
143   ) const override;
144 
145   bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
146 };
147 
148 /**
149  * Converter from js object to [com.facebook.react.bridge.ReadableNativeMap].
150  */
151 class ReadableNativeMapArrayFrontendConverter : public FrontendConverter {
152 public:
153   jobject convert(
154     jsi::Runtime &rt,
155     JNIEnv *env,
156     JSIInteropModuleRegistry *moduleRegistry,
157     const jsi::Value &value
158   ) const override;
159 
160   bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
161 };
162 
163 /**
164  * Converter from js type array to [expo.modules.kotlin.jni.JavaScriptTypedArray].
165  */
166 class TypedArrayFrontendConverter : public FrontendConverter {
167 public:
168   jobject convert(
169     jsi::Runtime &rt,
170     JNIEnv *env,
171     JSIInteropModuleRegistry *moduleRegistry,
172     const jsi::Value &value
173   ) const override;
174 
175   bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
176 };
177 
178 /**
179  * Converter from any js value to [expo.modules.kotlin.jni.JavaScriptValue].
180  */
181 class JavaScriptValueFrontendConverter : public FrontendConverter {
182 public:
183   jobject convert(
184     jsi::Runtime &rt,
185     JNIEnv *env,
186     JSIInteropModuleRegistry *moduleRegistry,
187     const jsi::Value &value
188   ) const override;
189 
190   bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
191 };
192 
193 /**
194  * Converter from js object to [expo.modules.kotlin.jni.JavaScriptObject].
195  */
196 class JavaScriptObjectFrontendConverter : public FrontendConverter {
197 public:
198   jobject convert(
199     jsi::Runtime &rt,
200     JNIEnv *env,
201     JSIInteropModuleRegistry *moduleRegistry,
202     const jsi::Value &value
203   ) const override;
204 
205   bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
206 };
207 
208 /**
209  * Converter from js function to [expo.modules.kotlin.jni.JavaScriptFunction].
210  */
211 class JavaScriptFunctionFrontendConverter : public FrontendConverter {
212 public:
213   jobject convert(
214     jsi::Runtime &rt,
215     JNIEnv *env,
216     JSIInteropModuleRegistry *moduleRegistry,
217     const jsi::Value &value
218   ) const override;
219 
220   bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
221 };
222 
223 /**
224  * Converter from js view object to int.
225  */
226 class ViewTagFrontendConverter : public FrontendConverter {
227 public:
228   jobject convert(
229     jsi::Runtime &rt,
230     JNIEnv *env,
231     JSIInteropModuleRegistry *moduleRegistry,
232     const jsi::Value &value
233   ) const override;
234 
235   bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
236 };
237 
238 /**
239  * Converter from js shared object to int.
240  */
241 class SharedObjectIdConverter : public FrontendConverter {
242 public:
243   jobject convert(
244     jsi::Runtime &rt,
245     JNIEnv *env,
246     JSIInteropModuleRegistry *moduleRegistry,
247     const jsi::Value &value
248   ) const override;
249 
250   bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
251 };
252 
253 /**
254  * Converter that always fails.
255  * Used to not fail when the function is created.
256  * TODO(@lukmccall): remove
257  */
258 class UnknownFrontendConverter : public FrontendConverter {
259 public:
260   jobject convert(
261     jsi::Runtime &rt,
262     JNIEnv *env,
263     JSIInteropModuleRegistry *moduleRegistry,
264     const jsi::Value &value
265   ) const override;
266 
267   bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
268 };
269 
270 /**
271  * Same types like enums can be represented by multiply js types.
272  * That's why we have a converter that can combine multiple converters into one.
273  *
274  * For instance, enum classes will be represented as a PolyFrontendConverter({StringFrontendConverter, IntegerFrontendConverter})
275  */
276 class PolyFrontendConverter : public FrontendConverter {
277 public:
278   PolyFrontendConverter(std::vector<std::shared_ptr<FrontendConverter>> converters);
279 
280   bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
281 
282   jobject convert(
283     jsi::Runtime &rt,
284     JNIEnv *env,
285     JSIInteropModuleRegistry *moduleRegistry,
286     const jsi::Value &value
287   ) const override;
288 
289 private:
290   std::vector<std::shared_ptr<FrontendConverter>> converters;
291 };
292 
293 /**
294  * Converter from js array object to Java primitive array.
295  */
296 class PrimitiveArrayFrontendConverter : public FrontendConverter {
297 public:
298   PrimitiveArrayFrontendConverter(
299     jni::local_ref<jni::JavaClass<SingleType>::javaobject> expectedType
300   );
301 
302   jobject convert(
303     jsi::Runtime &rt,
304     JNIEnv *env,
305     JSIInteropModuleRegistry *moduleRegistry,
306     const jsi::Value &value
307   ) const override;
308 
309   bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
310 
311 private:
312   /**
313    * A string representation of desired Java type.
314    */
315   std::string javaType;
316   /**
317    * Bare parameter type.
318    */
319   CppType parameterType;
320   /**
321    * Converter used to convert array elements.
322    */
323   std::shared_ptr<FrontendConverter> parameterConverter;
324 };
325 
326 /**
327  * Converter from js array object to [java.utils.ArrayList].
328  */
329 class ListFrontendConverter : public FrontendConverter {
330 public:
331   ListFrontendConverter(
332     jni::local_ref<jni::JavaClass<SingleType>::javaobject> expectedType
333   );
334 
335   jobject convert(
336     jsi::Runtime &rt,
337     JNIEnv *env,
338     JSIInteropModuleRegistry *moduleRegistry,
339     const jsi::Value &value
340   ) const override;
341 
342   bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
343 private:
344   /**
345    * Converter used to convert array elements.
346    */
347   std::shared_ptr<FrontendConverter> parameterConverter;
348 };
349 
350 /**
351  * Converter from js object to [java.utils.LinkedHashMap].
352  */
353 class MapFrontendConverter : public FrontendConverter {
354 public:
355   MapFrontendConverter(
356     jni::local_ref<jni::JavaClass<SingleType>::javaobject> expectedType
357   );
358 
359   jobject convert(
360     jsi::Runtime &rt,
361     JNIEnv *env,
362     JSIInteropModuleRegistry *moduleRegistry,
363     const jsi::Value &value
364   ) const override;
365 
366   bool canConvert(jsi::Runtime &rt, const jsi::Value &value) const override;
367 private:
368   /**
369    * Converter used to convert values.
370    */
371   std::shared_ptr<FrontendConverter> valueConverter;
372 };
373 } // namespace expo
374