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