1 #include "ReanimatedHermesRuntime.h"
2
3 // Only include this file in Hermes-enabled builds as some platforms (like tvOS)
4 // don't support hermes and it causes the compilation to fail.
5 #if JS_RUNTIME_HERMES
6
7 #include <cxxreact/MessageQueueThread.h>
8 #include <jsi/decorator.h>
9 #include <jsi/jsi.h>
10
11 #include <memory>
12 #include <string>
13 #include <utility>
14
15 #if __has_include(<reacthermes/HermesExecutorFactory.h>)
16 #include <reacthermes/HermesExecutorFactory.h>
17 #else // __has_include(<hermes/hermes.h>) || ANDROID
18 #include <hermes/hermes.h>
19 #endif
20
21 #include <hermes/inspector/RuntimeAdapter.h>
22 #include <hermes/inspector/chrome/Registration.h>
23
24 namespace reanimated {
25
26 using namespace facebook;
27 using namespace react;
28
29 #if HERMES_ENABLE_DEBUGGER
30
31 class HermesExecutorRuntimeAdapter
32 : public facebook::hermes::inspector::RuntimeAdapter {
33 public:
HermesExecutorRuntimeAdapter(facebook::hermes::HermesRuntime & hermesRuntime,std::shared_ptr<MessageQueueThread> thread)34 HermesExecutorRuntimeAdapter(
35 facebook::hermes::HermesRuntime &hermesRuntime,
36 std::shared_ptr<MessageQueueThread> thread)
37 : hermesRuntime_(hermesRuntime), thread_(std::move(thread)) {}
38
~HermesExecutorRuntimeAdapter()39 virtual ~HermesExecutorRuntimeAdapter() {
40 // This is required by iOS, because there is an assertion in the destructor
41 // that the thread was indeed `quit` before
42 thread_->quitSynchronous();
43 }
44
45 #if REACT_NATIVE_MINOR_VERSION >= 71
getRuntime()46 facebook::hermes::HermesRuntime &getRuntime() override {
47 return hermesRuntime_;
48 }
49 #else
getRuntime()50 facebook::jsi::Runtime &getRuntime() override {
51 return hermesRuntime_;
52 }
53
getDebugger()54 facebook::hermes::debugger::Debugger &getDebugger() override {
55 return hermesRuntime_.getDebugger();
56 }
57 #endif // REACT_NATIVE_MINOR_VERSION
58
59 // This is not empty in the original implementation, but we decided to tickle
60 // the runtime by running a small piece of code on every frame as using this
61 // required us to hold a refernce to the runtime inside this adapter which
62 // caused issues while reloading the app.
tickleJs()63 void tickleJs() override {}
64
65 public:
66 facebook::hermes::HermesRuntime &hermesRuntime_;
67 std::shared_ptr<MessageQueueThread> thread_;
68 };
69
70 #endif // HERMES_ENABLE_DEBUGGER
71
ReanimatedHermesRuntime(std::unique_ptr<facebook::hermes::HermesRuntime> runtime,std::shared_ptr<MessageQueueThread> jsQueue)72 ReanimatedHermesRuntime::ReanimatedHermesRuntime(
73 std::unique_ptr<facebook::hermes::HermesRuntime> runtime,
74 std::shared_ptr<MessageQueueThread> jsQueue)
75 : jsi::WithRuntimeDecorator<ReanimatedReentrancyCheck>(
76 *runtime,
77 reentrancyCheck_),
78 runtime_(std::move(runtime)) {
79 #if HERMES_ENABLE_DEBUGGER
80 auto adapter =
81 std::make_unique<HermesExecutorRuntimeAdapter>(*runtime_, jsQueue);
82 #if REACT_NATIVE_MINOR_VERSION >= 71
83 debugToken_ = facebook::hermes::inspector::chrome::enableDebugging(
84 std::move(adapter), "Reanimated Runtime");
85 #else
86 facebook::hermes::inspector::chrome::enableDebugging(
87 std::move(adapter), "Reanimated Runtime");
88 #endif // REACT_NATIVE_MINOR_VERSION
89 #else
90 // This is required by iOS, because there is an assertion in the destructor
91 // that the thread was indeed `quit` before
92 jsQueue->quitSynchronous();
93 #endif
94
95 #ifdef DEBUG
96 facebook::hermes::HermesRuntime *wrappedRuntime = runtime_.get();
97 jsi::Value evalWithSourceMap = jsi::Function::createFromHostFunction(
98 *runtime_,
99 jsi::PropNameID::forAscii(*runtime_, "evalWithSourceMap"),
100 3,
101 [wrappedRuntime](
102 jsi::Runtime &rt,
103 const jsi::Value &thisValue,
104 const jsi::Value *args,
105 size_t count) -> jsi::Value {
106 auto code = std::make_shared<const jsi::StringBuffer>(
107 args[0].asString(rt).utf8(rt));
108 std::string sourceURL;
109 if (count > 1 && args[1].isString()) {
110 sourceURL = args[1].asString(rt).utf8(rt);
111 }
112 std::shared_ptr<const jsi::Buffer> sourceMap;
113 if (count > 2 && args[2].isString()) {
114 sourceMap = std::make_shared<const jsi::StringBuffer>(
115 args[2].asString(rt).utf8(rt));
116 }
117 #if REACT_NATIVE_MINOR_VERSION >= 65
118 return wrappedRuntime->evaluateJavaScriptWithSourceMap(
119 code, sourceMap, sourceURL);
120 #else
121 return wrappedRuntime->evaluateJavaScript(code, sourceURL);
122 #endif
123 });
124 runtime_->global().setProperty(
125 *runtime_, "evalWithSourceMap", evalWithSourceMap);
126 #endif // DEBUG
127 }
128
~ReanimatedHermesRuntime()129 ReanimatedHermesRuntime::~ReanimatedHermesRuntime() {
130 #if HERMES_ENABLE_DEBUGGER
131 // We have to disable debugging before the runtime is destroyed.
132 #if REACT_NATIVE_MINOR_VERSION >= 71
133 facebook::hermes::inspector::chrome::disableDebugging(debugToken_);
134 #else
135 facebook::hermes::inspector::chrome::disableDebugging(*runtime_);
136 #endif // REACT_NATIVE_MINOR_VERSION
137 #endif // HERMES_ENABLE_DEBUGGER
138 }
139
140 } // namespace reanimated
141
142 #endif // JS_RUNTIME_HERMES
143