Skip to content

Commit c0a06d2

Browse files
react-native code-gen > C++ TurboModules struct support (facebook#35265)
Summary: Pull Request resolved: facebook#35265 This adds a templating layer for C++ TurboModules to automatically generate struct templates from TurboModule specs. You have to define concrete types for those templates to use them in your C++ TurboModule. E.g. for the JS flow type: ``` export type ObjectStruct = {| a: number, b: string, c?: ?string, |}; ``` code-gen will now generate the following template code: ``` #pragma mark - NativeCxxModuleExampleCxxBaseObjectStruct template <typename P0, typename P1, typename P2> struct NativeCxxModuleExampleCxxBaseObjectStruct { P0 a; P1 b; P2 c; bool operator==(const NativeCxxModuleExampleCxxBaseObjectStruct &other) const { return a == other.a && b == other.b && c == other.c; } }; template <typename P0, typename P1, typename P2> struct NativeCxxModuleExampleCxxBaseObjectStructBridging { static NativeCxxModuleExampleCxxBaseObjectStruct<P0, P1, P2> fromJs( jsi::Runtime &rt, const jsi::Object &value, const std::shared_ptr<CallInvoker> &jsInvoker) { NativeCxxModuleExampleCxxBaseObjectStruct<P0, P1, P2> result{ bridging::fromJs<P0>(rt, value.getProperty(rt, "a"), jsInvoker), bridging::fromJs<P1>(rt, value.getProperty(rt, "b"), jsInvoker), bridging::fromJs<P2>(rt, value.getProperty(rt, "c"), jsInvoker)}; return result; } static jsi::Object toJs( jsi::Runtime &rt, const NativeCxxModuleExampleCxxBaseObjectStruct<P0, P1, P2> &value) { auto result = facebook::jsi::Object(rt); result.setProperty(rt, "a", bridging::toJs(rt, value.a)); result.setProperty(rt, "b", bridging::toJs(rt, value.b)); if (value.c) { result.setProperty(rt, "c", bridging::toJs(rt, value.c.value())); } return result; } }; ``` and you can use it in our C++ TurboModule for example as: ``` using ObjectStruct = NativeCxxModuleExampleCxxBaseObjectStruct< int32_t, std::string, std::optional<std::string>>; template <> struct Bridging<ObjectStruct> : NativeCxxModuleExampleCxxBaseObjectStructBridging< int32_t, std::string, std::optional<std::string>> {}; ``` or as ``` using ObjectStruct = NativeCxxModuleExampleCxxBaseObjectStruct< float, folly::StringPiece, std::optional<std::string>>; template <> struct Bridging<ObjectStruct> : NativeCxxModuleExampleCxxBaseObjectStructBridging< float, folly::StringPiece, std::optional<std::string>> {}; ``` Or as ... Changelog: [Internal] Reviewed By: rshest Differential Revision: D41133761 fbshipit-source-id: fdf36e51073cb46c5234f6121842c79a884899c7
1 parent 64ea7ce commit c0a06d2

File tree

9 files changed

+493
-223
lines changed

9 files changed

+493
-223
lines changed

Libraries/WebPerformance/NativePerformanceObserver.h

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,34 @@
1313
#include <optional>
1414
#include <string>
1515
#include <vector>
16-
#include "NativePerformanceObserver_RawPerformanceEntry.h"
1716

1817
namespace facebook::react {
1918

19+
#pragma mark - Structs
20+
21+
using RawPerformanceEntry = NativePerformanceObserverCxxBaseRawPerformanceEntry<
22+
std::string,
23+
int32_t,
24+
double,
25+
double,
26+
// For "event" entries only:
27+
std::optional<double>,
28+
std::optional<double>,
29+
std::optional<double>>;
30+
31+
template <>
32+
struct Bridging<RawPerformanceEntry>
33+
: NativePerformanceObserverCxxBaseRawPerformanceEntryBridging<
34+
std::string,
35+
int32_t,
36+
double,
37+
double,
38+
std::optional<double>,
39+
std::optional<double>,
40+
std::optional<double>> {};
41+
42+
#pragma mark - implementation
43+
2044
class NativePerformanceObserver
2145
: public NativePerformanceObserverCxxSpec<NativePerformanceObserver>,
2246
std::enable_shared_from_this<NativePerformanceObserver> {

Libraries/WebPerformance/NativePerformanceObserver.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export const RawPerformanceEntryTypeValues = {
1818

1919
export type RawPerformanceEntryType = number;
2020

21-
export type RawPerformanceEntry = $ReadOnly<{
21+
export type RawPerformanceEntry = {|
2222
name: string,
2323
entryType: RawPerformanceEntryType,
2424
startTime: number,
@@ -27,7 +27,7 @@ export type RawPerformanceEntry = $ReadOnly<{
2727
processingStart?: number,
2828
processingEnd?: number,
2929
interactionId?: number,
30-
}>;
30+
|};
3131

3232
export interface Spec extends TurboModule {
3333
+startReporting: (entryType: string) => void;

Libraries/WebPerformance/NativePerformanceObserver_RawPerformanceEntry.h

Lines changed: 0 additions & 76 deletions
This file was deleted.

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

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import type {
1616
NativeModuleTypeAnnotation,
1717
NativeModuleFunctionTypeAnnotation,
1818
NativeModulePropertyShape,
19+
NativeModuleAliasMap,
1920
} from '../../CodegenSchema';
2021

2122
import type {AliasResolver} from './Utils';
@@ -28,8 +29,13 @@ type FilesOutput = Map<string, string>;
2829
const ModuleClassDeclarationTemplate = ({
2930
hasteModuleName,
3031
moduleProperties,
31-
}: $ReadOnly<{hasteModuleName: string, moduleProperties: string[]}>) => {
32-
return `class JSI_EXPORT ${hasteModuleName}CxxSpecJSI : public TurboModule {
32+
structs,
33+
}: $ReadOnly<{
34+
hasteModuleName: string,
35+
moduleProperties: string[],
36+
structs: string,
37+
}>) => {
38+
return `${structs}class JSI_EXPORT ${hasteModuleName}CxxSpecJSI : public TurboModule {
3339
protected:
3440
${hasteModuleName}CxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker);
3541
@@ -186,6 +192,76 @@ function translatePrimitiveJSTypeToCpp(
186192
}
187193
}
188194

195+
function createStructs(
196+
moduleName: string,
197+
aliasMap: NativeModuleAliasMap,
198+
resolveAlias: AliasResolver,
199+
): string {
200+
return Object.keys(aliasMap)
201+
.map(alias => {
202+
const value = aliasMap[alias];
203+
if (value.properties.length === 0) {
204+
return '';
205+
}
206+
const structName = `${moduleName}Base${alias}`;
207+
const templateParameterWithTypename = value.properties
208+
.map((v, i) => 'typename P' + i)
209+
.join(', ');
210+
const templateParameter = value.properties
211+
.map((v, i) => 'P' + i)
212+
.join(', ');
213+
return `#pragma mark - ${structName}
214+
215+
template <${templateParameterWithTypename}>
216+
struct ${structName} {
217+
${value.properties.map((v, i) => ' P' + i + ' ' + v.name).join(';\n')};
218+
bool operator==(const ${structName} &other) const {
219+
return ${value.properties
220+
.map(v => `${v.name} == other.${v.name}`)
221+
.join(' && ')};
222+
}
223+
};
224+
225+
template <${templateParameterWithTypename}>
226+
struct ${structName}Bridging {
227+
static ${structName}<${templateParameter}> fromJs(
228+
jsi::Runtime &rt,
229+
const jsi::Object &value,
230+
const std::shared_ptr<CallInvoker> &jsInvoker) {
231+
${structName}<${templateParameter}> result{
232+
${value.properties
233+
.map(
234+
(v, i) =>
235+
` bridging::fromJs<P${i}>(rt, value.getProperty(rt, "${v.name}"), jsInvoker)`,
236+
)
237+
.join(',\n')}};
238+
return result;
239+
}
240+
241+
static jsi::Object toJs(
242+
jsi::Runtime &rt,
243+
const ${structName}<${templateParameter}> &value) {
244+
auto result = facebook::jsi::Object(rt);
245+
${value.properties
246+
.map((v, i) => {
247+
if (v.optional) {
248+
return ` if (value.${v.name}) {
249+
result.setProperty(rt, "${v.name}", bridging::toJs(rt, value.${v.name}.value()));
250+
}`;
251+
} else {
252+
return ` result.setProperty(rt, "${v.name}", bridging::toJs(rt, value.${v.name}));`;
253+
}
254+
})
255+
.join('\n')}
256+
return result;
257+
}
258+
};
259+
260+
`;
261+
})
262+
.join('\n');
263+
}
264+
189265
function translatePropertyToCpp(
190266
prop: NativeModulePropertyShape,
191267
resolveAlias: AliasResolver,
@@ -251,13 +327,15 @@ module.exports = {
251327
moduleNames: [moduleName],
252328
} = nativeModules[hasteModuleName];
253329
const resolveAlias = createAliasResolver(aliases);
330+
const structs = createStructs(moduleName, aliases, resolveAlias);
254331

255332
return [
256333
ModuleClassDeclarationTemplate({
257334
hasteModuleName,
258335
moduleProperties: properties.map(prop =>
259336
translatePropertyToCpp(prop, resolveAlias, true),
260337
),
338+
structs,
261339
}),
262340
ModuleSpecClassDeclarationTemplate({
263341
hasteModuleName,

0 commit comments

Comments
 (0)