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