1 // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2 
3 #pragma once
4 
5 #include <jsi/jsi.h>
6 #include <fbjni/fbjni.h>
7 #include <ReactCommon/CallInvoker.h>
8 
9 namespace jsi = facebook::jsi;
10 namespace jni = facebook::jni;
11 namespace react = facebook::react;
12 
13 namespace expo {
14 class JavaScriptValue;
15 
16 class JavaScriptObject;
17 
18 class JSIInteropModuleRegistry;
19 
20 /**
21  * Dummy CallInvoker that invokes everything immediately.
22  * Used in the test environment to check the async flow.
23  */
24 class SyncCallInvoker : public react::CallInvoker {
25 public:
26   void invokeAsync(std::function<void()> &&func) override;
27 
28   void invokeSync(std::function<void()> &&func) override;
29 
30   ~SyncCallInvoker() override = default;
31 };
32 
33 /**
34  * A wrapper for the jsi::Runtime.
35  * This class is used as a bridge between CPP and Kotlin and to encapsulate common runtime helper functions.
36  *
37  * Instances of this class should be managed using a shared smart pointer.
38  * To pass runtime information to all of `JavaScriptValue` and `JavaScriptObject` we use `weak_from_this()`
39  * that requires that the object is held via a smart pointer. Otherwise, `weak_from_this()` returns `nullptr`.
40  */
41 class JavaScriptRuntime : public std::enable_shared_from_this<JavaScriptRuntime> {
42 public:
43   /**
44    * Initializes a runtime that is independent from React Native and its runtime initialization.
45    * This flow is mostly intended for tests. The JS call invoker is set to `SyncCallInvoker`.
46    */
47   JavaScriptRuntime(
48     JSIInteropModuleRegistry *jsiInteropModuleRegistry
49   );
50 
51   JavaScriptRuntime(
52     JSIInteropModuleRegistry *jsiInteropModuleRegistry,
53     jsi::Runtime *runtime,
54     std::shared_ptr<react::CallInvoker> jsInvoker,
55     std::shared_ptr<react::CallInvoker> nativeInvoker
56   );
57 
58   /**
59    * Returns the underlying runtime object.
60    */
61   jsi::Runtime &get() const;
62 
63   /**
64    * Evaluates given JavaScript source code.
65    * @throws if the input format is unknown, or evaluation causes an error,
66    * a jni::JniException<JavaScriptEvaluateException> will be thrown.
67    */
68   jni::local_ref<jni::HybridClass<JavaScriptValue>::javaobject> evaluateScript(
69     const std::string &script
70   );
71 
72   /**
73    * Returns the runtime global object for use in Kotlin.
74    */
75   jni::local_ref<jni::HybridClass<JavaScriptObject>::javaobject> global();
76 
77   /**
78    * Creates a new object for use in Kotlin.
79    */
80   jni::local_ref<jni::HybridClass<JavaScriptObject>::javaobject> createObject();
81 
82   /**
83    * Drains the JavaScript VM internal Microtask (a.k.a. event loop) queue.
84    */
85   void drainJSEventLoop();
86 
87   std::shared_ptr<react::CallInvoker> jsInvoker;
88   std::shared_ptr<react::CallInvoker> nativeInvoker;
89 
90   std::shared_ptr<jsi::Object> getMainObject();
91 
92   JSIInteropModuleRegistry *getModuleRegistry();
93 private:
94   std::shared_ptr<jsi::Runtime> runtime;
95   std::shared_ptr<jsi::Object> mainObject;
96   JSIInteropModuleRegistry *jsiInteropModuleRegistry;
97 
98   void installMainObject();
99 };
100 } // namespace expo
101