1 #include "NativeReanimatedModule.h"
2 
3 #ifdef ABI49_0_0RCT_NEW_ARCH_ENABLED
4 #include <react/renderer/core/TraitCast.h>
5 #include <react/renderer/uimanager/UIManagerBinding.h>
6 #include <react/renderer/uimanager/primitives.h>
7 #endif
8 
9 #include <functional>
10 #include <memory>
11 #include <thread>
12 #include <unordered_map>
13 
14 #ifdef ABI49_0_0RCT_NEW_ARCH_ENABLED
15 #include "FabricUtils.h"
16 #include "NewestShadowNodesRegistry.h"
17 #include "ReanimatedUIManagerBinding.h"
18 #include "ShadowTreeCloner.h"
19 #endif
20 
21 #include "EventHandlerRegistry.h"
22 #include "FeaturesConfig.h"
23 #include "ReanimatedHiddenHeaders.h"
24 #include "RuntimeDecorator.h"
25 #include "Shareables.h"
26 #include "WorkletEventHandler.h"
27 
28 using namespace ABI49_0_0facebook;
29 
30 namespace ABI49_0_0reanimated {
31 
32 NativeReanimatedModule::NativeReanimatedModule(
33     const std::shared_ptr<CallInvoker> &jsInvoker,
34     const std::shared_ptr<Scheduler> &scheduler,
35     const std::shared_ptr<jsi::Runtime> &rt,
36     const std::shared_ptr<ErrorHandler> &errorHandler,
37 #ifdef ABI49_0_0RCT_NEW_ARCH_ENABLED
38 // nothing
39 #else
40     std::function<jsi::Value(jsi::Runtime &, const int, const jsi::String &)>
41         propObtainer,
42 #endif
43     PlatformDepMethodsHolder platformDepMethodsHolder)
44     : NativeReanimatedModuleSpec(jsInvoker),
45       RuntimeManager(rt, errorHandler, scheduler, RuntimeType::UI),
46       eventHandlerRegistry(std::make_unique<EventHandlerRegistry>()),
47       requestRender(platformDepMethodsHolder.requestRender),
48 #ifdef ABI49_0_0RCT_NEW_ARCH_ENABLED
49 // nothing
50 #else
51       propObtainer(propObtainer),
52 #endif
53       animatedSensorModule(platformDepMethodsHolder),
54 #ifdef ABI49_0_0RCT_NEW_ARCH_ENABLED
55       synchronouslyUpdateUIPropsFunction(
56           platformDepMethodsHolder.synchronouslyUpdateUIPropsFunction)
57 #else
58       configurePropsPlatformFunction(
59           platformDepMethodsHolder.configurePropsFunction)
60 #endif
61 {
62   auto requestAnimationFrame = [=](jsi::Runtime &rt, const jsi::Value &fn) {
63     auto jsFunction = std::make_shared<jsi::Value>(rt, fn);
64     frameCallbacks.push_back([=](double timestamp) {
65       runtimeHelper->runOnUIGuarded(*jsFunction, jsi::Value(timestamp));
66     });
67     maybeRequestRender();
68   };
69 
70   auto scheduleOnJS = [this](
71                           jsi::Runtime &rt,
72                           const jsi::Value &remoteFun,
73                           const jsi::Value &argsValue) {
74     auto shareableRemoteFun = extractShareableOrThrow<ShareableRemoteFunction>(
75         rt,
76         remoteFun,
77         "Incompatible object passed to scheduleOnJS. It is only allowed to schedule functions defined on the ABI49_0_0React Native JS runtime this way.");
78     auto shareableArgs = argsValue.isUndefined()
79         ? nullptr
80         : extractShareableOrThrow(rt, argsValue);
81     auto jsRuntime = this->runtimeHelper->rnRuntime();
82     this->scheduler->scheduleOnJS([=] {
83       jsi::Runtime &rt = *jsRuntime;
84       auto remoteFun = shareableRemoteFun->getJSValue(rt);
85       if (shareableArgs == nullptr) {
86         // fast path for remote function w/o arguments
87         remoteFun.asObject(rt).asFunction(rt).call(rt);
88       } else {
89         auto argsArray = shareableArgs->getJSValue(rt).asObject(rt).asArray(rt);
90         auto argsSize = argsArray.size(rt);
91         // number of arguments is typically relatively small so it is ok to
92         // to use VLAs here, hence disabling the lint rule
93         jsi::Value args[argsSize]; // NOLINT(runtime/arrays)
94         for (size_t i = 0; i < argsSize; i++) {
95           args[i] = argsArray.getValueAtIndex(rt, i);
96         }
97         remoteFun.asObject(rt).asFunction(rt).call(rt, args, argsSize);
98       }
99     });
100   };
101 
102   auto makeShareableClone = [this](jsi::Runtime &rt, const jsi::Value &value) {
103     return this->makeShareableClone(rt, value, jsi::Value::undefined());
104   };
105 
106   auto updateDataSynchronously =
107       [this](
108           jsi::Runtime &rt,
109           const jsi::Value &synchronizedDataHolderRef,
110           const jsi::Value &newData) {
111         return this->updateDataSynchronously(
112             rt, synchronizedDataHolderRef, newData);
113       };
114 
115 #ifdef ABI49_0_0RCT_NEW_ARCH_ENABLED
116   auto updateProps = [this](
117                          jsi::Runtime &rt,
118                          const jsi::Value &shadowNodeValue,
119                          const jsi::Value &props) {
120     this->updateProps(rt, shadowNodeValue, props);
121   };
122 
123   auto removeShadowNodeFromRegistry =
124       [this](jsi::Runtime &rt, const jsi::Value &tag) {
125         this->removeShadowNodeFromRegistry(rt, tag);
126       };
127 
128   auto measure = [this](jsi::Runtime &rt, const jsi::Value &shadowNodeValue) {
129     return this->measure(rt, shadowNodeValue);
130   };
131 
132   auto dispatchCommand = [this](
133                              jsi::Runtime &rt,
134                              const jsi::Value &shadowNodeValue,
135                              const jsi::Value &commandNameValue,
136                              const jsi::Value &argsValue) {
137     this->dispatchCommand(rt, shadowNodeValue, commandNameValue, argsValue);
138   };
139 #endif
140 
141   RuntimeDecorator::decorateUIRuntime(
142       *runtime,
143 #ifdef ABI49_0_0RCT_NEW_ARCH_ENABLED
144       updateProps,
145       measure,
146       removeShadowNodeFromRegistry,
147       dispatchCommand,
148 #else
149       platformDepMethodsHolder.updatePropsFunction,
150       platformDepMethodsHolder.measureFunction,
151       platformDepMethodsHolder.scrollToFunction,
152 #endif
153       requestAnimationFrame,
154       scheduleOnJS,
155       makeShareableClone,
156       updateDataSynchronously,
157       platformDepMethodsHolder.getCurrentTime,
158       platformDepMethodsHolder.setGestureStateFunction,
159       platformDepMethodsHolder.progressLayoutAnimation,
160       platformDepMethodsHolder.endLayoutAnimation,
161       platformDepMethodsHolder.maybeFlushUIUpdatesQueueFunction);
162   onRenderCallback = [this](double timestampMs) {
163     this->renderRequested = false;
164     this->onRender(timestampMs);
165   };
166 
167 #ifdef ABI49_0_0RCT_NEW_ARCH_ENABLED
168   // nothing
169 #else
170   updatePropsFunction = platformDepMethodsHolder.updatePropsFunction;
171 #endif
172   subscribeForKeyboardEventsFunction =
173       platformDepMethodsHolder.subscribeForKeyboardEvents;
174   unsubscribeFromKeyboardEventsFunction =
175       platformDepMethodsHolder.unsubscribeFromKeyboardEvents;
176 }
177 
178 void NativeReanimatedModule::installCoreFunctions(
179     jsi::Runtime &rt,
180     const jsi::Value &callGuard,
181     const jsi::Value &valueUnpacker) {
182   if (!runtimeHelper) {
183     // initialize runtimeHelper here if not already present. We expect only one
184     // instace of the helper to exists.
185     runtimeHelper =
186         std::make_shared<JSRuntimeHelper>(&rt, this->runtime.get(), scheduler);
187   }
188   runtimeHelper->callGuard =
189       std::make_unique<CoreFunction>(runtimeHelper.get(), callGuard);
190   runtimeHelper->valueUnpacker =
191       std::make_unique<CoreFunction>(runtimeHelper.get(), valueUnpacker);
192 }
193 
194 NativeReanimatedModule::~NativeReanimatedModule() {
195   if (runtimeHelper) {
196     runtimeHelper->callGuard = nullptr;
197     runtimeHelper->valueUnpacker = nullptr;
198     // event handler registry and frame callbacks store some JSI values from UI
199     // runtime, so they have to go away before we tear down the runtime
200     eventHandlerRegistry.reset();
201     frameCallbacks.clear();
202     runtime.reset();
203     // make sure uiRuntimeDestroyed is set after the runtime is deallocated
204     runtimeHelper->uiRuntimeDestroyed = true;
205   }
206 }
207 
208 void NativeReanimatedModule::scheduleOnUI(
209     jsi::Runtime &rt,
210     const jsi::Value &worklet) {
211   auto shareableWorklet = extractShareableOrThrow(rt, worklet);
212   assert(
213       shareableWorklet->valueType() == Shareable::WorkletType &&
214       "only worklets can be scheduled to run on UI");
215   scheduler->scheduleOnUI([=] {
216     jsi::Runtime &rt = *runtimeHelper->uiRuntime();
217     auto workletValue = shareableWorklet->getJSValue(rt);
218     runtimeHelper->runOnUIGuarded(workletValue);
219   });
220 }
221 
222 jsi::Value NativeReanimatedModule::makeSynchronizedDataHolder(
223     jsi::Runtime &rt,
224     const jsi::Value &initialShareable) {
225   auto dataHolder = std::make_shared<ShareableSynchronizedDataHolder>(
226       runtimeHelper, rt, initialShareable);
227   return dataHolder->getJSValue(rt);
228 }
229 
230 void NativeReanimatedModule::updateDataSynchronously(
231     jsi::Runtime &rt,
232     const jsi::Value &synchronizedDataHolderRef,
233     const jsi::Value &newData) {
234   auto dataHolder = extractShareableOrThrow<ShareableSynchronizedDataHolder>(
235       rt, synchronizedDataHolderRef);
236   dataHolder->set(rt, newData);
237 }
238 
239 jsi::Value NativeReanimatedModule::getDataSynchronously(
240     jsi::Runtime &rt,
241     const jsi::Value &synchronizedDataHolderRef) {
242   auto dataHolder = extractShareableOrThrow<ShareableSynchronizedDataHolder>(
243       rt, synchronizedDataHolderRef);
244   return dataHolder->get(rt);
245 }
246 
247 jsi::Value NativeReanimatedModule::makeShareableClone(
248     jsi::Runtime &rt,
249     const jsi::Value &value,
250     const jsi::Value &shouldRetainRemote) {
251   std::shared_ptr<Shareable> shareable;
252   if (value.isObject()) {
253     auto object = value.asObject(rt);
254     if (!object.getProperty(rt, "__workletHash").isUndefined()) {
255       shareable = std::make_shared<ShareableWorklet>(runtimeHelper, rt, object);
256     } else if (!object.getProperty(rt, "__init").isUndefined()) {
257       shareable = std::make_shared<ShareableHandle>(runtimeHelper, rt, object);
258     } else if (object.isFunction(rt)) {
259       auto function = object.asFunction(rt);
260       if (function.isHostFunction(rt)) {
261         shareable =
262             std::make_shared<ShareableHostFunction>(rt, std::move(function));
263       } else {
264         shareable = std::make_shared<ShareableRemoteFunction>(
265             runtimeHelper, rt, std::move(function));
266       }
267     } else if (object.isArray(rt)) {
268       if (shouldRetainRemote.isBool() && shouldRetainRemote.getBool()) {
269         shareable = std::make_shared<RetainingShareable<ShareableArray>>(
270             runtimeHelper, rt, object.asArray(rt));
271       } else {
272         shareable = std::make_shared<ShareableArray>(rt, object.asArray(rt));
273       }
274     } else if (object.isHostObject(rt)) {
275       shareable = std::make_shared<ShareableHostObject>(
276           runtimeHelper, rt, object.getHostObject(rt));
277     } else {
278       if (shouldRetainRemote.isBool() && shouldRetainRemote.getBool()) {
279         shareable = std::make_shared<RetainingShareable<ShareableObject>>(
280             runtimeHelper, rt, object);
281       } else {
282         shareable = std::make_shared<ShareableObject>(rt, object);
283       }
284     }
285   } else if (value.isString()) {
286     shareable = std::make_shared<ShareableString>(value.asString(rt).utf8(rt));
287   } else if (value.isUndefined()) {
288     shareable = std::make_shared<ShareableScalar>();
289   } else if (value.isNull()) {
290     shareable = std::make_shared<ShareableScalar>(nullptr);
291   } else if (value.isBool()) {
292     shareable = std::make_shared<ShareableScalar>(value.getBool());
293   } else if (value.isNumber()) {
294     shareable = std::make_shared<ShareableScalar>(value.getNumber());
295   } else if (value.isSymbol()) {
296     // TODO: this is only a placeholder implementation, here we replace symbols
297     // with strings in order to make certain objects to be captured. There isn't
298     // yet any usecase for using symbols on the UI runtime so it is fine to keep
299     // it like this for now.
300     shareable =
301         std::make_shared<ShareableString>(value.getSymbol(rt).toString(rt));
302   } else {
303     throw std::runtime_error("attempted to convert an unsupported value type");
304   }
305   return ShareableJSRef::newHostObject(rt, shareable);
306 }
307 
308 jsi::Value NativeReanimatedModule::registerEventHandler(
309     jsi::Runtime &rt,
310     const jsi::Value &eventHash,
311     const jsi::Value &worklet) {
312   static uint64_t EVENT_HANDLER_ID = 1;
313 
314   uint64_t newRegistrationId = EVENT_HANDLER_ID++;
315   auto eventName = eventHash.asString(rt).utf8(rt);
316   auto handlerShareable = extractShareableOrThrow(rt, worklet);
317 
318   scheduler->scheduleOnUI([=] {
319     jsi::Runtime &rt = *runtimeHelper->uiRuntime();
320     auto handlerFunction = handlerShareable->getJSValue(rt);
321     auto handler = std::make_shared<WorkletEventHandler>(
322         runtimeHelper,
323         newRegistrationId,
324         eventName,
325         std::move(handlerFunction));
326     eventHandlerRegistry->registerEventHandler(std::move(handler));
327   });
328 
329   return jsi::Value(static_cast<double>(newRegistrationId));
330 }
331 
332 void NativeReanimatedModule::unregisterEventHandler(
333     jsi::Runtime &rt,
334     const jsi::Value &registrationId) {
335   uint64_t id = registrationId.asNumber();
336   scheduler->scheduleOnUI(
337       [=] { eventHandlerRegistry->unregisterEventHandler(id); });
338 }
339 
340 jsi::Value NativeReanimatedModule::getViewProp(
341     jsi::Runtime &rt,
342     const jsi::Value &viewTag,
343     const jsi::Value &propName,
344     const jsi::Value &callback) {
345   const int viewTagInt = static_cast<int>(viewTag.asNumber());
346   std::string propNameStr = propName.asString(rt).utf8(rt);
347   jsi::Function fun = callback.getObject(rt).asFunction(rt);
348   std::shared_ptr<jsi::Function> funPtr =
349       std::make_shared<jsi::Function>(std::move(fun));
350 
351   scheduler->scheduleOnUI([&rt, viewTagInt, funPtr, this, propNameStr]() {
352     const jsi::String propNameValue =
353         jsi::String::createFromUtf8(rt, propNameStr);
354     jsi::Value result = propObtainer(rt, viewTagInt, propNameValue);
355     std::string resultStr = result.asString(rt).utf8(rt);
356 
357     scheduler->scheduleOnJS([&rt, resultStr, funPtr]() {
358       const jsi::String resultValue =
359           jsi::String::createFromUtf8(rt, resultStr);
360       funPtr->call(rt, resultValue);
361     });
362   });
363 
364   return jsi::Value::undefined();
365 }
366 
367 jsi::Value NativeReanimatedModule::enableLayoutAnimations(
368     jsi::Runtime &rt,
369     const jsi::Value &config) {
370   FeaturesConfig::setLayoutAnimationEnabled(config.getBool());
371   return jsi::Value::undefined();
372 }
373 
374 jsi::Value NativeReanimatedModule::configureProps(
375     jsi::Runtime &rt,
376     const jsi::Value &uiProps,
377     const jsi::Value &nativeProps) {
378 #ifdef ABI49_0_0RCT_NEW_ARCH_ENABLED
379   jsi::Array array = nativeProps.asObject(rt).asArray(rt);
380   for (int i = 0; i < array.size(rt); ++i) {
381     std::string name = array.getValueAtIndex(rt, i).asString(rt).utf8(rt);
382     nativePropNames_.insert(name);
383   }
384 #else
385   configurePropsPlatformFunction(rt, uiProps, nativeProps);
386 #endif // ABI49_0_0RCT_NEW_ARCH_ENABLED
387 
388   return jsi::Value::undefined();
389 }
390 
391 jsi::Value NativeReanimatedModule::configureLayoutAnimation(
392     jsi::Runtime &rt,
393     const jsi::Value &viewTag,
394     const jsi::Value &type,
395     const jsi::Value &sharedTransitionTag,
396     const jsi::Value &config) {
397   layoutAnimationsManager_.configureAnimation(
398       viewTag.asNumber(),
399       static_cast<LayoutAnimationType>(type.asNumber()),
400       sharedTransitionTag.asString(rt).utf8(rt),
401       extractShareableOrThrow(rt, config));
402   return jsi::Value::undefined();
403 }
404 
405 void NativeReanimatedModule::onEvent(
406     double eventTimestamp,
407     const std::string &eventName,
408     const jsi::Value &payload) {
409   eventHandlerRegistry->processEvent(
410       *runtime, eventTimestamp, eventName, payload);
411 }
412 
413 bool NativeReanimatedModule::isAnyHandlerWaitingForEvent(
414     std::string eventName) {
415   return eventHandlerRegistry->isAnyHandlerWaitingForEvent(eventName);
416 }
417 
418 void NativeReanimatedModule::maybeRequestRender() {
419   if (!renderRequested) {
420     renderRequested = true;
421     requestRender(onRenderCallback, *this->runtime);
422   }
423 }
424 
425 void NativeReanimatedModule::onRender(double timestampMs) {
426   std::vector<FrameCallback> callbacks = frameCallbacks;
427   frameCallbacks.clear();
428   for (auto &callback : callbacks) {
429     callback(timestampMs);
430   }
431 }
432 
433 jsi::Value NativeReanimatedModule::registerSensor(
434     jsi::Runtime &rt,
435     const jsi::Value &sensorType,
436     const jsi::Value &interval,
437     const jsi::Value &iosReferenceFrame,
438     const jsi::Value &sensorDataHandler) {
439   return animatedSensorModule.registerSensor(
440       rt,
441       runtimeHelper,
442       sensorType,
443       interval,
444       iosReferenceFrame,
445       sensorDataHandler);
446 }
447 
448 void NativeReanimatedModule::unregisterSensor(
449     jsi::Runtime &rt,
450     const jsi::Value &sensorId) {
451   animatedSensorModule.unregisterSensor(sensorId);
452 }
453 
454 void NativeReanimatedModule::cleanupSensors() {
455   animatedSensorModule.unregisterAllSensors();
456 }
457 
458 #ifdef ABI49_0_0RCT_NEW_ARCH_ENABLED
459 bool NativeReanimatedModule::isThereAnyLayoutProp(
460     jsi::Runtime &rt,
461     const jsi::Value &props) {
462   const jsi::Array propNames = props.asObject(rt).getPropertyNames(rt);
463   for (size_t i = 0; i < propNames.size(rt); ++i) {
464     const std::string propName =
465         propNames.getValueAtIndex(rt, i).asString(rt).utf8(rt);
466     bool isLayoutProp =
467         nativePropNames_.find(propName) != nativePropNames_.end();
468     if (isLayoutProp) {
469       return true;
470     }
471   }
472   return false;
473 }
474 #endif // ABI49_0_0RCT_NEW_ARCH_ENABLED
475 
476 bool NativeReanimatedModule::handleEvent(
477     const std::string &eventName,
478     const jsi::Value &payload,
479     double currentTime) {
480   onEvent(currentTime, eventName, payload);
481 
482   // TODO: return true if Reanimated successfully handled the event
483   // to avoid sending it to JavaScript
484   return false;
485 }
486 
487 #ifdef ABI49_0_0RCT_NEW_ARCH_ENABLED
488 bool NativeReanimatedModule::handleRawEvent(
489     const RawEvent &rawEvent,
490     double currentTime) {
491   const EventTarget *eventTarget = rawEvent.eventTarget.get();
492   if (eventTarget == nullptr) {
493     // after app reload scrollview is unmounted and its content offset is set to
494     // 0 and view is thrown into recycle pool setting content offset triggers
495     // scroll event eventTarget is null though, because it's unmounting we can
496     // just ignore this event, because it's an event on unmounted component
497     return false;
498   }
499   const std::string &type = rawEvent.type;
500   const ValueFactory &payloadFactory = rawEvent.payloadFactory;
501 
502   int tag = eventTarget->getTag();
503   std::string eventType = type;
504   if (eventType.rfind("top", 0) == 0) {
505     eventType = "on" + eventType.substr(3);
506   }
507   std::string eventName = std::to_string(tag) + eventType;
508   jsi::Runtime &rt = *runtime.get();
509   jsi::Value payload = payloadFactory(rt);
510 
511   auto res = handleEvent(eventName, std::move(payload), currentTime);
512   // TODO: we should call performOperations conditionally if event is handled
513   // (res == true), but for now handleEvent always returns false. Thankfully,
514   // performOperations does not trigger a lot of code if there is nothing to be
515   // done so this is fine for now.
516   performOperations();
517   return res;
518 }
519 
520 void NativeReanimatedModule::updateProps(
521     jsi::Runtime &rt,
522     const jsi::Value &shadowNodeValue,
523     const jsi::Value &props) {
524   ShadowNode::Shared shadowNode = shadowNodeFromValue(rt, shadowNodeValue);
525 
526   // TODO: support multiple surfaces
527   surfaceId_ = shadowNode->getSurfaceId();
528 
529   if (isThereAnyLayoutProp(rt, props)) {
530     operationsInBatch_.emplace_back(
531         shadowNode, std::make_unique<jsi::Value>(rt, props));
532   } else {
533     // TODO: batch with layout props changes?
534     Tag tag = shadowNode->getTag();
535     synchronouslyUpdateUIPropsFunction(rt, tag, props);
536   }
537 }
538 
539 void NativeReanimatedModule::performOperations() {
540   if (operationsInBatch_.empty()) {
541     return;
542   }
543 
544   auto copiedOperationsQueue = std::move(operationsInBatch_);
545   operationsInBatch_ =
546       std::vector<std::pair<ShadowNode::Shared, std::unique_ptr<jsi::Value>>>();
547 
548   auto copiedTagsToRemove = std::move(tagsToRemove_);
549   tagsToRemove_ = std::vector<Tag>();
550 
551   react_native_assert(uiManager_ != nullptr);
552   const auto &shadowTreeRegistry = uiManager_->getShadowTreeRegistry();
553   jsi::Runtime &rt = *runtime.get();
554 
555   shadowTreeRegistry.visit(surfaceId_, [&](ShadowTree const &shadowTree) {
556     auto lock = newestShadowNodesRegistry_->createLock();
557 
558     shadowTree.commit(
559         [&](RootShadowNode const &oldRootShadowNode) {
560           auto rootNode =
561               oldRootShadowNode.ShadowNode::clone(ShadowNodeFragment{});
562 
563           ShadowTreeCloner shadowTreeCloner{
564               newestShadowNodesRegistry_, uiManager_, surfaceId_};
565 
566           for (const auto &pair : copiedOperationsQueue) {
567             const ShadowNodeFamily &family = pair.first->getFamily();
568             react_native_assert(family.getSurfaceId() == surfaceId_);
569 
570             auto newRootNode = shadowTreeCloner.cloneWithNewProps(
571                 rootNode, family, RawProps(rt, *pair.second));
572 
573             if (newRootNode == nullptr) {
574               // this happens when ABI49_0_0React removed the component but Reanimated
575               // still tries to animate it, let's skip update for this specific
576               // component
577               continue;
578             }
579             rootNode = newRootNode;
580           }
581 
582           // remove ShadowNodes and its ancestors from NewestShadowNodesRegistry
583           for (auto tag : copiedTagsToRemove) {
584             newestShadowNodesRegistry_->remove(tag);
585           }
586 
587           shadowTreeCloner.updateYogaChildren();
588 
589           return std::static_pointer_cast<RootShadowNode>(rootNode);
590         },
591         {/* default commit options */});
592   });
593 }
594 
595 void NativeReanimatedModule::removeShadowNodeFromRegistry(
596     jsi::Runtime &rt,
597     const jsi::Value &tag) {
598   tagsToRemove_.push_back(tag.asNumber());
599 }
600 
601 void NativeReanimatedModule::dispatchCommand(
602     jsi::Runtime &rt,
603     const jsi::Value &shadowNodeValue,
604     const jsi::Value &commandNameValue,
605     const jsi::Value &argsValue) {
606   ShadowNode::Shared shadowNode = shadowNodeFromValue(rt, shadowNodeValue);
607   std::string commandName = stringFromValue(rt, commandNameValue);
608   folly::dynamic args = commandArgsFromValue(rt, argsValue);
609   uiManager_->dispatchCommand(shadowNode, commandName, args);
610 }
611 
612 jsi::Value NativeReanimatedModule::measure(
613     jsi::Runtime &rt,
614     const jsi::Value &shadowNodeValue) {
615   // based on implementation from UIManagerBinding.cpp
616 
617   auto shadowNode = shadowNodeFromValue(rt, shadowNodeValue);
618   auto layoutMetrics = uiManager_->getRelativeLayoutMetrics(
619       *shadowNode, nullptr, {/* .includeTransform = */ true});
620 
621   if (layoutMetrics == EmptyLayoutMetrics) {
622     // Originally, in this case ABI49_0_0React Native returns `{0, 0, 0, 0, 0, 0}`, most
623     // likely due to the type of measure callback function which accepts just an
624     // array of numbers (not null). In Reanimated, `measure` returns
625     // `MeasuredDimensions | null`.
626     return jsi::Value::null();
627   }
628   auto newestCloneOfShadowNode =
629       uiManager_->getNewestCloneOfShadowNode(*shadowNode);
630 
631   auto layoutableShadowNode =
632       traitCast<LayoutableShadowNode const *>(newestCloneOfShadowNode.get());
633   ABI49_0_0facebook::ABI49_0_0React::Point originRelativeToParent =
634       layoutableShadowNode != nullptr
635       ? layoutableShadowNode->getLayoutMetrics().frame.origin
636       : ABI49_0_0facebook::ABI49_0_0React::Point();
637 
638   auto frame = layoutMetrics.frame;
639 
640   jsi::Object result(rt);
641   result.setProperty(
642       rt, "x", jsi::Value(static_cast<double>(originRelativeToParent.x)));
643   result.setProperty(
644       rt, "y", jsi::Value(static_cast<double>(originRelativeToParent.y)));
645   result.setProperty(
646       rt, "width", jsi::Value(static_cast<double>(frame.size.width)));
647   result.setProperty(
648       rt, "height", jsi::Value(static_cast<double>(frame.size.height)));
649   result.setProperty(
650       rt, "pageX", jsi::Value(static_cast<double>(frame.origin.x)));
651   result.setProperty(
652       rt, "pageY", jsi::Value(static_cast<double>(frame.origin.y)));
653   return result;
654 }
655 
656 void NativeReanimatedModule::setUIManager(
657     std::shared_ptr<UIManager> uiManager) {
658   uiManager_ = uiManager;
659 }
660 
661 void NativeReanimatedModule::setNewestShadowNodesRegistry(
662     std::shared_ptr<NewestShadowNodesRegistry> newestShadowNodesRegistry) {
663   newestShadowNodesRegistry_ = newestShadowNodesRegistry;
664 }
665 #endif // ABI49_0_0RCT_NEW_ARCH_ENABLED
666 
667 jsi::Value NativeReanimatedModule::subscribeForKeyboardEvents(
668     jsi::Runtime &rt,
669     const jsi::Value &handlerWorklet,
670     const jsi::Value &isStatusBarTranslucent) {
671   auto shareableHandler = extractShareableOrThrow(rt, handlerWorklet);
672   return subscribeForKeyboardEventsFunction(
673       [=](int keyboardState, int height) {
674         jsi::Runtime &rt = *runtimeHelper->uiRuntime();
675         auto handler = shareableHandler->getJSValue(rt);
676         runtimeHelper->runOnUIGuarded(
677             handler, jsi::Value(keyboardState), jsi::Value(height));
678       },
679       isStatusBarTranslucent.getBool());
680 }
681 
682 void NativeReanimatedModule::unsubscribeFromKeyboardEvents(
683     jsi::Runtime &rt,
684     const jsi::Value &listenerId) {
685   unsubscribeFromKeyboardEventsFunction(listenerId.asNumber());
686 }
687 
688 } // namespace reanimated
689