diff --git a/Common/cpp/SharedItems/Shareables.cpp b/Common/cpp/SharedItems/Shareables.cpp index 1d542933e80..da7d17dbe9f 100644 --- a/Common/cpp/SharedItems/Shareables.cpp +++ b/Common/cpp/SharedItems/Shareables.cpp @@ -4,6 +4,9 @@ using namespace facebook; namespace reanimated { +std::atomic ShareableObject::isNativeStateImplemented_ = + IsNativeStateImplemented::Unknown; + jsi::Function getValueUnpacker(jsi::Runtime &rt) { auto valueUnpacker = rt.global().getProperty(rt, "__valueUnpacker"); assert(valueUnpacker.isObject() && "valueUnpacker not found"); @@ -99,8 +102,8 @@ jsi::Value makeShareableClone( } else if (value.isSymbol()) { // TODO: this is only a placeholder implementation, here we replace symbols // with strings in order to make certain objects to be captured. There isn't - // yet any usecase for using symbols on the UI runtime so it is fine to keep - // it like this for now. + // yet any use case for using symbols on the UI runtime so it is fine to + // keep it like this for now. shareable = std::make_shared(value.getSymbol(rt).toString(rt)); } else { @@ -198,8 +201,11 @@ ShareableObject::ShareableObject(jsi::Runtime &rt, const jsi::Object &object) auto value = extractShareableOrThrow(rt, object.getProperty(rt, key)); data_.emplace_back(key.utf8(rt), value); } - if (object.hasNativeState(rt)) { - nativeState_ = object.getNativeState(rt); + if (isNativeStateImplemented_ == IsNativeStateImplemented::Yes) { + makeNativeStateFromObject(rt, object); + } else if (isNativeStateImplemented_ == IsNativeStateImplemented::Unknown) { + runWithNativeStateProbe( + [&]() { makeNativeStateFromObject(rt, object); }); } } @@ -208,9 +214,11 @@ ShareableObject::ShareableObject( const jsi::Object &object, const jsi::Value &nativeStateSource) : ShareableObject(rt, object) { - if (nativeStateSource.isObject() && - nativeStateSource.asObject(rt).hasNativeState(rt)) { - nativeState_ = nativeStateSource.asObject(rt).getNativeState(rt); + if (isNativeStateImplemented_ == IsNativeStateImplemented::Yes) { + makeNativeStateFromNativeStateSource(rt, nativeStateSource); + } else if (isNativeStateImplemented_ == IsNativeStateImplemented::Unknown) { + runWithNativeStateProbe( + [&]() { makeNativeStateFromNativeStateSource(rt, nativeStateSource); }); } } @@ -221,11 +229,45 @@ jsi::Value ShareableObject::toJSValue(jsi::Runtime &rt) { rt, data_[i].first.c_str(), data_[i].second->getJSValue(rt)); } if (nativeState_ != nullptr) { - obj.setNativeState(rt, nativeState_); + if (isNativeStateImplemented_ == IsNativeStateImplemented::Yes) { + obj.setNativeState(rt, nativeState_); + } else if (isNativeStateImplemented_ == IsNativeStateImplemented::Unknown) { + runWithNativeStateProbe( + [&]() { obj.setNativeState(rt, nativeState_); }); + } } return obj; } +void ShareableObject::makeNativeStateFromObject( + jsi::Runtime &rt, + const jsi::Object &object) { + if (object.hasNativeState(rt)) { + nativeState_ = object.getNativeState(rt); + isNativeStateImplemented_ = IsNativeStateImplemented::Yes; + } +} + +void ShareableObject::makeNativeStateFromNativeStateSource( + jsi::Runtime &rt, + const jsi::Value &nativeStateSource) { + if (nativeStateSource.isObject() && + nativeStateSource.asObject(rt).hasNativeState(rt)) { + nativeState_ = nativeStateSource.asObject(rt).getNativeState(rt); + isNativeStateImplemented_ = IsNativeStateImplemented::Yes; + } +} + +void ShareableObject::runWithNativeStateProbe( + std::function &&block) { + try { + block(); + isNativeStateImplemented_ = IsNativeStateImplemented::Yes; + } catch (...) { + isNativeStateImplemented_ = IsNativeStateImplemented::No; + } +} + jsi::Value ShareableHostObject::toJSValue(jsi::Runtime &rt) { return jsi::Object::createFromHostObject(rt, hostObject_); } @@ -270,12 +312,12 @@ jsi::Value ShareableHandle::toJSValue(jsi::Runtime &rt) { rt, initObj, jsi::String::createFromAscii(rt, "Handle"))); // We are locking the initialization here since the thread that is - // initalizing can be pre-empted on runtime lock. E.g. - // UI thread can be pre-empted on initialization of a shared value and then - // JS thread can try to access the shared value, locking the whole runtime. - // If we put the lock on `getValueUnpacker` part (basically any part that - // requires runtime) we would get a deadlock since UI thread would never - // release it. + // initializing can be preempted on runtime lock. E.g. + // UI thread can be preempted on initialization of a shared value and + // then JS thread can try to access the shared value, locking the whole + // runtime. If we put the lock on `getValueUnpacker` part (basically any + // part that requires runtime) we would get a deadlock since UI thread + // would never release it. std::unique_lock lock(initializationMutex_); if (remoteValue_ == nullptr) { remoteValue_ = std::move(value); diff --git a/Common/cpp/SharedItems/Shareables.h b/Common/cpp/SharedItems/Shareables.h index 46b06720f0e..815b0decf94 100644 --- a/Common/cpp/SharedItems/Shareables.h +++ b/Common/cpp/SharedItems/Shareables.h @@ -179,6 +179,12 @@ class ShareableArray : public Shareable { std::vector> data_; }; +enum class IsNativeStateImplemented { + Unknown, + Yes, + No, +}; + class ShareableObject : public Shareable { public: ShareableObject(jsi::Runtime &rt, const jsi::Object &object); @@ -191,8 +197,20 @@ class ShareableObject : public Shareable { jsi::Value toJSValue(jsi::Runtime &rt) override; protected: + void makeNativeStateFromObject(jsi::Runtime &rt, const jsi::Object &object); + void makeNativeStateFromNativeStateSource( + jsi::Runtime &rt, + const jsi::Value &nativeStateSource); + std::vector>> data_; std::shared_ptr nativeState_; + + // In some implementations of JSC - namely, macOS + // the methods that refer to the native state of an object + // are not implemented and they throw the error. Therefore + // we need to keep track of the access mode to the native state. + static std::atomic isNativeStateImplemented_; + static void runWithNativeStateProbe(std::function &&block); }; class ShareableHostObject : public Shareable { diff --git a/MacOSExample/macos/Podfile.lock b/MacOSExample/macos/Podfile.lock index 00cd2772b76..732dc0261be 100644 --- a/MacOSExample/macos/Podfile.lock +++ b/MacOSExample/macos/Podfile.lock @@ -1203,11 +1203,11 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 0686b6af8cbd638c784fea5afb789be66699823c - DoubleConversion: acaf5db79676d2e9119015819153f0f99191de12 + DoubleConversion: ca54355f8932558971f6643521d62b9bc8231cee FBLazyVector: 6d4868013ba47ee362b596a3ccc6f256a0412519 FBReactNativeSpec: 030e69b320c718834248085bfd0bd6652141c28c fmt: 03574da4b7ba40de39da59677ca66610ce8c4a02 - glog: 6df0a3d6e2750a50609471fd1a01fd2948d405b5 + glog: 3a72874c0322c7caf24931d3a2777cb7a3090529 RCT-Folly: 68e9c0fd4c0f05964afd447041d3ac2d67298f27 RCTRequired: 80f6978c000be199cc243f876974fd848651a247 RCTTypeSafety: cf3c24b03624d8ed3b7a1723f25ac159ff19ad70 @@ -1255,7 +1255,7 @@ SPEC CHECKSUMS: RNReanimated: 102299541274020227cc99875a2bc080e23e82ce RNSVG: ba3e7232f45e34b7b47e74472386cf4e1a676d0a SocketRocket: f6c6249082c011e6de2de60ed641ef8bbe0cfac9 - Yoga: d7c14f5598b68c9415cc426df87de9d5697e40c4 + Yoga: 24ddb4963e2a3330df762423f0a9de2096325401 PODFILE CHECKSUM: bc6f61f87c0dc3e6c1b90a39188b38c3dcd174b0 diff --git a/src/createAnimatedComponent/JSPropsUpdater.ts b/src/createAnimatedComponent/JSPropsUpdater.ts index 7222e33bed0..206db88a8e2 100644 --- a/src/createAnimatedComponent/JSPropsUpdater.ts +++ b/src/createAnimatedComponent/JSPropsUpdater.ts @@ -25,8 +25,8 @@ class JSPropsUpdaterPaper implements IJSPropsUpdater { constructor() { this._reanimatedEventEmitter = new NativeEventEmitter( - // NativeEventEmitter only uses this parameter on iOS. - Platform.OS === 'ios' + // NativeEventEmitter only uses this parameter on iOS and macOS. + Platform.OS === 'ios' || Platform.OS === 'macos' ? (NativeReanimatedModule as unknown as NativeModule) : undefined );