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 
17 class JavaScriptValue;
18 
19 class JavaScriptObject;
20 
21 class JSIInteropModuleRegistry;
22 
23 #if REACT_NATIVE_TARGET_VERSION >= 73
24 using NativeMethodCallInvokerCompatible = react::NativeMethodCallInvoker;
25 #else
26 using NativeMethodCallInvokerCompatible = react::CallInvoker;
27 #endif
28 
29 /**
30  * A wrapper for the jsi::Runtime.
31  * This class is used as a bridge between CPP and Kotlin and to encapsulate common runtime helper functions.
32  *
33  * Instances of this class should be managed using a shared smart pointer.
34  * To pass runtime information to all of `JavaScriptValue` and `JavaScriptObject` we use `weak_from_this()`
35  * that requires that the object is held via a smart pointer. Otherwise, `weak_from_this()` returns `nullptr`.
36  */
37 class JavaScriptRuntime : public std::enable_shared_from_this<JavaScriptRuntime> {
38 public:
39   /**
40    * Initializes a runtime that is independent from React Native and its runtime initialization.
41    * This flow is mostly intended for tests. The JS call invoker is set to `SyncCallInvoker`.
42    * See **JavaScriptRuntime.cpp** for the `SyncCallInvoker` implementation.
43    */
44   JavaScriptRuntime(
45     JSIInteropModuleRegistry *jsiInteropModuleRegistry
46   );
47 
48   JavaScriptRuntime(
49     JSIInteropModuleRegistry *jsiInteropModuleRegistry,
50     jsi::Runtime *runtime,
51     std::shared_ptr<react::CallInvoker> jsInvoker,
52     std::shared_ptr<NativeMethodCallInvokerCompatible> nativeInvoker
53   );
54 
55   /**
56    * Returns the underlying runtime object.
57    */
58   jsi::Runtime &get() const;
59 
60   /**
61    * Evaluates given JavaScript source code.
62    * @throws if the input format is unknown, or evaluation causes an error,
63    * a jni::JniException<JavaScriptEvaluateException> will be thrown.
64    */
65   jni::local_ref<jni::HybridClass<JavaScriptValue, Destructible>::javaobject> evaluateScript(
66     const std::string &script
67   );
68 
69   /**
70    * Returns the runtime global object for use in Kotlin.
71    */
72   jni::local_ref<jni::HybridClass<JavaScriptObject, Destructible>::javaobject> global();
73 
74   /**
75    * Creates a new object for use in Kotlin.
76    */
77   jni::local_ref<jni::HybridClass<JavaScriptObject, Destructible>::javaobject> createObject();
78 
79   /**
80    * Drains the JavaScript VM internal Microtask (a.k.a. event loop) queue.
81    */
82   void drainJSEventLoop();
83 
84   void installMainObject();
85 
86   std::shared_ptr<react::CallInvoker> jsInvoker;
87   std::shared_ptr<NativeMethodCallInvokerCompatible> nativeInvoker;
88 
89   std::shared_ptr<jsi::Object> getMainObject();
90 
91   JSIInteropModuleRegistry *getModuleRegistry();
92 
93 private:
94   std::shared_ptr<jsi::Runtime> runtime;
95   std::shared_ptr<jsi::Object> mainObject;
96   JSIInteropModuleRegistry *jsiInteropModuleRegistry;
97 };
98 } // namespace expo
99