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: 34 HermesExecutorRuntimeAdapter( 35 facebook::hermes::HermesRuntime &hermesRuntime, 36 std::shared_ptr<MessageQueueThread> thread) 37 : hermesRuntime_(hermesRuntime), thread_(std::move(thread)) {} 38 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 46 facebook::hermes::HermesRuntime &getRuntime() override { 47 return hermesRuntime_; 48 } 49 #else 50 facebook::jsi::Runtime &getRuntime() override { 51 return hermesRuntime_; 52 } 53 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. 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 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 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