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