RFC: ArrayBuffer support in TurboModules#947
Conversation
|
Firstly - congrats on a very thorough and well written RFC!
It's my understanding that the JS engine and JSI itself isn't thread-safe either.
I think that could be a nice addition indeed. I'd actually imagine a read-only variant would be used in most cases 🤔
Personally - I'd apply the 80-20 rule here and go for the least amount of work bringing most of the value and stick with the basic sync support. Did you consider "views"? ( |
|
i really like this. for real-time media or GPU pipelines, say camera ---> ML ---> WebRTC, efficient ArrayBuffer bridging would make a world of difference. it'd enable moving small binary payloads(LUTs, masks, uniform buffers) without having to serialize or clone data just to cross the bridge. a few things worth clarifying though:
|
Thanks ❤️
Agree with that.
I agree with that, but after deeper investigation I couldn't find an easy and clean way to achieve that. One way would be to create a read-only view over the buffer when processing it, but that's on the developers. Also there is an active TC39 proposal for an Immutable ArrayBuffer which would provide a standardized, runtime-enforced way to prevent modifications to the buffer contents.
👍
My idea is to have this solution type-agnostic as the underlying native classes, such as |
|
The semantics around memory ownership seem to deviate from the spec of ArrayBuffer in regards to transferring/detaching. I'm not sure of what the material consequences are, especially with existing code that handles ArrayBuffers, but it seems like this could break developer expectations in many ways. I'd expect the buffer to be moved, not borrowed, when passing between JS and native (in both directions).
This problem goes away if ownership is moved to the receiving thread. You shouldn't be able to even read an ArrayBuffer from multiple threads.
So in summary and to answer this unresolved question, I would say absolutely yes. And to do so by using moves and not borrows. |
|
Thanks @tom-sherman for your input! Regarding this:
I agree that moving (transferring ownership) an ArrayBuffer coul be fundamentally safer and cleaner than borrowing it. However, the primary technical challenge remains: the current JSI and Hermes Runtime implementations do not expose a dedicated API for "detaching" an ArrayBuffer from the JavaScript side. Without true detachment, the only immediate way to transfer ownership is by "moving" the underlying buffer to the native thread and extending its lifetime accordingly. This addresses the memory management aspect but has a critical flaw:
I currently do not see a clean, safe path to fully implement buffer transfers that invalidate the JS reference. Achieving this requires dedicated changes to both the JSI specification and the underlying Hermes engine to introduce a proper detachment mechanism. Since I don't have a deep expertise in this topic, output from more experienced developers is really welcome and highly appreciated. |
|
I don't have any expertise as to how to solve the invalidation of JS references in Hermes and JSI, but I wanted to add another voice highlighting the importance of ownership transfer. As far as I am aware, JS does not have other instances where a developer needs to think about thread-safety - it is always thread safe by default. When working with threads (e.g. a |
grabbou
left a comment
There was a problem hiding this comment.
Thanks for this RFC. Excited to get this going. Leaving some comments for consideration when it comes to the RFC document itself.
| ```ts | ||
| export interface Spec extends TurboModule { | ||
| getBuffer(): ArrayBuffer; | ||
| processBuffer(buffer: ArrayBuffer): void; |
There was a problem hiding this comment.
Should we extend the RFC with some notes on asynchronous functions? I know there was discussion beforehand whether to implement asynchronous code in the first PR, however, I would treat that separate from the RFC itself, which could cover broader use case.
There was a problem hiding this comment.
I unnecessarily coupled this RFC with the first PR in my head. Asynchronous functions are mentioned a bit later, but they should be outlined here as well as this is the final API we would like to have.
|
|
||
| ## Motivation | ||
|
|
||
| TurboModules currently lack a first-class way to represent `ArrayBuffer` end-to-end in Codegen, which forces developers to rely on copies, ad-hoc platform bridges, global helpers, or external libraries. This hurts performance for binary-heavy use cases such as media data or ML tensors, and it increases implementation complexity. The expected outcome is a cross-platform contract that lets JS and native pass binary data with minimal copying. Codegen should be able to generate working code for the `ArrayBuffer` type on every platform. |
There was a problem hiding this comment.
I would say current workaround is typically working directly with JSI, which has its own advantages and disadvantages. Great example here is prior art by Marc, who did a lot of this manually before migrating over to Nitro Modules. I also think a lot of Software Mansion libraries go with C++ and work directly with JSI for that reason as well.
There was a problem hiding this comment.
Yup - both react-native-mmkv (see MMKVManagedBuffer.h) and react-native-vision-camera (see SharedArray.h) use raw JSI access to expose ArrayBuffer data. In VisionCamera I kinda overcomplicated it for a while with TypedArray support (that's primarily for Uint8Array), but still - it's a lot of effort to get right, and even tho I added a ton of code for that, it still has it's bugs. Like thread-safety.
Also; react-native-fast-tflite (see TypedArray.h) and vision-camera-resize-plugin (see ResizePlugin.mm) are using raw JSI ArrayBuffers.
In Nitro, i finally solved all of that - so MMKV benefits from that ✨ for free ✨, but VisionCamera is not on Nitro yet.
I can definitely understand the Motivation here, lol.
|
|
||
| For example, several important use cases are currently difficult to implement efficiently while working with TurboModules: | ||
|
|
||
| - **Real-time media streaming**: A native video decoder could stream frames directly to a JavaScript-based player component. Without zero-copy `ArrayBuffer`s, each frame would need to be copied, leading to significant performance overhead and potential frame drops. |
There was a problem hiding this comment.
I think great example here would be to use Blob Manager as an example (and likely first candidate to migrate over, once this lands).
Broadly speaking working with binary data in general.
There was a problem hiding this comment.
Great catch, will add a Blob Manager example to the next version of the RFC.
That said, it's worth noting that (unless I'm mistaken) this is already how JSI, Expo Modules, and Nitro Modules handle ArrayBuffers today. On one hand, aligning with existing community behavior might make sense for practical and compatibility reasons. On the other hand, once this behavior becomes part of the core, its reach and visibility will likely expand far beyond those ecosystems, making the current de facto behavior less relevant over time. Leaving this as an open question and summoning a few folks from the community for feedback! |
|
Thanks for putting this together. Generally, I'm very aligned supporting a type-safe abstraction over the existing ArrayBuffer support in JSI, and there seems to be plenty of use-cases where this would become a good way forward to unlock cheaper data sharing between JS and native. I agree with the concerns around thread-safety expressed in this thread here. Is there any prior art we can reference? Is the operation model similar to SharedArrayBuffer? Should we consider Atomics as a complementary but necessary capability here? Making this fully support async JS to native invocation calls will likely increase complexity, as it would require us to keep the JS object alive for the duration of the native memory reference, but not impossible. Alternatively, we'd need to make it really obvious that ArrayBuffer args can only be used in sync calls through codegen. |
|
|
||
| #### Java | ||
|
|
||
| Java class `java.nio.ByteBuffer` can be constructed using JNI function `jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity)`. This function returns a direct instance of direct `java.nio.ByteBuffer` referring to the block of memory starting at the memory address `address` and extending `capacity` bytes. What is important, direct buffers don't deallocate the memory on destruction, what is desired in our case since JS is responsible for that. |
There was a problem hiding this comment.
Would recommend we use the existing fbjni abstraction here: https://github.com/facebookincubator/fbjni/blob/main/cxx/fbjni/ByteBuffer.cpp#L84
| When passing an `ArrayBuffer` to native code, it should always be treated as "borrowed" or "non-owning": JS owns the ArrayBuffer's memory and the JS GC is responsible for freeing it. Native code should access the passed memory only for the duration of the synchronous call. | ||
|
|
||
| The same rules apply when passing buffers from native to JS: native code remains the owner of the allocated memory and may expose zero-copy buffers to JS. |
There was a problem hiding this comment.
Why is this so? createArrayBuffer uses a shared_ptr so it should be feasible to implemented shared ownership semantics here.
There was a problem hiding this comment.
Agreed - ownership can transfer from native to JS here, that's perfectly fine. We use this in a lot of Nitro libraries.
There was a problem hiding this comment.
I thought that aligning the ownership strategy for both directions would make sense - but of course you are right, it would work fine for case of "native to JS" direction.
mrousavy
left a comment
There was a problem hiding this comment.
I really like this RFC - adding ArrayBuffer to react-native core seems like a no-brainer.
I can see that some of this was inspired by Nitro, where we already have nitro::ArrayBuffer (see the ArrayBuffer docs).
I remember implementing ArrayBuffers wasn't super easy, as a lot of work went into the nitro::ArrayBuffer primitives - I think I learned a lot about how this works - I can help with the implementation if any questions arise.
Nitro has owning and non-owning ArrayBuffers, where owning is a native one (we can unwrap that later again), and non-owning is one that was created in JS, or somewhere else.
PROBLEM: If an owning ArrayBuffer (aka one that was created in native) gets passed to JS, and then back from JS to native, the information that it is a native ArrayBuffer gets lost because we don't have .isMutableBuffer() and .getMutableBuffer() in JSI.
I created a feature request for this here: facebook/hermes#1578 - @tmikov said he thinks it's a good idea - I even created a PoC PR that implements this (facebook/hermes#1733), but it's not fully finished - might need a little bit of help here from the Hermes team.
In Nitro, I have a workaround for this by simply attaching a NativeState of the jsi::MutableBuffer pointer to my object (https://github.com/mrousavy/nitro/blob/cffffcb91f90fe4823fa7410bdb39c51c4c99125/packages/react-native-nitro-modules/cpp/jsi/JSIConverter%2BArrayBuffer.hpp#L81-L83) - works very well 😄
But; this means it would not detect owning ArrayBuffers that were created natively in TurboModules, since TurboModules will likely not use nitro::MutableBufferNativeState lol. Not a biggie, it'll just be non-owning.
We need the Hermes PR for native support for that.
|
|
||
| #### Java | ||
|
|
||
| While in C++ and Objective-C data can be easily shared between JS and Native, Java stores the data as a `folly::dynamic` map on the Native side. The `folly` library has support for data buffers (class `IOBuf`). This means that JS buffers can be stored on the Native side, but its implementation will be more challenging. Moreover, classes responsible for storing variables, such as `NativeMap` or `NatviveArray`, have a rich inheritance tree and are widely used across the JNI files. Adding storage for buffers to them will require changes to a large number of `ReactAndroid` JNI and Java/Kotlin files. These changes are required to add support for e.g. Promises or Structs. |
There was a problem hiding this comment.
While in C++ and Objective-C data can be easily shared between JS and Native, Java stores the data as a
folly::dynamicmap on the Native side.
Yea I found the same issue a while ago - folly is quite cool but wrapping everything in dynamic probably has to go at some point in the future.
AFAIK other implementations (such as Nitro Modules or Expo Modules) has similar approach - ownership is not transferred from JS to Native, but borrowed. From what I see thread safety is either manual or ensured by copying the content (e.g. Expo Blob) - I am not aware of the other solution to this problem - happy to be corrected.
The proposed operational model isn't similar to
Yes, I can see we can do it either way. Would it also be a applicable solution to the "borrowed" vs "moved" discussion? Instead it can be "shared", by keeping JS object alive. It would not eliminate the thread-safety problems, but perhaps could be another solution to the problem. |
|
Thank you all for the feedback and discussion on this RFC! I've updated the proposal. There are two open questions and fundamental design decisions that need input:
I'd appreciate guidance from the core team and community on how to proceed with these questions! |
|
@javache did you maybe have time to take a look again at this RFC and open questions once again? 😊 |
|
Here are my general thoughts on the matter, and unfortunately I see a couple of fundamental problems (sorry, I should have joined here earlier):
Problem 1 has partial workarounds - you can check whether the buffer is resizable and reject it, but you can't prevent it from becoming asynchronously detached. Problem 2 is basically unsolvable. This is why NodeJS uses Buffer - it can fully control it. My recommendation: give up on ArrayBuffer. It simply can never work well because of problem 2. It will always be somewhat of a hack. Use Buffer instead, which is well understood by everybody, compatible with NodeJS, etc, etc. If you folks are dead-set on ArrayBuffer, I think @mrousavy's proposal for |
javache
left a comment
There was a problem hiding this comment.
I'm ok with proceeding here with the constraints @tmikov points out - let's enforce that when we bridge ArrayBuffers originating from JS that these are marked as unmovable (and not supported in async calls) and let's pull in facebook/hermes#1733 to avoid copying data when async scenarios are needed in the native layer.
| processBuffer(buffer: ArrayBuffer): void; | ||
|
|
||
| getAsyncBuffer(): Promise<ArrayBuffer>; | ||
| processAsyncBuffer(buffer: ArrayBuffer): Promise<void>; |
There was a problem hiding this comment.
Let's remove support for this until we have getMutableBuffer and can provide a safe variant of this.
|
|
||
| #### Native-to-JS (Transfer Ownership) | ||
|
|
||
| When passing a buffer from native to JS, ownership can be transferred, and it can be treated as "owned". Native code creates the buffer, wraps it in a shared pointer, and passes it to JS. The JS GC will properly manage the buffer's lifetime, and the buffer will be freed when no longer referenced. |
There was a problem hiding this comment.
In this scenario (of a shared_ptr), ownership is shared through ref counting, not transferred.
Hi @javache, great to hear you're okay with proceeding with the constraints. |
…etween JavaScript and native (facebook#56689) Summary: Pull Request resolved: facebook#56689 ## Changelog: [General] [Added] - Add ArrayBuffer support for React Native Turbo Modules react-native-community/discussions-and-proposals#947 Adds first-class `ArrayBuffer` support across the entire TurboModule pipeline, enabling efficient binary data transfer between JavaScript and native code without base64 encoding overhead. **Schema & Parser:** - Added `NativeModuleArrayBufferTypeAnnotation` to `CodegenSchema.js` and `.d.ts` - Added `emitArrayBuffer` to `parsers-primitives.js` type map **C++ (Bridging & Generator):** - Added `ArrayBufferKind` to `TurboModuleMethodValueKind` enum - Created `ArrayBuffer.h` bridging header with `OwnedMutableBuffer` and `Bridging<std::vector<uint8_t>>` - Added `jsi::ArrayBuffer` conversion operators to `Convert.h` - Mapped `ArrayBufferTypeAnnotation` → `jsi::ArrayBuffer` in `GenerateModuleH.js` **Android (Java/JNI):** - Mapped `ArrayBufferTypeAnnotation` → `java.nio.ByteBuffer` in Java spec generator - Mapped to `Ljava/nio/ByteBuffer;` JNI signature in JNI generator - Added `ByteBuffer` arg/return handling in `JavaTurboModule.cpp` via `NewDirectByteBuffer`/`GetDirectBufferAddress` **iOS (ObjC):** - Mapped `ArrayBufferTypeAnnotation` → `NSData *` in ObjC spec generator - Added ArrayBuffer→NSData conversion in `convertJSIValueToObjCObject()` - Added NSData→ArrayBuffer conversion in `convertReturnIdToJSIValue()` **Flow-Schema:** - Added `ArrayBuffer` to `JAVASCRIPT_BUILTINS` in `BoundaryTypes.js` **Tests:** - Added Flow and TypeScript parser fixtures (`NATIVE_MODULE_WITH_ARRAYBUFFER`) - Added generator schema fixture (`ARRAYBUFFER_MODULE`) - Added `emitArrayBuffer` unit tests Differential Revision: D95649873
…etween JavaScript and native Summary: ## Changelog: [General] [Added] - Add ArrayBuffer support for React Native Turbo Modules react-native-community/discussions-and-proposals#947 Adds first-class `ArrayBuffer` support across the entire TurboModule pipeline, enabling efficient binary data transfer between JavaScript and native code without base64 encoding overhead. **Schema & Parser:** - Added `NativeModuleArrayBufferTypeAnnotation` to `CodegenSchema.js` and `.d.ts` - Added `emitArrayBuffer` to `parsers-primitives.js` type map **C++ (Bridging & Generator):** - Added `ArrayBufferKind` to `TurboModuleMethodValueKind` enum - Created `ArrayBuffer.h` bridging header with `OwnedMutableBuffer` and `Bridging<std::vector<uint8_t>>` - Added `jsi::ArrayBuffer` conversion operators to `Convert.h` - Mapped `ArrayBufferTypeAnnotation` → `jsi::ArrayBuffer` in `GenerateModuleH.js` **Android (Java/JNI):** - Mapped `ArrayBufferTypeAnnotation` → `java.nio.ByteBuffer` in Java spec generator - Mapped to `Ljava/nio/ByteBuffer;` JNI signature in JNI generator - Added `ByteBuffer` arg/return handling in `JavaTurboModule.cpp` via `NewDirectByteBuffer`/`GetDirectBufferAddress` **iOS (ObjC):** - Mapped `ArrayBufferTypeAnnotation` → `NSData *` in ObjC spec generator - Added ArrayBuffer→NSData conversion in `convertJSIValueToObjCObject()` - Added NSData→ArrayBuffer conversion in `convertReturnIdToJSIValue()` **Flow-Schema:** - Added `ArrayBuffer` to `JAVASCRIPT_BUILTINS` in `BoundaryTypes.js` **Tests:** - Added Flow and TypeScript parser fixtures (`NATIVE_MODULE_WITH_ARRAYBUFFER`) - Added generator schema fixture (`ARRAYBUFFER_MODULE`) - Added `emitArrayBuffer` unit tests Differential Revision: D95649873
Summary: Pull Request resolved: facebook#56690 ## Changelog: [General] [Added] - Add ArrayBuffer sample methods to TurboModule examples react-native-community/discussions-and-proposals#947 Add `getArrayBuffer` method to NativeCxxModuleExample and NativeSampleTurboModule specs to demonstrate and validate ArrayBuffer support across all platforms. - **JS Specs:** Added `getArrayBuffer: (arg: ArrayBuffer) => ArrayBuffer` to both NativeCxxModuleExample.js and NativeSampleTurboModule.js - **C++:** Implemented in NativeCxxModuleExample.cpp using `OwnedMutableBuffer` to copy and return the ArrayBuffer - **Java/Kotlin:** Implemented in SampleTurboModule.kt with `ByteBuffer` param/return - **ObjC:** Implemented in RCTSampleTurboModule.mm (iOS + macOS) with `NSData *` param/return Differential Revision: D95652569
…etween JavaScript and native (facebook#56689) Summary: Pull Request resolved: facebook#56689 ## Changelog: [General] [Added] - Add ArrayBuffer support for React Native Turbo Modules react-native-community/discussions-and-proposals#947 Adds first-class `ArrayBuffer` support across the entire TurboModule pipeline, enabling efficient binary data transfer between JavaScript and native code without base64 encoding overhead. **Schema & Parser:** - Added `NativeModuleArrayBufferTypeAnnotation` to `CodegenSchema.js` and `.d.ts` - Added `emitArrayBuffer` to `parsers-primitives.js` type map **C++ (Bridging & Generator):** - Added `ArrayBufferKind` to `TurboModuleMethodValueKind` enum - Created `ArrayBuffer.h` bridging header with `OwnedMutableBuffer` and `Bridging<std::vector<uint8_t>>` - Added `jsi::ArrayBuffer` conversion operators to `Convert.h` - Mapped `ArrayBufferTypeAnnotation` → `jsi::ArrayBuffer` in `GenerateModuleH.js` **Android (Java/JNI):** - Mapped `ArrayBufferTypeAnnotation` → `java.nio.ByteBuffer` in Java spec generator - Mapped to `Ljava/nio/ByteBuffer;` JNI signature in JNI generator - Added `ByteBuffer` arg/return handling in `JavaTurboModule.cpp` via `NewDirectByteBuffer`/`GetDirectBufferAddress` **iOS (ObjC):** - Mapped `ArrayBufferTypeAnnotation` → `NSData *` in ObjC spec generator - Added ArrayBuffer→NSData conversion in `convertJSIValueToObjCObject()` - Added NSData→ArrayBuffer conversion in `convertReturnIdToJSIValue()` **Flow-Schema:** - Added `ArrayBuffer` to `JAVASCRIPT_BUILTINS` in `BoundaryTypes.js` **Tests:** - Added Flow and TypeScript parser fixtures (`NATIVE_MODULE_WITH_ARRAYBUFFER`) - Added generator schema fixture (`ARRAYBUFFER_MODULE`) - Added `emitArrayBuffer` unit tests Differential Revision: D95649873
…etween JavaScript and native Summary: ## Changelog: [General] [Added] - Add ArrayBuffer support for React Native Turbo Modules react-native-community/discussions-and-proposals#947 Adds first-class `ArrayBuffer` support across the entire TurboModule pipeline, enabling efficient binary data transfer between JavaScript and native code without base64 encoding overhead. **Schema & Parser:** - Added `NativeModuleArrayBufferTypeAnnotation` to `CodegenSchema.js` and `.d.ts` - Added `emitArrayBuffer` to `parsers-primitives.js` type map **C++ (Bridging & Generator):** - Added `ArrayBufferKind` to `TurboModuleMethodValueKind` enum - Created `ArrayBuffer.h` bridging header with `OwnedMutableBuffer` and `Bridging<std::vector<uint8_t>>` - Added `jsi::ArrayBuffer` conversion operators to `Convert.h` - Mapped `ArrayBufferTypeAnnotation` → `jsi::ArrayBuffer` in `GenerateModuleH.js` **Android (Java/JNI):** - Mapped `ArrayBufferTypeAnnotation` → `java.nio.ByteBuffer` in Java spec generator - Mapped to `Ljava/nio/ByteBuffer;` JNI signature in JNI generator - Added `ByteBuffer` arg/return handling in `JavaTurboModule.cpp` via `NewDirectByteBuffer`/`GetDirectBufferAddress` **iOS (ObjC):** - Mapped `ArrayBufferTypeAnnotation` → `NSData *` in ObjC spec generator - Added ArrayBuffer→NSData conversion in `convertJSIValueToObjCObject()` - Added NSData→ArrayBuffer conversion in `convertReturnIdToJSIValue()` **Flow-Schema:** - Added `ArrayBuffer` to `JAVASCRIPT_BUILTINS` in `BoundaryTypes.js` **Tests:** - Added Flow and TypeScript parser fixtures (`NATIVE_MODULE_WITH_ARRAYBUFFER`) - Added generator schema fixture (`ARRAYBUFFER_MODULE`) - Added `emitArrayBuffer` unit tests Differential Revision: D95649873
Summary: Pull Request resolved: facebook#56690 ## Changelog: [General] [Added] - Add ArrayBuffer sample methods to TurboModule examples react-native-community/discussions-and-proposals#947 Add `getArrayBuffer` method to NativeCxxModuleExample and NativeSampleTurboModule specs to demonstrate and validate ArrayBuffer support across all platforms. - **JS Specs:** Added `getArrayBuffer: (arg: ArrayBuffer) => ArrayBuffer` to both NativeCxxModuleExample.js and NativeSampleTurboModule.js - **C++:** Implemented in NativeCxxModuleExample.cpp using `OwnedMutableBuffer` to copy and return the ArrayBuffer - **Java/Kotlin:** Implemented in SampleTurboModule.kt with `ByteBuffer` param/return - **ObjC:** Implemented in RCTSampleTurboModule.mm (iOS + macOS) with `NSData *` param/return Differential Revision: D95652569
Summary: ## Changelog: [General] [Added] - Add ArrayBuffer sample methods to TurboModule examples react-native-community/discussions-and-proposals#947 Add `getArrayBuffer` method to NativeCxxModuleExample and NativeSampleTurboModule specs to demonstrate and validate ArrayBuffer support across all platforms. - **JS Specs:** Added `getArrayBuffer: (arg: ArrayBuffer) => ArrayBuffer` to both NativeCxxModuleExample.js and NativeSampleTurboModule.js - **C++:** Implemented in NativeCxxModuleExample.cpp using `OwnedMutableBuffer` to copy and return the ArrayBuffer - **Java/Kotlin:** Implemented in SampleTurboModule.kt with `ByteBuffer` param/return - **ObjC:** Implemented in RCTSampleTurboModule.mm (iOS + macOS) with `NSData *` param/return Differential Revision: D95652569
…etween JavaScript and native Summary: ## Changelog: [General] [Added] - Add ArrayBuffer support for React Native Turbo Modules react-native-community/discussions-and-proposals#947 Adds first-class `ArrayBuffer` support across the entire TurboModule pipeline, enabling efficient binary data transfer between JavaScript and native code without base64 encoding overhead. **Schema & Parser:** - Added `NativeModuleArrayBufferTypeAnnotation` to `CodegenSchema.js` and `.d.ts` - Added `emitArrayBuffer` to `parsers-primitives.js` type map **C++ (Bridging & Generator):** - Added `ArrayBufferKind` to `TurboModuleMethodValueKind` enum - Created `ArrayBuffer.h` bridging header with `OwnedMutableBuffer` and `Bridging<std::vector<uint8_t>>` - Added `jsi::ArrayBuffer` conversion operators to `Convert.h` - Mapped `ArrayBufferTypeAnnotation` → `jsi::ArrayBuffer` in `GenerateModuleH.js` **Android (Java/JNI):** - Mapped `ArrayBufferTypeAnnotation` → `java.nio.ByteBuffer` in Java spec generator - Mapped to `Ljava/nio/ByteBuffer;` JNI signature in JNI generator - Added `ByteBuffer` arg/return handling in `JavaTurboModule.cpp` via `NewDirectByteBuffer`/`GetDirectBufferAddress` **iOS (ObjC):** - Mapped `ArrayBufferTypeAnnotation` → `NSData *` in ObjC spec generator - Added ArrayBuffer→NSData conversion in `convertJSIValueToObjCObject()` - Added NSData→ArrayBuffer conversion in `convertReturnIdToJSIValue()` **Flow-Schema:** - Added `ArrayBuffer` to `JAVASCRIPT_BUILTINS` in `BoundaryTypes.js` **Tests:** - Added Flow and TypeScript parser fixtures (`NATIVE_MODULE_WITH_ARRAYBUFFER`) - Added generator schema fixture (`ARRAYBUFFER_MODULE`) - Added `emitArrayBuffer` unit tests Differential Revision: D95649873
Summary: Pull Request resolved: facebook#56690 ## Changelog: [General] [Added] - Add ArrayBuffer sample methods to TurboModule examples react-native-community/discussions-and-proposals#947 Add `getArrayBuffer` method to NativeCxxModuleExample and NativeSampleTurboModule specs to demonstrate and validate ArrayBuffer support across all platforms. - **JS Specs:** Added `getArrayBuffer: (arg: ArrayBuffer) => ArrayBuffer` to both NativeCxxModuleExample.js and NativeSampleTurboModule.js - **C++:** Implemented in NativeCxxModuleExample.cpp using `OwnedMutableBuffer` to copy and return the ArrayBuffer - **Java/Kotlin:** Implemented in SampleTurboModule.kt with `ByteBuffer` param/return - **ObjC:** Implemented in RCTSampleTurboModule.mm (iOS + macOS) with `NSData *` param/return Reviewed By: javache Differential Revision: D95652569
…etween JavaScript and native (facebook#56689) Summary: Pull Request resolved: facebook#56689 ## Changelog: [General] [Added] - Add ArrayBuffer support for React Native Turbo Modules react-native-community/discussions-and-proposals#947 Adds first-class `ArrayBuffer` support across the entire TurboModule pipeline, enabling efficient binary data transfer between JavaScript and native code without base64 encoding overhead. **Schema & Parser:** - Added `NativeModuleArrayBufferTypeAnnotation` to `CodegenSchema.js` and `.d.ts` - Added `emitArrayBuffer` to `parsers-primitives.js` type map **C++ (Bridging & Generator):** - Added `ArrayBufferKind` to `TurboModuleMethodValueKind` enum - Created `ArrayBuffer.h` bridging header with `OwnedMutableBuffer` and `Bridging<std::vector<uint8_t>>` - Added `jsi::ArrayBuffer` conversion operators to `Convert.h` - Mapped `ArrayBufferTypeAnnotation` → `jsi::ArrayBuffer` in `GenerateModuleH.js` **Android (Java/JNI):** - Mapped `ArrayBufferTypeAnnotation` → `java.nio.ByteBuffer` in Java spec generator - Mapped to `Ljava/nio/ByteBuffer;` JNI signature in JNI generator - Added `ByteBuffer` arg/return handling in `JavaTurboModule.cpp` via `NewDirectByteBuffer`/`GetDirectBufferAddress` **iOS (ObjC):** - Mapped `ArrayBufferTypeAnnotation` → `NSData *` in ObjC spec generator - Added ArrayBuffer→NSData conversion in `convertJSIValueToObjCObject()` - Added NSData→ArrayBuffer conversion in `convertReturnIdToJSIValue()` **Flow-Schema:** - Added `ArrayBuffer` to `JAVASCRIPT_BUILTINS` in `BoundaryTypes.js` **Tests:** - Added Flow and TypeScript parser fixtures (`NATIVE_MODULE_WITH_ARRAYBUFFER`) - Added generator schema fixture (`ARRAYBUFFER_MODULE`) - Added `emitArrayBuffer` unit tests Differential Revision: D95649873
…etween JavaScript and native Summary: ## Changelog: [General] [Added] - Add ArrayBuffer support for React Native Turbo Modules react-native-community/discussions-and-proposals#947 Adds first-class `ArrayBuffer` support across the entire TurboModule pipeline, enabling efficient binary data transfer between JavaScript and native code without base64 encoding overhead. **Schema & Parser:** - Added `NativeModuleArrayBufferTypeAnnotation` to `CodegenSchema.js` and `.d.ts` - Added `emitArrayBuffer` to `parsers-primitives.js` type map **C++ (Bridging & Generator):** - Added `ArrayBufferKind` to `TurboModuleMethodValueKind` enum - Created `ArrayBuffer.h` bridging header with `OwnedMutableBuffer` and `Bridging<std::vector<uint8_t>>` - Added `jsi::ArrayBuffer` conversion operators to `Convert.h` - Mapped `ArrayBufferTypeAnnotation` → `jsi::ArrayBuffer` in `GenerateModuleH.js` **Android (Java/JNI):** - Mapped `ArrayBufferTypeAnnotation` → `java.nio.ByteBuffer` in Java spec generator - Mapped to `Ljava/nio/ByteBuffer;` JNI signature in JNI generator - Added `ByteBuffer` arg/return handling in `JavaTurboModule.cpp` via `NewDirectByteBuffer`/`GetDirectBufferAddress` **iOS (ObjC):** - Mapped `ArrayBufferTypeAnnotation` → `NSData *` in ObjC spec generator - Added ArrayBuffer→NSData conversion in `convertJSIValueToObjCObject()` - Added NSData→ArrayBuffer conversion in `convertReturnIdToJSIValue()` **Flow-Schema:** - Added `ArrayBuffer` to `JAVASCRIPT_BUILTINS` in `BoundaryTypes.js` **Tests:** - Added Flow and TypeScript parser fixtures (`NATIVE_MODULE_WITH_ARRAYBUFFER`) - Added generator schema fixture (`ARRAYBUFFER_MODULE`) - Added `emitArrayBuffer` unit tests Differential Revision: D95649873
Summary: Pull Request resolved: facebook#56690 ## Changelog: [General] [Added] - Add ArrayBuffer sample methods to TurboModule examples react-native-community/discussions-and-proposals#947 Add `getArrayBuffer` method to NativeCxxModuleExample and NativeSampleTurboModule specs to demonstrate and validate ArrayBuffer support across all platforms. - **JS Specs:** Added `getArrayBuffer: (arg: ArrayBuffer) => ArrayBuffer` to both NativeCxxModuleExample.js and NativeSampleTurboModule.js - **C++:** Implemented in NativeCxxModuleExample.cpp using `OwnedMutableBuffer` to copy and return the ArrayBuffer - **Java/Kotlin:** Implemented in SampleTurboModule.kt with `ByteBuffer` param/return - **ObjC:** Implemented in RCTSampleTurboModule.mm (iOS + macOS) with `NSData *` param/return Reviewed By: javache Differential Revision: D95652569
Summary: Pull Request resolved: facebook#56690 ## Changelog: [General] [Added] - Add ArrayBuffer sample methods to TurboModule examples react-native-community/discussions-and-proposals#947 Add `getArrayBuffer` method to NativeCxxModuleExample and NativeSampleTurboModule specs to demonstrate and validate ArrayBuffer support across all platforms. - **JS Specs:** Added `getArrayBuffer: (arg: ArrayBuffer) => ArrayBuffer` to both NativeCxxModuleExample.js and NativeSampleTurboModule.js - **C++:** Implemented in NativeCxxModuleExample.cpp using `OwnedMutableBuffer` to copy and return the ArrayBuffer - **Java/Kotlin:** Implemented in SampleTurboModule.kt with `ByteBuffer` param/return - **ObjC:** Implemented in RCTSampleTurboModule.mm (iOS + macOS) with `NSData *` param/return Reviewed By: javache Differential Revision: D95652569
…etween JavaScript and native (facebook#56689) Summary: Pull Request resolved: facebook#56689 ## Changelog: [General] [Added] - Add ArrayBuffer support for React Native Turbo Modules react-native-community/discussions-and-proposals#947 Adds first-class `ArrayBuffer` support across the entire TurboModule pipeline, enabling efficient binary data transfer between JavaScript and native code without base64 encoding overhead. **Schema & Parser:** - Added `NativeModuleArrayBufferTypeAnnotation` to `CodegenSchema.js` and `.d.ts` - Added `emitArrayBuffer` to `parsers-primitives.js` type map **C++ (Bridging & Generator):** - Added `ArrayBufferKind` to `TurboModuleMethodValueKind` enum - Created `ArrayBuffer.h` bridging header with `OwnedMutableBuffer` and `Bridging<std::vector<uint8_t>>` - Added `jsi::ArrayBuffer` conversion operators to `Convert.h` - Mapped `ArrayBufferTypeAnnotation` → `jsi::ArrayBuffer` in `GenerateModuleH.js` **Android (Java/JNI):** - Mapped `ArrayBufferTypeAnnotation` → `java.nio.ByteBuffer` in Java spec generator - Mapped to `Ljava/nio/ByteBuffer;` JNI signature in JNI generator - Added `ByteBuffer` arg/return handling in `JavaTurboModule.cpp` via `NewDirectByteBuffer`/`GetDirectBufferAddress` **iOS (ObjC):** - Mapped `ArrayBufferTypeAnnotation` → `NSData *` in ObjC spec generator - Added ArrayBuffer→NSData conversion in `convertJSIValueToObjCObject()` - Added NSData→ArrayBuffer conversion in `convertReturnIdToJSIValue()` **Flow-Schema:** - Added `ArrayBuffer` to `JAVASCRIPT_BUILTINS` in `BoundaryTypes.js` **Tests:** - Added Flow and TypeScript parser fixtures (`NATIVE_MODULE_WITH_ARRAYBUFFER`) - Added generator schema fixture (`ARRAYBUFFER_MODULE`) - Added `emitArrayBuffer` unit tests Differential Revision: D95649873
Summary: Pull Request resolved: facebook#56690 ## Changelog: [General] [Added] - Add ArrayBuffer sample methods to TurboModule examples react-native-community/discussions-and-proposals#947 Add `getArrayBuffer` method to NativeCxxModuleExample and NativeSampleTurboModule specs to demonstrate and validate ArrayBuffer support across all platforms. - **JS Specs:** Added `getArrayBuffer: (arg: ArrayBuffer) => ArrayBuffer` to both NativeCxxModuleExample.js and NativeSampleTurboModule.js - **C++:** Implemented in NativeCxxModuleExample.cpp using `OwnedMutableBuffer` to copy and return the ArrayBuffer - **Java/Kotlin:** Implemented in SampleTurboModule.kt with `ByteBuffer` param/return - **ObjC:** Implemented in RCTSampleTurboModule.mm (iOS + macOS) with `NSData *` param/return Reviewed By: javache Differential Revision: D95652569
…etween JavaScript and native (facebook#56689) Summary: Pull Request resolved: facebook#56689 ## Changelog: [General] [Added] - Add ArrayBuffer support for React Native Turbo Modules react-native-community/discussions-and-proposals#947 Adds first-class `ArrayBuffer` support across the entire TurboModule pipeline, enabling efficient binary data transfer between JavaScript and native code without base64 encoding overhead. **Schema & Parser:** - Added `NativeModuleArrayBufferTypeAnnotation` to `CodegenSchema.js` and `.d.ts` - Added `emitArrayBuffer` to `parsers-primitives.js` type map **C++ (Bridging & Generator):** - Added `ArrayBufferKind` to `TurboModuleMethodValueKind` enum - Created `ArrayBuffer.h` bridging header with `OwnedMutableBuffer` and `Bridging<std::vector<uint8_t>>` - Added `jsi::ArrayBuffer` conversion operators to `Convert.h` - Mapped `ArrayBufferTypeAnnotation` → `jsi::ArrayBuffer` in `GenerateModuleH.js` **Android (Java/JNI):** - Mapped `ArrayBufferTypeAnnotation` → `java.nio.ByteBuffer` in Java spec generator - Mapped to `Ljava/nio/ByteBuffer;` JNI signature in JNI generator - Added `ByteBuffer` arg/return handling in `JavaTurboModule.cpp` via `NewDirectByteBuffer`/`GetDirectBufferAddress` **iOS (ObjC):** - Mapped `ArrayBufferTypeAnnotation` → `NSData *` in ObjC spec generator - Added ArrayBuffer→NSData conversion in `convertJSIValueToObjCObject()` - Added NSData→ArrayBuffer conversion in `convertReturnIdToJSIValue()` **Flow-Schema:** - Added `ArrayBuffer` to `JAVASCRIPT_BUILTINS` in `BoundaryTypes.js` **Tests:** - Added Flow and TypeScript parser fixtures (`NATIVE_MODULE_WITH_ARRAYBUFFER`) - Added generator schema fixture (`ARRAYBUFFER_MODULE`) - Added `emitArrayBuffer` unit tests Differential Revision: D95649873
…etween JavaScript and native Summary: ## Changelog: [General] [Added] - Add ArrayBuffer support for React Native Turbo Modules react-native-community/discussions-and-proposals#947 Adds first-class `ArrayBuffer` support across the entire TurboModule pipeline, enabling efficient binary data transfer between JavaScript and native code without base64 encoding overhead. **Schema & Parser:** - Added `NativeModuleArrayBufferTypeAnnotation` to `CodegenSchema.js` and `.d.ts` - Added `emitArrayBuffer` to `parsers-primitives.js` type map **C++ (Bridging & Generator):** - Added `ArrayBufferKind` to `TurboModuleMethodValueKind` enum - Created `ArrayBuffer.h` bridging header with `OwnedMutableBuffer` and `Bridging<std::vector<uint8_t>>` - Added `jsi::ArrayBuffer` conversion operators to `Convert.h` - Mapped `ArrayBufferTypeAnnotation` → `jsi::ArrayBuffer` in `GenerateModuleH.js` **Android (Java/JNI):** - Mapped `ArrayBufferTypeAnnotation` → `java.nio.ByteBuffer` in Java spec generator - Mapped to `Ljava/nio/ByteBuffer;` JNI signature in JNI generator - Added `ByteBuffer` arg/return handling in `JavaTurboModule.cpp` via `NewDirectByteBuffer`/`GetDirectBufferAddress` **iOS (ObjC):** - Mapped `ArrayBufferTypeAnnotation` → `NSData *` in ObjC spec generator - Added ArrayBuffer→NSData conversion in `convertJSIValueToObjCObject()` - Added NSData→ArrayBuffer conversion in `convertReturnIdToJSIValue()` **Flow-Schema:** - Added `ArrayBuffer` to `JAVASCRIPT_BUILTINS` in `BoundaryTypes.js` **Tests:** - Added Flow and TypeScript parser fixtures (`NATIVE_MODULE_WITH_ARRAYBUFFER`) - Added generator schema fixture (`ARRAYBUFFER_MODULE`) - Added `emitArrayBuffer` unit tests Differential Revision: D95649873
Summary: Pull Request resolved: facebook#56690 ## Changelog: [General] [Added] - Add ArrayBuffer sample methods to TurboModule examples react-native-community/discussions-and-proposals#947 Add `getArrayBuffer` method to NativeCxxModuleExample and NativeSampleTurboModule specs to demonstrate and validate ArrayBuffer support across all platforms. - **JS Specs:** Added `getArrayBuffer: (arg: ArrayBuffer) => ArrayBuffer` to both NativeCxxModuleExample.js and NativeSampleTurboModule.js - **C++:** Implemented in NativeCxxModuleExample.cpp using `OwnedMutableBuffer` to copy and return the ArrayBuffer - **Java/Kotlin:** Implemented in SampleTurboModule.kt with `ByteBuffer` param/return - **ObjC:** Implemented in RCTSampleTurboModule.mm (iOS + macOS) with `NSData *` param/return Reviewed By: javache Differential Revision: D95652569
… between JavaScript and native (facebook#56689) Summary: Pull Request resolved: facebook#56689 ## Changelog: [General] [Added] - Add ArrayBuffer support for React Native Turbo Modules react-native-community/discussions-and-proposals#947 Adds first-class `ArrayBuffer` support across the entire TurboModule pipeline, enabling efficient binary data transfer between JavaScript and native code. Memory is shared directly where lifetimes allow (zero-copy on sync calls and on native→JS returns); copies happen only when JS ownership cannot be guaranteed (async parameters that may outlive the JS `ArrayBuffer` they were derived from). **Schema & Parser:** - Added `NativeModuleArrayBufferTypeAnnotation` to `CodegenSchema.js` and `.d.ts` - Added `emitArrayBuffer` to `parsers-primitives.js` type map **C++ (Bridging & Generator):** - Added `ArrayBufferKind` to `TurboModuleMethodValueKind` enum - Created `ArrayBuffer.h` with two `jsi::MutableBuffer` implementations: `SharedMutableBuffer` (non-owning, zero-copy, with an optional release callback to drop a prevent-GC reference such as a JNI global ref or an ARC-retained ObjC object) and `VectorMutableBuffer` (owning copy, used when the source bytes' lifetime cannot be guaranteed) - Added `jsi::ArrayBuffer` conversion operators to `Convert.h` - Mapped `ArrayBufferTypeAnnotation` → `jsi::ArrayBuffer` in `GenerateModuleH.js` **Android (Java/JNI):** - Mapped `ArrayBufferTypeAnnotation` → `java.nio.ByteBuffer` in Java spec generator - Mapped to `Ljava/nio/ByteBuffer;` JNI signature in JNI generator - Added `JByteBufferMutableBuffer` in `JavaTurboModule.cpp`: holds a JNI global ref to a direct `ByteBuffer`, pinning its memory for the lifetime of the JS `ArrayBuffer`. The destructor attaches the current thread before releasing the global ref, so it is safe to be invoked on any thread by the JS GC. - Sync params wrap the JS-owned memory with `JByteBuffer::wrapBytes` (zero-copy). Async params (`VoidKind` / `PromiseKind` return kinds) allocate a Java-owned direct `ByteBuffer` via `JByteBuffer::allocateDirect` and `memcpy` the bytes in, because the JS `ArrayBuffer` may be GC'd before Java reads it. - Native→JS returns wrap the returned direct `ByteBuffer` with `JByteBufferMutableBuffer` for zero-copy **iOS (ObjC):** - Mapped `ArrayBufferTypeAnnotation` → `NSMutableData *` in ObjC spec generator - Added `NSMutableDataMutableBuffer` in `RCTTurboModule.mm`: holds a strong (ARC) reference to an `NSMutableData`, keeping the bytes alive for the JS `ArrayBuffer`'s lifetime (zero-copy returns) - Sync params wrap the JS-owned bytes with `+[NSMutableData dataWithBytesNoCopy:length:freeWhenDone:NO]` (zero-copy). Async params copy via `+[NSMutableData dataWithBytes:length:]` because the JS `ArrayBuffer` may be GC'd before the module reads it. - Added ArrayBuffer→`NSMutableData` conversion in `convertJSIValueToObjCObject()` and `NSMutableData`→ArrayBuffer conversion in `convertReturnIdToJSIValue()` **Flow-Schema:** - Added `ArrayBuffer` to `JAVASCRIPT_BUILTINS` in `BoundaryTypes.js` **Tests:** - Added Flow and TypeScript parser fixtures (`NATIVE_MODULE_WITH_ARRAYBUFFER`) - Added generator schema fixture (`ARRAYBUFFER_MODULE`) - Added `emitArrayBuffer` unit tests Differential Revision: D95649873
Proposal: Adding first-class
ArrayBuffersupport to Codegen and TurboModules to enable zero-copy binary data exchange between JavaScript and Native modules.View the rendered RFC