Skip to content

Commit 226ef2e

Browse files
paradowstackmeta-codesync[bot]
authored andcommitted
ArrayBuffer support to C++ TurboModules (#56729)
Summary: Adds `ArrayBuffer` support to C++ TurboModules. This includes: - Codegen support for `ArrayBufferTypeAnnotation` in module specs - `AsyncArrayBuffer` — a move-only bridging type for holding ArrayBuffer data off the JS thread - `Bridging<AsyncArrayBuffer>::toJs` for resolving promises with native-backed or owned-bytes buffers iOS and Android support will follow in separate PRs. The new codegen type is currently restricted to C++ TurboModules via `excludedPlatforms: ['iOS', 'android']`. ## Changelog: [GENERAL] [ADDED] - Add `ArrayBuffer` support to C++ TurboModules Pull Request resolved: #56729 Test Plan: - JS parser/generators tests added. - New bridging unit tests in `BridgingTest.cpp` cover `AsyncArrayBuffer` construction and JS bridging. - `rn-tester`: `getArrayBuffer`, `processAsyncBuffer`, and `getAsyncBuffer` methods added to `NativeCxxModuleExample` in demo app. ``` [chpurrer@54913.od /data/sandcastle/boxes/fbsource (a00a9a84e7)]$ buck test //xplat/js/react-native-github/packages/react-native/ReactCommon/react/bridging:tests ↷ Skip: fbsource//xplat/js/react-native-github/packages/react-native/ReactCommon/react/bridging:tests - BridgingTest.asyncArrayBufferBorrowNativeBackedTest (0.0s) Note: Google Test filter = BridgingTest.asyncArrayBufferBorrowNativeBackedTest [==========] Running 1 test from 1 test suite. [----------] Global test environment set-up. [----------] 1 test from BridgingTest [ RUN ] BridgingTest.asyncArrayBufferBorrowNativeBackedTest xplat/js/react-native-github/packages/react-native/ReactCommon/react/bridging/tests/BridgingTest.cpp:865: Skipped Runtime does not expose tryGetMutableBuffer; borrow always throws [ SKIPPED ] BridgingTest.asyncArrayBufferBorrowNativeBackedTest (7 ms) [----------] 1 test from BridgingTest (7 ms total) [----------] Global test environment tear-down [==========] 1 test from 1 test suite ran. (7 ms total) [ PASSED ] 0 tests. [ SKIPPED ] 1 test, listed below: [ SKIPPED ] BridgingTest.asyncArrayBufferBorrowNativeBackedTest Buck UI: https://www.internalfb.com/buck2/4a7fa180-4ce6-4dcf-b685-45d183bd4091 Test UI: https://www.internalfb.com/intern/testinfra/testrun/30680772470375729 Network: Up: 0B Down: 0B (reSessionID-9a7e839f-b20c-4d94-944f-8d1720e694f8) Command: test. Time elapsed: 3.0s Buck UI: https://www.internalfb.com/buck2/4a7fa180-4ce6-4dcf-b685-45d183bd4091 Tests finished: Pass 31. Fail 0. Timeout 0. Fatal 0. Skip 1. Omit 0. Infra Failure 0. Build failure 0 [chpurrer@54913.od /data/sandcastle/boxes/fbsource (a00a9a84e7)]$ ``` ## RNTester-ios ``` buck install -r rntester-ios ``` https://pxl.cl/9TgTf Reviewed By: javache Differential Revision: D105629442 Pulled By: christophpurrer fbshipit-source-id: 91f6e3d16f69ba792469e02d6e539794e70b7101
1 parent 13ab72a commit 226ef2e

48 files changed

Lines changed: 1552 additions & 21 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/react-native-codegen/src/CodegenSchema.d.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ export interface VoidTypeAnnotation {
4646
readonly type: 'VoidTypeAnnotation';
4747
}
4848

49+
export interface ArrayBufferTypeAnnotation {
50+
readonly type: 'ArrayBufferTypeAnnotation';
51+
}
52+
4953
export interface BooleanLiteralTypeAnnotation {
5054
readonly type: 'BooleanLiteralTypeAnnotation';
5155
readonly value: boolean;
@@ -417,7 +421,8 @@ export type NativeModuleEventEmitterTypeAnnotation =
417421
| ArrayTypeAnnotation<NativeModuleEventEmitterBaseTypeAnnotation>;
418422

419423
export type NativeModuleBaseTypeAnnotation =
420-
NativeModuleStringTypeAnnotation
424+
ArrayBufferTypeAnnotation
425+
| NativeModuleStringTypeAnnotation
421426
| StringLiteralTypeAnnotation
422427
| StringLiteralUnionTypeAnnotation
423428
| NativeModuleNumberTypeAnnotation

packages/react-native-codegen/src/CodegenSchema.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ export type VoidTypeAnnotation = Readonly<{
6868
type: 'VoidTypeAnnotation',
6969
}>;
7070

71+
export type ArrayBufferTypeAnnotation = Readonly<{
72+
type: 'ArrayBufferTypeAnnotation',
73+
}>;
74+
7175
export type ObjectTypeAnnotation<+T> = Readonly<{
7276
type: 'ObjectTypeAnnotation',
7377
properties: ReadonlyArray<NamedShape<T>>,
@@ -411,6 +415,7 @@ export type NativeModuleEventEmitterTypeAnnotation =
411415
| ArrayTypeAnnotation<NativeModuleEventEmitterBaseTypeAnnotation>;
412416

413417
export type NativeModuleBaseTypeAnnotation =
418+
| ArrayBufferTypeAnnotation
414419
| StringTypeAnnotation
415420
| StringLiteralTypeAnnotation
416421
| StringLiteralUnionTypeAnnotation

packages/react-native-codegen/src/generators/modules/GenerateModuleH.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ function serializeArg(
146146
return wrap(val => `${val}.asObject(rt)`);
147147
case 'MixedTypeAnnotation':
148148
return wrap(val => `jsi::Value(rt, ${val})`);
149+
case 'ArrayBufferTypeAnnotation':
150+
return wrap(val => `${val}.asObject(rt).getArrayBuffer(rt)`);
149151
default:
150152
realTypeAnnotation.type as empty;
151153
throw new Error(
@@ -307,6 +309,8 @@ function translatePrimitiveJSTypeToCpp(
307309
return wrapOptional('jsi::Value', isRequired);
308310
case 'MixedTypeAnnotation':
309311
return wrapOptional('jsi::Value', isRequired);
312+
case 'ArrayBufferTypeAnnotation':
313+
return wrapOptional('jsi::ArrayBuffer', isRequired);
310314
default:
311315
realTypeAnnotation.type as empty;
312316
throw new Error(createErrorMessage(realTypeAnnotation.type));

packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,10 @@ function translateFunctionParamToJavaType(
267267
case 'FunctionTypeAnnotation':
268268
imports.add('com.facebook.react.bridge.Callback');
269269
return wrapOptional('Callback', isRequired);
270+
case 'ArrayBufferTypeAnnotation':
271+
throw new Error(
272+
`${createErrorMessage(realTypeAnnotation.type)} ArrayBuffer is only supported for C++ TurboModules.`,
273+
);
270274
default:
271275
realTypeAnnotation.type as 'MixedTypeAnnotation';
272276
throw new Error(createErrorMessage(realTypeAnnotation.type));
@@ -361,6 +365,10 @@ function translateFunctionReturnTypeToJavaType(
361365
case 'ArrayTypeAnnotation':
362366
imports.add('com.facebook.react.bridge.WritableArray');
363367
return wrapOptional('WritableArray', isRequired);
368+
case 'ArrayBufferTypeAnnotation':
369+
throw new Error(
370+
`${createErrorMessage(realTypeAnnotation.type)} ArrayBuffer is only supported for C++ TurboModules.`,
371+
);
364372
default:
365373
realTypeAnnotation.type as 'MixedTypeAnnotation';
366374
throw new Error(createErrorMessage(realTypeAnnotation.type));
@@ -443,6 +451,10 @@ function getFalsyReturnStatementFromReturnType(
443451
return 'return null;';
444452
case 'ArrayTypeAnnotation':
445453
return 'return null;';
454+
case 'ArrayBufferTypeAnnotation':
455+
throw new Error(
456+
`${createErrorMessage(realTypeAnnotation.type)} ArrayBuffer is only supported for C++ TurboModules.`,
457+
);
446458
default:
447459
realTypeAnnotation.type as 'MixedTypeAnnotation';
448460
throw new Error(createErrorMessage(realTypeAnnotation.type));

packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,8 @@ function translateReturnTypeToKind(
216216
return 'ObjectKind';
217217
case 'ArrayTypeAnnotation':
218218
return 'ArrayKind';
219+
case 'ArrayBufferTypeAnnotation':
220+
throw new Error('ArrayBuffer is only supported for C++ TurboModules.');
219221
default:
220222
realTypeAnnotation.type as 'MixedTypeAnnotation';
221223
throw new Error(
@@ -303,6 +305,8 @@ function translateParamTypeToJniType(
303305
return 'Lcom/facebook/react/bridge/ReadableArray;';
304306
case 'FunctionTypeAnnotation':
305307
return 'Lcom/facebook/react/bridge/Callback;';
308+
case 'ArrayBufferTypeAnnotation':
309+
throw new Error('ArrayBuffer is only supported for C++ TurboModules.');
306310
default:
307311
realTypeAnnotation.type as 'MixedTypeAnnotation';
308312
throw new Error(
@@ -387,6 +391,8 @@ function translateReturnTypeToJniType(
387391
return 'Lcom/facebook/react/bridge/WritableMap;';
388392
case 'ArrayTypeAnnotation':
389393
return 'Lcom/facebook/react/bridge/WritableArray;';
394+
case 'ArrayBufferTypeAnnotation':
395+
throw new Error('ArrayBuffer is only supported for C++ TurboModules.');
390396
default:
391397
realTypeAnnotation.type as 'MixedTypeAnnotation';
392398
throw new Error(

packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/StructCollector.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ class StructCollector {
132132
return wrapNullable(nullable, typeAnnotation);
133133
case 'MixedTypeAnnotation':
134134
throw new Error('Mixed types are unsupported in structs');
135+
case 'ArrayBufferTypeAnnotation':
136+
throw new Error(
137+
'ArrayBuffer is unsupported in TurboModule struct types.',
138+
);
135139
case 'UnionTypeAnnotation':
136140
try {
137141
const validUnionType = parseValidUnionType(typeAnnotation);

packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,10 @@ function getReturnObjCType(
387387
}
388388
case 'GenericObjectTypeAnnotation':
389389
return wrapOptional('NSDictionary *', isRequired);
390+
case 'ArrayBufferTypeAnnotation':
391+
throw new Error(
392+
`Unsupported return type for ${methodName}: ArrayBuffer is only supported for C++ TurboModules.`,
393+
);
390394
default:
391395
typeAnnotation.type as 'MixedTypeAnnotation';
392396
throw new Error(
@@ -459,6 +463,10 @@ function getReturnJSType(
459463
validUnionType as empty;
460464
throw new Error(`Unsupported union member types`);
461465
}
466+
case 'ArrayBufferTypeAnnotation':
467+
throw new Error(
468+
`Unsupported return type for ${methodName}: ArrayBuffer is only supported for C++ TurboModules.`,
469+
);
462470
default:
463471
typeAnnotation.type as 'MixedTypeAnnotation';
464472
throw new Error(

packages/react-native-codegen/src/generators/modules/__test_fixtures__/fixtures.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2600,6 +2600,89 @@ const CXX_ONLY_NATIVE_MODULES: SchemaType = {
26002600
},
26012601
};
26022602

2603+
const ARRAY_BUFFER_NATIVE_MODULE: SchemaType = {
2604+
modules: {
2605+
NativeSampleTurboModule: {
2606+
type: 'NativeModule',
2607+
aliasMap: {},
2608+
enumMap: {},
2609+
spec: {
2610+
eventEmitters: [],
2611+
methods: [
2612+
{
2613+
name: 'getArrayBuffer',
2614+
optional: false,
2615+
typeAnnotation: {
2616+
type: 'FunctionTypeAnnotation',
2617+
returnTypeAnnotation: {
2618+
type: 'ArrayBufferTypeAnnotation',
2619+
},
2620+
params: [],
2621+
},
2622+
},
2623+
{
2624+
name: 'voidArrayBuffer',
2625+
optional: false,
2626+
typeAnnotation: {
2627+
type: 'FunctionTypeAnnotation',
2628+
returnTypeAnnotation: {
2629+
type: 'VoidTypeAnnotation',
2630+
},
2631+
params: [
2632+
{
2633+
name: 'arg',
2634+
optional: false,
2635+
typeAnnotation: {
2636+
type: 'ArrayBufferTypeAnnotation',
2637+
},
2638+
},
2639+
],
2640+
},
2641+
},
2642+
{
2643+
name: 'voidNullableArrayBuffer',
2644+
optional: false,
2645+
typeAnnotation: {
2646+
type: 'FunctionTypeAnnotation',
2647+
returnTypeAnnotation: {
2648+
type: 'VoidTypeAnnotation',
2649+
},
2650+
params: [
2651+
{
2652+
name: 'arg',
2653+
optional: false,
2654+
typeAnnotation: {
2655+
type: 'NullableTypeAnnotation',
2656+
typeAnnotation: {
2657+
type: 'ArrayBufferTypeAnnotation',
2658+
},
2659+
},
2660+
},
2661+
],
2662+
},
2663+
},
2664+
{
2665+
name: 'promiseArrayBuffer',
2666+
optional: false,
2667+
typeAnnotation: {
2668+
type: 'FunctionTypeAnnotation',
2669+
returnTypeAnnotation: {
2670+
type: 'PromiseTypeAnnotation',
2671+
elementType: {
2672+
type: 'ArrayBufferTypeAnnotation',
2673+
},
2674+
},
2675+
params: [],
2676+
},
2677+
},
2678+
],
2679+
},
2680+
moduleName: 'SampleTurboModule',
2681+
excludedPlatforms: ['iOS', 'android'],
2682+
},
2683+
},
2684+
};
2685+
26032686
const SAMPLE_WITH_UPPERCASE_NAME: SchemaType = {
26042687
modules: {
26052688
NativeSampleTurboModule: {
@@ -2793,6 +2876,7 @@ const STRING_LITERALS: SchemaType = {
27932876
};
27942877

27952878
module.exports = {
2879+
array_buffer_native_module: ARRAY_BUFFER_NATIVE_MODULE,
27962880
complex_objects: COMPLEX_OBJECTS,
27972881
two_modules_different_files: TWO_MODULES_DIFFERENT_FILES,
27982882
empty_native_modules: EMPTY_NATIVE_MODULES,

packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleH-test.js.snap

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,75 @@ private:
3838
}
3939
`;
4040

41+
exports[`GenerateModuleH can generate fixture array_buffer_native_module 1`] = `
42+
Map {
43+
"array_buffer_native_moduleJSI.h" => "/**
44+
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
45+
*
46+
* Do not edit this file as changes may cause incorrect behavior and will be lost
47+
* once the code is regenerated.
48+
*
49+
* @generated by codegen project: GenerateModuleH.js
50+
*/
51+
52+
#pragma once
53+
54+
#include <ReactCommon/TurboModule.h>
55+
#include <react/bridging/Bridging.h>
56+
57+
namespace facebook::react {
58+
59+
60+
template <typename T>
61+
class JSI_EXPORT NativeSampleTurboModuleCxxSpec : public TurboModule {
62+
public:
63+
static constexpr std::string_view kModuleName = \\"SampleTurboModule\\";
64+
65+
protected:
66+
NativeSampleTurboModuleCxxSpec(std::shared_ptr<CallInvoker> jsInvoker) : TurboModule(std::string{NativeSampleTurboModuleCxxSpec::kModuleName}, jsInvoker) {
67+
methodMap_[\\"getArrayBuffer\\"] = MethodMetadata {.argCount = 0, .invoker = __getArrayBuffer};
68+
methodMap_[\\"voidArrayBuffer\\"] = MethodMetadata {.argCount = 1, .invoker = __voidArrayBuffer};
69+
methodMap_[\\"voidNullableArrayBuffer\\"] = MethodMetadata {.argCount = 1, .invoker = __voidNullableArrayBuffer};
70+
methodMap_[\\"promiseArrayBuffer\\"] = MethodMetadata {.argCount = 0, .invoker = __promiseArrayBuffer};
71+
}
72+
73+
private:
74+
static jsi::Value __getArrayBuffer(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* /*args*/, size_t /*count*/) {
75+
static_assert(
76+
bridging::getParameterCount(&T::getArrayBuffer) == 1,
77+
\\"Expected getArrayBuffer(...) to have 1 parameters\\");
78+
return bridging::callFromJs<jsi::ArrayBuffer>(rt, &T::getArrayBuffer, static_cast<NativeSampleTurboModuleCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule));
79+
}
80+
81+
static jsi::Value __voidArrayBuffer(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
82+
static_assert(
83+
bridging::getParameterCount(&T::voidArrayBuffer) == 2,
84+
\\"Expected voidArrayBuffer(...) to have 2 parameters\\");
85+
bridging::callFromJs<void>(rt, &T::voidArrayBuffer, static_cast<NativeSampleTurboModuleCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule),
86+
count <= 0 ? throw jsi::JSError(rt, \\"Expected argument in position 0 to be passed\\") : args[0].asObject(rt).getArrayBuffer(rt));return jsi::Value::undefined();
87+
}
88+
89+
static jsi::Value __voidNullableArrayBuffer(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
90+
static_assert(
91+
bridging::getParameterCount(&T::voidNullableArrayBuffer) == 2,
92+
\\"Expected voidNullableArrayBuffer(...) to have 2 parameters\\");
93+
bridging::callFromJs<void>(rt, &T::voidNullableArrayBuffer, static_cast<NativeSampleTurboModuleCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule),
94+
count <= 0 || args[0].isNull() || args[0].isUndefined() ? std::nullopt : std::make_optional(args[0].asObject(rt).getArrayBuffer(rt)));return jsi::Value::undefined();
95+
}
96+
97+
static jsi::Value __promiseArrayBuffer(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* /*args*/, size_t /*count*/) {
98+
static_assert(
99+
bridging::getParameterCount(&T::promiseArrayBuffer) == 1,
100+
\\"Expected promiseArrayBuffer(...) to have 1 parameters\\");
101+
return bridging::callFromJs<jsi::Value>(rt, &T::promiseArrayBuffer, static_cast<NativeSampleTurboModuleCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule));
102+
}
103+
};
104+
105+
} // namespace facebook::react
106+
",
107+
}
108+
`;
109+
41110
exports[`GenerateModuleH can generate fixture complex_objects 1`] = `
42111
Map {
43112
"complex_objectsJSI.h" => "/**

packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleHObjCpp-test.js.snap

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,47 @@ namespace facebook::react {
6565
}
6666
`;
6767
68+
exports[`GenerateModuleHObjCpp can generate fixture array_buffer_native_module 1`] = `
69+
Map {
70+
"array_buffer_native_module.h" => "/**
71+
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
72+
*
73+
* Do not edit this file as changes may cause incorrect behavior and will be lost
74+
* once the code is regenerated.
75+
*
76+
* @generated by codegen project: GenerateModuleObjCpp
77+
*
78+
* We create an umbrella header (and corresponding implementation) here since
79+
* Cxx compilation in BUCK has a limitation: source-code producing genrule()s
80+
* must have a single output. More files => more genrule()s => slower builds.
81+
*/
82+
83+
#ifndef __cplusplus
84+
#error This file must be compiled as Obj-C++. If you are importing it, you must change your file extension to .mm.
85+
#endif
86+
87+
// Avoid multiple includes of array_buffer_native_module symbols
88+
#ifndef array_buffer_native_module_H
89+
#define array_buffer_native_module_H
90+
91+
#import <Foundation/Foundation.h>
92+
#import <RCTRequired/RCTRequired.h>
93+
#import <RCTTypeSafety/RCTConvertHelpers.h>
94+
#import <RCTTypeSafety/RCTTypedModuleConstants.h>
95+
#import <React/RCTBridgeModule.h>
96+
#import <React/RCTCxxConvert.h>
97+
#import <React/RCTManagedPointer.h>
98+
#import <ReactCommon/RCTTurboModule.h>
99+
#import <optional>
100+
#import <vector>
101+
102+
103+
104+
#endif // array_buffer_native_module_H
105+
",
106+
}
107+
`;
108+
68109
exports[`GenerateModuleHObjCpp can generate fixture complex_objects 1`] = `
69110
Map {
70111
"complex_objects.h" => "/**

0 commit comments

Comments
 (0)