Skip to content

Commit 26c2724

Browse files
committed
feat: support Array<ArrayBuffer> as a TurboModule method parameter
1 parent 1cc1db8 commit 26c2724

33 files changed

Lines changed: 1838 additions & 69 deletions

File tree

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ import type {AliasResolver} from './Utils';
2525
const {unwrapNullable} = require('../../parsers/parsers-commons');
2626
const {wrapOptional} = require('../TypeUtils/Java');
2727
const {parseValidUnionType, toPascalCase} = require('../Utils');
28-
const {createAliasResolver, getModules} = require('./Utils');
28+
const {
29+
createAliasResolver,
30+
getModules,
31+
isArrayBufferElementType,
32+
} = require('./Utils');
2933

3034
type FilesOutput = Map<string, string>;
3135

@@ -272,6 +276,10 @@ function translateFunctionParamToJavaType(
272276
imports.add('com.facebook.react.bridge.ReadableMap');
273277
return wrapOptional('ReadableMap', isRequired);
274278
case 'ArrayTypeAnnotation':
279+
if (isArrayBufferElementType(realTypeAnnotation.elementType)) {
280+
imports.add('com.facebook.react.bridge.ArrayBuffer');
281+
return wrapOptional('ArrayBuffer[]', isRequired);
282+
}
275283
imports.add('com.facebook.react.bridge.ReadableArray');
276284
return wrapOptional('ReadableArray', isRequired);
277285
case 'FunctionTypeAnnotation':

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ import type {AliasResolver} from './Utils';
2424

2525
const {unwrapNullable} = require('../../parsers/parsers-commons');
2626
const {parseValidUnionType} = require('../Utils');
27-
const {createAliasResolver, getModules} = require('./Utils');
27+
const {
28+
createAliasResolver,
29+
getModules,
30+
isArrayBufferElementType,
31+
} = require('./Utils');
2832

2933
type FilesOutput = Map<string, string>;
3034

@@ -308,7 +312,9 @@ function translateParamTypeToJniType(
308312
case 'ObjectTypeAnnotation':
309313
return 'Lcom/facebook/react/bridge/ReadableMap;';
310314
case 'ArrayTypeAnnotation':
311-
return 'Lcom/facebook/react/bridge/ReadableArray;';
315+
return isArrayBufferElementType(realTypeAnnotation.elementType)
316+
? '[Lcom/facebook/react/bridge/ArrayBuffer;'
317+
: 'Lcom/facebook/react/bridge/ReadableArray;';
312318
case 'FunctionTypeAnnotation':
313319
return 'Lcom/facebook/react/bridge/Callback;';
314320
case 'ArrayBufferTypeAnnotation':

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const {
2626
} = require('../../../parsers/parsers-commons');
2727
const {wrapOptional} = require('../../TypeUtils/Objective-C');
2828
const {capitalize, parseValidUnionType} = require('../../Utils');
29+
const {isArrayBufferElementType} = require('../Utils');
2930
const {getNamespacedStructName} = require('./Utils');
3031
const invariant = require('invariant');
3132

@@ -218,6 +219,11 @@ function getParamObjCType(
218219
* type Animal = {};
219220
* Array<Animal> => NSArray<JS::NativeSampleTurboModule::Animal *>, etc.
220221
*/
222+
if (isArrayBufferElementType(typeAnnotation.elementType)) {
223+
return notStruct(
224+
wrapOptional('NSArray<RCTArrayBuffer *> *', !nullable),
225+
);
226+
}
221227
return notStruct(wrapOptional('NSArray *', !nullable));
222228
}
223229
case 'ArrayBufferTypeAnnotation': {

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@
1212

1313
import type {
1414
NativeModuleAliasMap,
15+
NativeModuleBaseTypeAnnotation,
1516
NativeModuleObjectTypeAnnotation,
1617
NativeModuleSchema,
1718
NativeModuleTypeAnnotation,
1819
Nullable,
1920
SchemaType,
21+
UnsafeAnyTypeAnnotation,
2022
} from '../../CodegenSchema';
2123

2224
const {unwrapNullable} = require('../../parsers/parsers-commons');
@@ -77,9 +79,29 @@ function isArrayRecursiveMember(
7779
);
7880
}
7981

82+
/**
83+
* Whether an array's element type is `ArrayBuffer`. Rejects nullable elements
84+
* (`Array<?ArrayBuffer>`) so generators fall back to an untyped array. Handles
85+
* the `AnyTypeAnnotation` that `emitArrayType` substitutes when the element
86+
* type failed to parse.
87+
*/
88+
function isArrayBufferElementType(
89+
elementType:
90+
Nullable<NativeModuleBaseTypeAnnotation> | UnsafeAnyTypeAnnotation,
91+
): boolean {
92+
if (elementType == null || elementType.type === 'AnyTypeAnnotation') {
93+
return false;
94+
}
95+
if (elementType.type === 'NullableTypeAnnotation') {
96+
return false;
97+
}
98+
return elementType.type === 'ArrayBufferTypeAnnotation';
99+
}
100+
80101
module.exports = {
81102
createAliasResolver,
82103
getModules,
83104
isDirectRecursiveMember,
84105
isArrayRecursiveMember,
106+
isArrayBufferElementType,
85107
};

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2692,6 +2692,28 @@ const ARRAY_BUFFER_NATIVE_MODULE: SchemaType = {
26922692
params: [],
26932693
},
26942694
},
2695+
{
2696+
name: 'arrayBufferArray',
2697+
optional: false,
2698+
typeAnnotation: {
2699+
type: 'FunctionTypeAnnotation',
2700+
returnTypeAnnotation: {
2701+
type: 'NumberTypeAnnotation',
2702+
},
2703+
params: [
2704+
{
2705+
name: 'values',
2706+
optional: false,
2707+
typeAnnotation: {
2708+
type: 'ArrayTypeAnnotation',
2709+
elementType: {
2710+
type: 'ArrayBufferTypeAnnotation',
2711+
},
2712+
},
2713+
},
2714+
],
2715+
},
2716+
},
26952717
],
26962718
},
26972719
moduleName: 'SampleTurboModule',

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,16 @@ describe('GenerateModuleHObjCpp', () => {
3131
).toMatchSnapshot();
3232
});
3333
});
34+
35+
it('generates NSArray<RCTArrayBuffer *> for a top-level Array<ArrayBuffer> parameter', () => {
36+
const output = generator.generate(
37+
'array_buffer_native_module',
38+
fixtures.array_buffer_native_module,
39+
'com.facebook.fbreact.specs',
40+
false,
41+
);
42+
expect([...output.values()].join('\n')).toContain(
43+
'arrayBufferArray:(NSArray<RCTArrayBuffer *> *)values',
44+
);
45+
});
3446
});

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

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,60 @@ describe('GenerateModuleJavaSpec', () => {
2929
).toMatchSnapshot();
3030
});
3131
});
32+
33+
it('generates ArrayBuffer[] for a top-level Array<ArrayBuffer> parameter', () => {
34+
const output = generator.generate(
35+
'array_buffer_native_module',
36+
fixtures.array_buffer_native_module,
37+
'com.facebook.fbreact.specs',
38+
);
39+
expect([...output.values()].join('\n')).toContain('ArrayBuffer[] values');
40+
});
41+
42+
it('does not generate ArrayBuffer[] when array element type is nullable', () => {
43+
const schema: $FlowFixMe = {
44+
modules: {
45+
NativeSampleTurboModule: {
46+
type: 'NativeModule',
47+
aliasMap: {},
48+
enumMap: {},
49+
moduleName: 'SampleTurboModule',
50+
spec: {
51+
eventEmitters: [],
52+
methods: [
53+
{
54+
name: 'nullableElements',
55+
optional: false,
56+
typeAnnotation: {
57+
type: 'FunctionTypeAnnotation',
58+
returnTypeAnnotation: {type: 'NumberTypeAnnotation'},
59+
params: [
60+
{
61+
name: 'values',
62+
optional: false,
63+
typeAnnotation: {
64+
type: 'ArrayTypeAnnotation',
65+
elementType: {
66+
type: 'NullableTypeAnnotation',
67+
typeAnnotation: {type: 'ArrayBufferTypeAnnotation'},
68+
},
69+
},
70+
},
71+
],
72+
},
73+
},
74+
],
75+
},
76+
},
77+
},
78+
};
79+
const output = generator.generate(
80+
'nullable_array_buffer_elements',
81+
schema,
82+
'com.facebook.fbreact.specs',
83+
);
84+
const contents = [...output.values()].join('\n');
85+
expect(contents).toContain('nullableElements(ReadableArray values)');
86+
expect(contents).not.toMatch(/nullableElements\(ArrayBuffer\[\] values\)/);
87+
});
3288
});

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,15 @@ describe('GenerateModuleJniCpp', () => {
2929
).toMatchSnapshot();
3030
});
3131
});
32+
33+
it('generates a JNI ArrayBuffer array signature for a top-level Array<ArrayBuffer> parameter', () => {
34+
const output = generator.generate(
35+
'array_buffer_native_module',
36+
fixtures.array_buffer_native_module,
37+
'com.facebook.fbreact.specs',
38+
);
39+
expect([...output.values()].join('\n')).toContain(
40+
'[Lcom/facebook/react/bridge/ArrayBuffer;',
41+
);
42+
});
3243
});

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ protected:
6969
methodMap_[\\"voidNullableArrayBuffer\\"] = MethodMetadata {.argCount = 1, .invoker = __voidNullableArrayBuffer};
7070
methodMap_[\\"promiseArrayBuffer\\"] = MethodMetadata {.argCount = 0, .invoker = __promiseArrayBuffer};
7171
methodMap_[\\"promiseNullableArrayBuffer\\"] = MethodMetadata {.argCount = 0, .invoker = __promiseNullableArrayBuffer};
72+
methodMap_[\\"arrayBufferArray\\"] = MethodMetadata {.argCount = 1, .invoker = __arrayBufferArray};
7273
}
7374

7475
private:
@@ -108,6 +109,14 @@ private:
108109
\\"Expected promiseNullableArrayBuffer(...) to have 1 parameters\\");
109110
return bridging::callFromJs<jsi::Value>(rt, &T::promiseNullableArrayBuffer, static_cast<NativeSampleTurboModuleCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule));
110111
}
112+
113+
static jsi::Value __arrayBufferArray(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
114+
static_assert(
115+
bridging::getParameterCount(&T::arrayBufferArray) == 2,
116+
\\"Expected arrayBufferArray(...) to have 2 parameters\\");
117+
return bridging::callFromJs<double>(rt, &T::arrayBufferArray, static_cast<NativeSampleTurboModuleCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule),
118+
count <= 0 ? throw jsi::JSError(rt, \\"Expected argument in position 0 to be passed\\") : args[0].asObject(rt).asArray(rt));
119+
}
111120
};
112121

113122
} // namespace facebook::react

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ Map {
111111
reject:(RCTPromiseRejectBlock)reject;
112112
- (void)promiseNullableArrayBuffer:(RCTPromiseResolveBlock)resolve
113113
reject:(RCTPromiseRejectBlock)reject;
114+
- (NSNumber *)arrayBufferArray:(NSArray<RCTArrayBuffer *> *)values;
114115
115116
@end
116117

0 commit comments

Comments
 (0)