Skip to content

Commit b444f0e

Browse files
react-native-code-gen Add Enum Type support for C++ TurboModules
Summary: There are cases where we want to pass an enum type into a TurboModule method which is handy to restrict certain arguments to a restricted set of values, e.g. restricting quality to ```enum Quality { SD, HD, }``` Approach: - We are not generating an ```enum``` type in C++ but rather just cast type safe to the corresponding member type. - We don't support mixed enum types at this time, e.g. ```export enum StringOption { One = 'one', Two = 2, Three = 'three', };``` will not work. See: https://www.typescriptlang.org/docs/handbook/enums.html#heterogeneous-enums - We only support untyped (default to String), String, and Number enum properties This is for C++ only, Java and ObjC are not supported atm. Changelog: [General][Added] - react-native-code-gen Add Enum Type support for C++ TurboModules Reviewed By: RSNara Differential Revision: D38880963 fbshipit-source-id: f2399b29948306bc555429b6f96c43ea4c39c46e
1 parent 621f4cf commit b444f0e

22 files changed

Lines changed: 361 additions & 15 deletions

File tree

packages/eslint-plugin-specs/react-native-modules.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ function rule(context) {
134134
const [parsingErrors, tryParse] = createParserErrorCapturer();
135135

136136
const sourceCode = context.getSourceCode().getText();
137-
const ast = flowParser.parse(sourceCode);
137+
const ast = flowParser.parse(sourceCode, {enums: true});
138138

139139
tryParse(() => {
140140
buildModuleSchema(hasteModuleName, ast, tryParse);

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,11 @@ export type NativeModuleBooleanTypeAnnotation = $ReadOnly<{
282282
type: 'BooleanTypeAnnotation',
283283
}>;
284284

285+
export type NativeModuleEnumDeclaration = $ReadOnly<{
286+
type: 'EnumDeclaration',
287+
memberType: 'NumberTypeAnnotation' | 'StringTypeAnnotation',
288+
}>;
289+
285290
export type NativeModuleGenericObjectTypeAnnotation = $ReadOnly<{
286291
type: 'GenericObjectTypeAnnotation',
287292
}>;
@@ -316,6 +321,7 @@ export type NativeModuleBaseTypeAnnotation =
316321
| NativeModuleDoubleTypeAnnotation
317322
| NativeModuleFloatTypeAnnotation
318323
| NativeModuleBooleanTypeAnnotation
324+
| NativeModuleEnumDeclaration
319325
| NativeModuleGenericObjectTypeAnnotation
320326
| ReservedTypeAnnotation
321327
| NativeModuleTypeAliasTypeAnnotation

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,17 @@ function serializeArg(
153153
return wrap(val => `${val}.asString(rt)`);
154154
case 'BooleanTypeAnnotation':
155155
return wrap(val => `${val}.asBool()`);
156+
case 'EnumDeclaration':
157+
switch (realTypeAnnotation.memberType) {
158+
case 'NumberTypeAnnotation':
159+
return wrap(val => `${val}.asNumber()`);
160+
case 'StringTypeAnnotation':
161+
return wrap(val => `${val}.asString(rt)`);
162+
default:
163+
throw new Error(
164+
`Unknown enum type for "${arg.name}, found: ${realTypeAnnotation.type}"`,
165+
);
166+
}
156167
case 'NumberTypeAnnotation':
157168
return wrap(val => `${val}.asNumber()`);
158169
case 'FloatTypeAnnotation':

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,15 @@ function translatePrimitiveJSTypeToCpp(
146146
return wrap('int');
147147
case 'BooleanTypeAnnotation':
148148
return wrap('bool');
149+
case 'EnumDeclaration':
150+
switch (realTypeAnnotation.memberType) {
151+
case 'NumberTypeAnnotation':
152+
return wrap('double');
153+
case 'StringTypeAnnotation':
154+
return wrap('jsi::String');
155+
default:
156+
throw new Error(createErrorMessage(realTypeAnnotation.type));
157+
}
149158
case 'GenericObjectTypeAnnotation':
150159
return wrap('jsi::Object');
151160
case 'UnionTypeAnnotation':

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,10 @@ function translateFunctionParamToJavaType(
156156
imports.add('com.facebook.react.bridge.Callback');
157157
return 'Callback';
158158
default:
159-
(realTypeAnnotation.type: 'MixedTypeAnnotation' | 'UnionTypeAnnotation');
159+
(realTypeAnnotation.type:
160+
| 'EnumDeclaration'
161+
| 'MixedTypeAnnotation'
162+
| 'UnionTypeAnnotation');
160163
throw new Error(createErrorMessage(realTypeAnnotation.type));
161164
}
162165
}
@@ -220,7 +223,10 @@ function translateFunctionReturnTypeToJavaType(
220223
imports.add('com.facebook.react.bridge.WritableArray');
221224
return wrapIntoNullableIfNeeded('WritableArray');
222225
default:
223-
(realTypeAnnotation.type: 'MixedTypeAnnotation' | 'UnionTypeAnnotation');
226+
(realTypeAnnotation.type:
227+
| 'EnumDeclaration'
228+
| 'MixedTypeAnnotation'
229+
| 'UnionTypeAnnotation');
224230
throw new Error(createErrorMessage(realTypeAnnotation.type));
225231
}
226232
}
@@ -272,7 +278,10 @@ function getFalsyReturnStatementFromReturnType(
272278
case 'ArrayTypeAnnotation':
273279
return 'return null;';
274280
default:
275-
(realTypeAnnotation.type: 'MixedTypeAnnotation' | 'UnionTypeAnnotation');
281+
(realTypeAnnotation.type:
282+
| 'EnumDeclaration'
283+
| 'MixedTypeAnnotation'
284+
| 'UnionTypeAnnotation');
276285
throw new Error(createErrorMessage(realTypeAnnotation.type));
277286
}
278287
}

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,10 @@ function translateReturnTypeToKind(
171171
case 'ArrayTypeAnnotation':
172172
return 'ArrayKind';
173173
default:
174-
(realTypeAnnotation.type: 'MixedTypeAnnotation' | 'UnionTypeAnnotation');
174+
(realTypeAnnotation.type:
175+
| 'EnumDeclaration'
176+
| 'MixedTypeAnnotation'
177+
| 'UnionTypeAnnotation');
175178
throw new Error(
176179
`Unknown prop type for returning value, found: ${realTypeAnnotation.type}"`,
177180
);
@@ -226,7 +229,10 @@ function translateParamTypeToJniType(
226229
case 'FunctionTypeAnnotation':
227230
return 'Lcom/facebook/react/bridge/Callback;';
228231
default:
229-
(realTypeAnnotation.type: 'MixedTypeAnnotation' | 'UnionTypeAnnotation');
232+
(realTypeAnnotation.type:
233+
| 'EnumDeclaration'
234+
| 'MixedTypeAnnotation'
235+
| 'UnionTypeAnnotation');
230236
throw new Error(
231237
`Unknown prop type for method arg, found: ${realTypeAnnotation.type}"`,
232238
);
@@ -278,7 +284,10 @@ function translateReturnTypeToJniType(
278284
case 'ArrayTypeAnnotation':
279285
return 'Lcom/facebook/react/bridge/WritableArray;';
280286
default:
281-
(realTypeAnnotation.type: 'MixedTypeAnnotation' | 'UnionTypeAnnotation');
287+
(realTypeAnnotation.type:
288+
| 'EnumDeclaration'
289+
| 'MixedTypeAnnotation'
290+
| 'UnionTypeAnnotation');
282291
throw new Error(
283292
`Unknown prop type for method return type, found: ${realTypeAnnotation.type}"`,
284293
);

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ class StructCollector {
112112
this._insertAlias(typeAnnotation.name, structContext, resolveAlias);
113113
return wrapNullable(nullable, typeAnnotation);
114114
}
115+
case 'EnumDeclaration':
116+
throw new Error('Enum types are unsupported in structs');
115117
case 'MixedTypeAnnotation':
116118
throw new Error('Mixed types are unsupported in structs');
117119
case 'UnionTypeAnnotation':

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,10 @@ function getReturnObjCType(
338338
case 'GenericObjectTypeAnnotation':
339339
return wrapIntoNullableIfNeeded('NSDictionary *');
340340
default:
341-
(typeAnnotation.type: 'MixedTypeAnnotation' | 'UnionTypeAnnotation');
341+
(typeAnnotation.type:
342+
| 'EnumDeclaration'
343+
| 'MixedTypeAnnotation'
344+
| 'UnionTypeAnnotation');
342345
throw new Error(
343346
`Unsupported return type for ${methodName}. Found: ${typeAnnotation.type}`,
344347
);
@@ -378,7 +381,10 @@ function getReturnJSType(
378381
case 'GenericObjectTypeAnnotation':
379382
return 'ObjectKind';
380383
default:
381-
(typeAnnotation.type: 'MixedTypeAnnotation' | 'UnionTypeAnnotation');
384+
(typeAnnotation.type:
385+
| 'EnumDeclaration'
386+
| 'MixedTypeAnnotation'
387+
| 'UnionTypeAnnotation');
382388
throw new Error(
383389
`Unsupported return type for ${methodName}. Found: ${typeAnnotation.type}`,
384390
);

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,6 +1567,42 @@ const CXX_ONLY_NATIVE_MODULES: SchemaType = {
15671567
],
15681568
},
15691569
},
1570+
{
1571+
name: 'getEnums',
1572+
optional: false,
1573+
typeAnnotation: {
1574+
type: 'FunctionTypeAnnotation',
1575+
returnTypeAnnotation: {
1576+
type: 'StringTypeAnnotation',
1577+
},
1578+
params: [
1579+
{
1580+
name: 'enumInt',
1581+
optional: false,
1582+
typeAnnotation: {
1583+
type: 'EnumDeclaration',
1584+
memberType: 'NumberTypeAnnotation',
1585+
},
1586+
},
1587+
{
1588+
name: 'enumFloat',
1589+
optional: false,
1590+
typeAnnotation: {
1591+
type: 'EnumDeclaration',
1592+
memberType: 'NumberTypeAnnotation',
1593+
},
1594+
},
1595+
{
1596+
name: 'enumString',
1597+
optional: false,
1598+
typeAnnotation: {
1599+
type: 'EnumDeclaration',
1600+
memberType: 'StringTypeAnnotation',
1601+
},
1602+
},
1603+
],
1604+
},
1605+
},
15701606
{
15711607
name: 'getUnion',
15721608
optional: false,

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ static jsi::Value __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getNullableNu
115115
auto result = static_cast<NativeSampleTurboModuleCxxSpecJSI *>(&turboModule)->getNullableNumberFromNullableAlias(rt, args[0].isNull() || args[0].isUndefined() ? std::nullopt : std::make_optional(args[0].asObject(rt)));
116116
return result ? jsi::Value(std::move(*result)) : jsi::Value::null();
117117
}
118+
static jsi::Value __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getEnums(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
119+
return static_cast<NativeSampleTurboModuleCxxSpecJSI *>(&turboModule)->getEnums(rt, args[0].asNumber(), args[1].asNumber(), args[2].asString(rt));
120+
}
118121
static jsi::Value __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getUnion(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
119122
return static_cast<NativeSampleTurboModuleCxxSpecJSI *>(&turboModule)->getUnion(rt, args[0].asNumber(), args[1].asNumber(), args[2].asObject(rt), args[3].asString(rt));
120123
}
@@ -123,6 +126,7 @@ NativeSampleTurboModuleCxxSpecJSI::NativeSampleTurboModuleCxxSpecJSI(std::shared
123126
: TurboModule(\\"SampleTurboModuleCxx\\", jsInvoker) {
124127
methodMap_[\\"getMixed\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getMixed};
125128
methodMap_[\\"getNullableNumberFromNullableAlias\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getNullableNumberFromNullableAlias};
129+
methodMap_[\\"getEnums\\"] = MethodMetadata {3, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getEnums};
126130
methodMap_[\\"getUnion\\"] = MethodMetadata {4, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getUnion};
127131
}
128132

0 commit comments

Comments
 (0)