From 56521f16c88f3e028f2cee2a5d2ff2422a86ac01 Mon Sep 17 00:00:00 2001 From: Chi Tsai Date: Sat, 15 Nov 2025 17:50:26 -0800 Subject: [PATCH 1/3] Add JSI_UNSTABLE flag Summary: X-link: https://github.com/facebook/react-native/pull/54547 We want to have a proper way of releasing new JSI features, rather than releasing into JSI as they are added. To do this, we are adding a new `JSI_UNSTABLE` flag that will gate new features. Until Hermes releases a new version, all of these "unstable" features are subject to change. Interfaces may be modified, API behavior may be changed, etc. When Hermes release a new version, the new additions will be moved out of the "unstable" status and become frozen. This diff adds the `JSI_UNSTABLE` flag to JSI and adds corresponding changes to the BUCK build. Later diffs will use this gate new APIs. Changelog: [Internal] Reviewed By: lavenzg Differential Revision: D85915163 fbshipit-source-id: b2f754af4bb53db8dfe837e3ea2e2543d691754d --- API/jsi/jsi/jsi.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/API/jsi/jsi/jsi.h b/API/jsi/jsi/jsi.h index 08edcd2a032..f4a2f6d63d4 100644 --- a/API/jsi/jsi/jsi.h +++ b/API/jsi/jsi/jsi.h @@ -252,6 +252,12 @@ class JSI_EXPORT NativeState { virtual ~NativeState(); }; +// JSI_UNSTABLE gates features that will be released with a Hermes version in +// the future. Until released, these features may be subject to change. After +// release, these features will be moved out of JSI_UNSTABLE and become frozen. +#ifdef JSI_UNSTABLE +#endif // JSI_UNSTABLE + /// Represents a JS runtime. Movable, but not copyable. Note that /// this object may not be thread-aware, but cannot be used safely from /// multiple threads at once. The application is responsible for From 21e4cf64aa569ee5b4e869ef451307389b75cbf1 Mon Sep 17 00:00:00 2001 From: Tzvetan Mikov Date: Wed, 26 Nov 2025 15:03:15 -0800 Subject: [PATCH 2/3] include Summary: `jsi.h` uses types like `uint32_t` but never includes ``. Changelog: [Internal] Reviewed By: avp Differential Revision: D87953504 fbshipit-source-id: 7d10e458ff14b01da293f2714d6a562958f9e8ff --- API/jsi/jsi/jsi.h | 1 + 1 file changed, 1 insertion(+) diff --git a/API/jsi/jsi/jsi.h b/API/jsi/jsi/jsi.h index f4a2f6d63d4..f9493d16dfc 100644 --- a/API/jsi/jsi/jsi.h +++ b/API/jsi/jsi/jsi.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include #include From 1dffa48fb978dcb2d4f9405187438b5190b2494e Mon Sep 17 00:00:00 2001 From: Chi Tsai Date: Tue, 2 Dec 2025 13:06:21 -0800 Subject: [PATCH 3/3] Add ISerialization interface Summary: X-link: https://github.com/facebook/react-native/pull/52624 Add a new optional interface `ISerialization` to JSI. This interface contains four APIs to clone objects from one runtime to another runtime. Four methods are introduced in this interface: * `serialize`: Takes in a JS value (represented by `jsi::Value`) and serialize the value into an opaque `Serialized` object. * `deserialize`: Takes in the `Serialized` object created by `serialize` and deserialize it into the runtime, returning the created JS value. * `serializeWithTransfer`: Takes in a JS value (represented by `jsi::Value`) and a `transferList` (a `jsi::Array` of `jsi::Value`s). This will serialize the `value` into an opaque `Serialize` object and transfer the ownership of everything in `transferList` into the `Serialized` object. If any non-transferable values is passed into the transferList, this will throw. This `Serialized` object must only be deserialized once. * `deserializeWithTransfer`: Takes in the `Serialized` object created by `serializeWithTransfer`. It will deserialize the object into the runtime and any value owned by the `Serialized` object will now be owned by the current runtime. It will return an `jsi::Array` where the first value is the deserialized value passed into `serializeWithTransfer`, followed by all transferred values. The lifetime of the `Serialized` object created from the APIs is independent of the original object and runtime. Note that objects can only be copied into another runtime instance of the same type. For example, a serialized object produced by the Hermes runtime can only be deserialized by another Hermes runtime. Changelog: [Internal] Reviewed By: dannysu, fbmal7 Differential Revision: D76547681 fbshipit-source-id: 0c774f3f469c26d3894cac179f4ce5e32cf5ad7f --- API/jsi/jsi/jsi.cpp | 4 +++ API/jsi/jsi/jsi.h | 66 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/API/jsi/jsi/jsi.cpp b/API/jsi/jsi/jsi.cpp index 79eb311dc3c..d7a81382e3d 100644 --- a/API/jsi/jsi/jsi.cpp +++ b/API/jsi/jsi/jsi.cpp @@ -284,6 +284,10 @@ HostObject::~HostObject() {} NativeState::~NativeState() {} +#ifdef JSI_UNSTABLE +Serialized::~Serialized() {} +#endif + Runtime::~Runtime() {} ICast* Runtime::castInterface(const UUID& /*interfaceUUID*/) { diff --git a/API/jsi/jsi/jsi.h b/API/jsi/jsi/jsi.h index f9493d16dfc..cbef7e252d2 100644 --- a/API/jsi/jsi/jsi.h +++ b/API/jsi/jsi/jsi.h @@ -257,6 +257,72 @@ class JSI_EXPORT NativeState { // the future. Until released, these features may be subject to change. After // release, these features will be moved out of JSI_UNSTABLE and become frozen. #ifdef JSI_UNSTABLE +/// Opaque class that is used to store serialized object from a runtime. The +/// lifetime of this object is orthogonal to the original runtime object, and +/// may outlive the original object. +class JSI_EXPORT Serialized { + public: + /// Uses \p secretAddr to validate if the Serialized data is supported. If so, + /// return the pointer to the underlying serialized data. Otherwise, return a + /// nullptr. This should be used by the runtime to deserialize the data. + virtual void* getPrivate(const void* secretAddr) = 0; + virtual ~Serialized(); +}; + +/// Provides a set of APIs that allows copying objects between different +/// runtime instances. The runtimes instances must be of the same type. As an +/// example, a serialized object from Hermes runtime may only be deserialized by +/// another Hermes runtime. +class JSI_EXPORT ISerialization : public ICast { + public: + static constexpr jsi::UUID uuid{ + 0xd40fe0ec, + 0xa47c, + 0x42c9, + 0x8c09, + 0x661aeab832d8}; + + /// Serializes the given Value \p value using the structured clone algorithm. + /// It returns a shared pointer of an opaque Serialized object that can be + /// deserialized multiple times. The lifetime of the Serialized object is not + /// tied to the lifetime of the original object. + virtual std::shared_ptr serialize(Value& value) = 0; + + /// Given a Serialized object provided by \p serialized, deserialize it using + /// the structured clone algorithm into a JS value in the current runtime. + /// Returns the deserialized JS value. + virtual Value deserialize(const std::shared_ptr& serialized) = 0; + + /// Serializes the given jsi::Value \p value using the structured clone + /// algorithm. \p transferList must be a JS Array. Given the length property + /// of \p transferList, this API will transfer everything at index [0, length + /// - 1] to the serialized object. The transferred values will no longer be + /// usable in the original runtime. It returns a unique pointer of an opaque + /// Serialized object that can be deserialized once only by + /// deserializeWithTransfer. The lifetime of the Serialized object is not tied + /// to the lifetime of the original object. + virtual std::unique_ptr serializeWithTransfer( + Value& value, + const Array& transferList) = 0; + + /// Using the structure clone algorithm, deserialize the object provided by \p + /// serialized into a JS value in the current runtime. \p serialized must be + /// created by serializeWithTransfer. If the current runtime does not support + /// the serialization scheme in \p serialized, then this method will throw and + /// \p serialized will remain unmodified. Otherwise, this will consume the + /// serialized data entirely and make the serialized objects in the current + /// runtime. Any transferred values in the serialized object will be owned by + /// the current runtime. + // This method returns an Array containing the deserialized values, where the + // first element is the value passed into serializeWithTransfer, + /// followed by all transferred values. + virtual Array deserializeWithTransfer( + std::unique_ptr& serialized) = 0; + + protected: + ~ISerialization() = default; +}; + #endif // JSI_UNSTABLE /// Represents a JS runtime. Movable, but not copyable. Note that