diff --git a/React/Fabric/RCTSurfacePresenter.mm b/React/Fabric/RCTSurfacePresenter.mm index eadaf626d53022..23ec85a5111b23 100644 --- a/React/Fabric/RCTSurfacePresenter.mm +++ b/React/Fabric/RCTSurfacePresenter.mm @@ -277,6 +277,10 @@ - (RCTScheduler *)_createScheduler CoreFeatures::enablePropIteratorSetter = true; } + if (reactNativeConfig && reactNativeConfig->getBool("react_fabric:use_native_state")) { + CoreFeatures::useNativeState = true; + } + auto componentRegistryFactory = [factory = wrapManagedObject(_mountingManager.componentViewRegistry.componentViewFactory)]( EventDispatcher::Weak const &eventDispatcher, ContextContainer::Shared const &contextContainer) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java b/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java index a4df8b3565d6a9..20c90c9526e7d5 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java +++ b/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java @@ -118,4 +118,10 @@ public class ReactFeatureFlags { * SurfaceMountingManager. */ public static boolean reduceDeleteCreateMutation = false; + + /** + * Use JSI NativeState API to store references to native objects rather than the more expensive + * HostObject pattern + */ + public static boolean useNativeState = false; } diff --git a/ReactAndroid/src/main/jni/react/fabric/Binding.cpp b/ReactAndroid/src/main/jni/react/fabric/Binding.cpp index a9b6dc7c3c223a..51999c97a2933a 100644 --- a/ReactAndroid/src/main/jni/react/fabric/Binding.cpp +++ b/ReactAndroid/src/main/jni/react/fabric/Binding.cpp @@ -433,6 +433,9 @@ void Binding::installFabricUIManager( CoreFeatures::enablePropIteratorSetter = getFeatureFlagValue("enableCppPropsIteratorSetter"); + // NativeState experiment + CoreFeatures::useNativeState = getFeatureFlagValue("useNativeState"); + // RemoveDelete mega-op ShadowViewMutation::PlatformSupportsRemoveDeleteTreeInstruction = getFeatureFlagValue("enableRemoveDeleteTreeInstruction"); diff --git a/ReactCommon/react/renderer/core/CoreFeatures.cpp b/ReactCommon/react/renderer/core/CoreFeatures.cpp index b71e35cb6236fd..8685a1890ad736 100644 --- a/ReactCommon/react/renderer/core/CoreFeatures.cpp +++ b/ReactCommon/react/renderer/core/CoreFeatures.cpp @@ -13,6 +13,7 @@ namespace react { bool CoreFeatures::enablePropIteratorSetter = false; bool CoreFeatures::enableMapBuffer = false; bool CoreFeatures::blockPaintForUseLayoutEffect = false; +bool CoreFeatures::useNativeState = false; } // namespace react } // namespace facebook diff --git a/ReactCommon/react/renderer/core/CoreFeatures.h b/ReactCommon/react/renderer/core/CoreFeatures.h index 1a106390baac53..3064784eea245a 100644 --- a/ReactCommon/react/renderer/core/CoreFeatures.h +++ b/ReactCommon/react/renderer/core/CoreFeatures.h @@ -30,6 +30,10 @@ class CoreFeatures { // useLayoutEffect hooks to be processed. This changes affects scheduling of // when a transaction is mounted. static bool blockPaintForUseLayoutEffect; + + // Whether to use Hermes' NativeState instead of HostObject + // in simple data passing scenarios with JS + static bool useNativeState; }; } // namespace react diff --git a/ReactCommon/react/renderer/core/ShadowNode.h b/ReactCommon/react/renderer/core/ShadowNode.h index e1483bb08caafd..6662502d8e770c 100644 --- a/ReactCommon/react/renderer/core/ShadowNode.h +++ b/ReactCommon/react/renderer/core/ShadowNode.h @@ -29,7 +29,9 @@ static constexpr const int kShadowNodeChildrenSmallVectorSize = 8; class ComponentDescriptor; struct ShadowNodeFragment; -class ShadowNode : public Sealable, public DebugStringConvertible { +class ShadowNode : public Sealable, + public DebugStringConvertible, + public jsi::NativeState { public: using Shared = std::shared_ptr; using Weak = std::weak_ptr; diff --git a/ReactCommon/react/renderer/graphics/BUCK b/ReactCommon/react/renderer/graphics/BUCK index 5c103d48d3fe82..62700a5396f1a4 100644 --- a/ReactCommon/react/renderer/graphics/BUCK +++ b/ReactCommon/react/renderer/graphics/BUCK @@ -8,7 +8,6 @@ load( "get_apple_compiler_flags", "get_apple_inspector_flags", "get_preprocessor_flags_for_build_mode", - "react_native_target", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -53,7 +52,7 @@ rn_xplat_cxx_library( fbandroid_allow_jni_merging = True, fbandroid_deps = [ FBJNI_TARGET, - react_native_target("jni/react/jni:jni"), + "//xplat/folly:dynamic", ], fbandroid_exported_headers = subdir_glob( [ diff --git a/ReactCommon/react/renderer/runtimescheduler/BUCK b/ReactCommon/react/renderer/runtimescheduler/BUCK index 4a90c584e3fd15..d09b4a91f73846 100644 --- a/ReactCommon/react/renderer/runtimescheduler/BUCK +++ b/ReactCommon/react/renderer/runtimescheduler/BUCK @@ -49,6 +49,7 @@ rn_xplat_cxx_library( deps = [ "//xplat/folly:dynamic", react_native_xplat_target("runtimeexecutor:runtimeexecutor"), + react_native_xplat_target("react/renderer/core:core"), react_native_xplat_target("react/renderer/debug:debug"), react_native_xplat_target("butter:butter"), react_native_xplat_target("callinvoker:callinvoker"), diff --git a/ReactCommon/react/renderer/runtimescheduler/Task.h b/ReactCommon/react/renderer/runtimescheduler/Task.h index 46cc35e53cc261..ea489bcf8ae272 100644 --- a/ReactCommon/react/renderer/runtimescheduler/Task.h +++ b/ReactCommon/react/renderer/runtimescheduler/Task.h @@ -21,7 +21,7 @@ class TaskPriorityComparer; using RawCallback = std::function; -struct Task final { +struct Task final : public jsi::NativeState { Task( SchedulerPriority priority, jsi::Function callback, diff --git a/ReactCommon/react/renderer/runtimescheduler/primitives.h b/ReactCommon/react/renderer/runtimescheduler/primitives.h index caa2b2614014ca..4253d490ca0788 100644 --- a/ReactCommon/react/renderer/runtimescheduler/primitives.h +++ b/ReactCommon/react/renderer/runtimescheduler/primitives.h @@ -9,6 +9,7 @@ #include #include +#include #include namespace facebook { @@ -22,9 +23,15 @@ struct TaskWrapper : public jsi::HostObject { inline static jsi::Value valueFromTask( jsi::Runtime &runtime, - std::shared_ptr const &task) { - return jsi::Object::createFromHostObject( - runtime, std::make_shared(task)); + std::shared_ptr task) { + if (CoreFeatures::useNativeState) { + jsi::Object obj(runtime); + obj.setNativeState(runtime, std::move(task)); + return obj; + } else { + return jsi::Object::createFromHostObject( + runtime, std::make_shared(task)); + } } inline static std::shared_ptr taskFromValue( @@ -34,7 +41,11 @@ inline static std::shared_ptr taskFromValue( return nullptr; } - return value.getObject(runtime).getHostObject(runtime)->task; + if (CoreFeatures::useNativeState) { + return value.getObject(runtime).getNativeState(runtime); + } else { + return value.getObject(runtime).getHostObject(runtime)->task; + } } } // namespace react diff --git a/ReactCommon/react/renderer/uimanager/primitives.h b/ReactCommon/react/renderer/uimanager/primitives.h index a7e7f5f275e673..006eb76262eaaa 100644 --- a/ReactCommon/react/renderer/uimanager/primitives.h +++ b/ReactCommon/react/renderer/uimanager/primitives.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -38,9 +39,9 @@ struct ShadowNodeWrapper : public jsi::HostObject { ShadowNode::Shared shadowNode; }; -struct ShadowNodeListWrapper : public jsi::HostObject { +struct ShadowNodeListWrapper : public jsi::HostObject, public jsi::NativeState { ShadowNodeListWrapper(ShadowNode::UnsharedListOfShared shadowNodeList) - : shadowNodeList(shadowNodeList) {} + : shadowNodeList(std::move(shadowNodeList)) {} // The below method needs to be implemented out-of-line in order for the class // to have at least one "key function" (see @@ -52,29 +53,63 @@ struct ShadowNodeListWrapper : public jsi::HostObject { inline static ShadowNode::Shared shadowNodeFromValue( jsi::Runtime &runtime, - jsi::Value const &value) { + const jsi::Value &value) { if (value.isNull()) { return nullptr; } - return value.getObject(runtime) - .getHostObject(runtime) - ->shadowNode; + if (CoreFeatures::useNativeState) { + return value.getObject(runtime).getNativeState(runtime); + } else { + return value.getObject(runtime) + .getHostObject(runtime) + ->shadowNode; + } } inline static jsi::Value valueFromShadowNode( jsi::Runtime &runtime, ShadowNode::Shared shadowNode) { - return jsi::Object::createFromHostObject( - runtime, std::make_shared(std::move(shadowNode))); + if (CoreFeatures::useNativeState) { + jsi::Object obj(runtime); + // Need to const_cast since JSI only allows non-const pointees + obj.setNativeState( + runtime, std::const_pointer_cast(std::move(shadowNode))); + return obj; + } else { + return jsi::Object::createFromHostObject( + runtime, std::make_shared(std::move(shadowNode))); + } } inline static ShadowNode::UnsharedListOfShared shadowNodeListFromValue( jsi::Runtime &runtime, - jsi::Value const &value) { - return value.getObject(runtime) - .getHostObject(runtime) - ->shadowNodeList; + const jsi::Value &value) { + if (CoreFeatures::useNativeState) { + return value.getObject(runtime) + .getNativeState(runtime) + ->shadowNodeList; + } else { + return value.getObject(runtime) + .getHostObject(runtime) + ->shadowNodeList; + } +} + +inline static jsi::Value valueFromShadowNodeList( + jsi::Runtime &runtime, + ShadowNode::UnsharedListOfShared shadowNodeList) { + auto wrapper = + std::make_shared(std::move(shadowNodeList)); + if (CoreFeatures::useNativeState) { + // Use the wrapper for NativeState too, otherwise we can't implement + // the marker interface. Could be simplifed to a simple struct wrapper. + jsi::Object obj(runtime); + obj.setNativeState(runtime, std::move(wrapper)); + return obj; + } else { + return jsi::Object::createFromHostObject(runtime, std::move(wrapper)); + } } inline static ShadowNode::UnsharedListOfShared shadowNodeListFromWeakList( @@ -93,25 +128,14 @@ inline static ShadowNode::UnsharedListOfShared shadowNodeListFromWeakList( inline static ShadowNode::UnsharedListOfWeak weakShadowNodeListFromValue( jsi::Runtime &runtime, jsi::Value const &value) { - auto shadowNodeList = value.getObject(runtime) - .getHostObject(runtime) - ->shadowNodeList; - + auto shadowNodeList = shadowNodeListFromValue(runtime, value); auto weakShadowNodeList = std::make_shared(); for (auto const &shadowNode : *shadowNodeList) { weakShadowNodeList->push_back(shadowNode); } - return weakShadowNodeList; } -inline static jsi::Value valueFromShadowNodeList( - jsi::Runtime &runtime, - const ShadowNode::UnsharedListOfShared &shadowNodeList) { - return jsi::Object::createFromHostObject( - runtime, std::make_unique(shadowNodeList)); -} - inline static Tag tagFromValue(jsi::Value const &value) { return (Tag)value.getNumber(); }