1 // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2 
3 #include "JavaScriptRuntime.h"
4 #include "JavaScriptValue.h"
5 #include "JavaScriptObject.h"
6 #include "Exceptions.h"
7 
8 #if FOR_HERMES
9 
10 #include <hermes/hermes.h>
11 
12 #include <utility>
13 
14 #else
15 
16 #include <jsi/JSCRuntime.h>
17 
18 #endif
19 
20 namespace jsi = facebook::jsi;
21 
22 namespace expo {
23 
24 void SyncCallInvoker::invokeAsync(std::function<void()> &&func) {
25   func();
26 }
27 
28 void SyncCallInvoker::invokeSync(std::function<void()> &&func) {
29   func();
30 }
31 
32 JavaScriptRuntime::JavaScriptRuntime()
33   : jsInvoker(std::make_shared<SyncCallInvoker>()),
34     nativeInvoker(std::make_shared<SyncCallInvoker>()) {
35 #if FOR_HERMES
36   auto config = ::hermes::vm::RuntimeConfig::Builder()
37     .withEnableSampleProfiling(false);
38   runtime = facebook::hermes::makeHermesRuntime(config.build());
39 
40   // This version of the Hermes uses a Promise implementation that is provided by the RN.
41   // The `setImmediate` function isn't defined, but is required by the Promise implementation.
42   // That's why we inject it here.
43   auto setImmediatePropName = jsi::PropNameID::forUtf8(*runtime, "setImmediate");
44   runtime->global().setProperty(
45     *runtime,
46     setImmediatePropName,
47     jsi::Function::createFromHostFunction(
48       *runtime,
49       setImmediatePropName,
50       1,
51       [](jsi::Runtime &rt,
52          const jsi::Value &thisVal,
53          const jsi::Value *args,
54          size_t count) {
55         args[0].asObject(rt).asFunction(rt).call(rt);
56         return jsi::Value::undefined();
57       }
58     )
59   );
60 #else
61   runtime = facebook::jsc::makeJSCRuntime();
62 #endif
63 
64   // By default "global" property isn't set.
65   runtime->global().setProperty(
66     *runtime,
67     jsi::PropNameID::forUtf8(*runtime, "global"),
68     runtime->global()
69   );
70 }
71 
72 JavaScriptRuntime::JavaScriptRuntime(
73   jsi::Runtime *runtime,
74   std::shared_ptr<react::CallInvoker> jsInvoker,
75   std::shared_ptr<react::CallInvoker> nativeInvoker
76 ) : jsInvoker(std::move(jsInvoker)), nativeInvoker(std::move(nativeInvoker)) {
77   // Creating a shared pointer that points to the runtime but doesn't own it, thus doesn't release it.
78   // In this code flow, the runtime should be owned by something else like the CatalystInstance.
79   // See explanation for constructor (8): https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr
80   this->runtime = std::shared_ptr<jsi::Runtime>(std::shared_ptr<jsi::Runtime>(), runtime);
81 }
82 
83 jsi::Runtime *JavaScriptRuntime::get() {
84   return runtime.get();
85 }
86 
87 jni::local_ref<JavaScriptValue::javaobject>
88 JavaScriptRuntime::evaluateScript(const std::string &script) {
89   auto scriptBuffer = std::make_shared<jsi::StringBuffer>(script);
90   try {
91     return JavaScriptValue::newObjectCxxArgs(
92       weak_from_this(),
93       std::make_shared<jsi::Value>(runtime->evaluateJavaScript(scriptBuffer, "<<evaluated>>"))
94     );
95   } catch (const jsi::JSError &error) {
96     jni::throwNewJavaException(
97       JavaScriptEvaluateException::create(
98         error.getMessage(),
99         error.getStack()
100       ).get()
101     );
102   } catch (const jsi::JSIException &error) {
103     jni::throwNewJavaException(
104       JavaScriptEvaluateException::create(
105         error.what(),
106         ""
107       ).get()
108     );
109   }
110 }
111 
112 jni::local_ref<JavaScriptObject::javaobject> JavaScriptRuntime::global() {
113   auto global = std::make_shared<jsi::Object>(runtime->global());
114   return JavaScriptObject::newObjectCxxArgs(weak_from_this(), global);
115 }
116 
117 jni::local_ref<JavaScriptObject::javaobject> JavaScriptRuntime::createObject() {
118   auto newObject = std::make_shared<jsi::Object>(*runtime);
119   return JavaScriptObject::newObjectCxxArgs(weak_from_this(), newObject);
120 }
121 
122 void JavaScriptRuntime::drainJSEventLoop() {
123   while (!runtime->drainMicrotasks()) {}
124 }
125 } // namespace expo
126