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