Skip to content

Commit

Permalink
Add logic to generate recursive TM members (facebook#41768)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: facebook#41768

Changelog: [Internal]

Reviewed By: rshest

Differential Revision: D51572375

fbshipit-source-id: e4ce0726c457ea02a7710ea1d21bbd697e35848a
  • Loading branch information
christophpurrer authored and facebook-github-bot committed Dec 4, 2023
1 parent 09b110f commit 749b8dd
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ const {
createAliasResolver,
getAreEnumMembersInteger,
getModules,
isArrayRecursiveMember,
isDirectRecursiveMember,
} = require('./Utils');

type FilesOutput = Map<string, string>;
Expand Down Expand Up @@ -129,6 +131,7 @@ ${modules.join('\n\n')}

function translatePrimitiveJSTypeToCpp(
moduleName: string,
parentObjectAliasName: ?string,
nullableTypeAnnotation: Nullable<NativeModuleTypeAnnotation>,
optional: boolean,
createErrorMessage: (typeName: string) => string,
Expand All @@ -139,14 +142,18 @@ function translatePrimitiveJSTypeToCpp(
nullableTypeAnnotation,
);
const isRequired = !optional && !nullable;
const isRecursiveType = isDirectRecursiveMember(
parentObjectAliasName,
nullableTypeAnnotation,
);

let realTypeAnnotation = typeAnnotation;
if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') {
realTypeAnnotation = resolveAlias(realTypeAnnotation.name);
}

function wrap(type: string) {
return isRequired ? type : `std::optional<${type}>`;
return isRequired || isRecursiveType ? type : `std::optional<${type}>`;
}

switch (realTypeAnnotation.type) {
Expand Down Expand Up @@ -221,10 +228,12 @@ function createStructsString(
enumMap: NativeModuleEnumMap,
): string {
const getCppType = (
parentObjectAlias: string,
v: NamedShape<Nullable<NativeModuleBaseTypeAnnotation>>,
) =>
translatePrimitiveJSTypeToCpp(
moduleName,
parentObjectAlias,
v.typeAnnotation,
false,
typeName => `Unsupported type for param "${v.name}". Found: ${typeName}`,
Expand All @@ -250,7 +259,7 @@ function createStructsString(
.join(', ');
const debugParameterConversion = value.properties
.map(
(v, i) => ` static ${getCppType(v)} ${
(v, i) => ` static ${getCppType(alias, v)} ${
v.name
}ToJs(jsi::Runtime &rt, P${i} value) {
return bridging::toJs(rt, value);
Expand Down Expand Up @@ -321,12 +330,39 @@ ${value.properties
return '';
}
const structName = `${moduleName}${alias}`;
const templateParameterWithTypename = value.properties
const templateParameter = value.properties.filter(
v =>
!isDirectRecursiveMember(alias, v.typeAnnotation) &&
!isArrayRecursiveMember(alias, v.typeAnnotation),
);
const templateParameterWithTypename = templateParameter
.map((v, i) => `typename P${i}`)
.join(', ');
const templateParameterWithoutTypename = templateParameter
.map((v, i) => `P${i}`)
.join(', ');
let i = -1;
const templateMemberTypes = value.properties.map(v => {
if (isDirectRecursiveMember(alias, v.typeAnnotation)) {
return `std::unique_ptr<${structName}<${templateParameterWithoutTypename}>> ${v.name}`;
} else if (isArrayRecursiveMember(alias, v.typeAnnotation)) {
const [nullable] = unwrapNullable<NativeModuleTypeAnnotation>(
v.typeAnnotation,
);
return (
(nullable
? `std::optional<std::vector<${structName}<${templateParameterWithoutTypename}>>>`
: `std::vector<${structName}<${templateParameterWithoutTypename}>>`) +
` ${v.name}`
);
} else {
i++;
return `P${i} ${v.name}`;
}
});
const debugParameterConversion = value.properties
.map(
(v, i) => ` static ${getCppType(v)} ${
v => ` static ${getCppType(alias, v)} ${
v.name
}ToJs(jsi::Runtime &rt, decltype(types.${v.name}) value) {
return bridging::toJs(rt, value);
Expand All @@ -338,7 +374,7 @@ ${value.properties
template <${templateParameterWithTypename}>
struct ${structName} {
${value.properties.map((v, i) => ' P' + i + ' ' + v.name).join(';\n')};
${templateMemberTypes.map(v => ' ' + v).join(';\n')};
bool operator==(const ${structName} &other) const {
return ${value.properties
.map(v => `${v.name} == other.${v.name}`)
Expand All @@ -356,10 +392,13 @@ struct ${structName}Bridging {
const std::shared_ptr<CallInvoker> &jsInvoker) {
T result{
${value.properties
.map(
(v, i) =>
` bridging::fromJs<decltype(types.${v.name})>(rt, value.getProperty(rt, "${v.name}"), jsInvoker)`,
)
.map(v => {
if (isDirectRecursiveMember(alias, v.typeAnnotation)) {
return ` value.hasProperty(rt, "${v.name}") ? std::make_unique<T>(bridging::fromJs<T>(rt, value.getProperty(rt, "${v.name}"), jsInvoker)) : nullptr`;
} else {
return ` bridging::fromJs<decltype(types.${v.name})>(rt, value.getProperty(rt, "${v.name}"), jsInvoker)`;
}
})
.join(',\n')}};
return result;
}
Expand All @@ -374,8 +413,12 @@ ${debugParameterConversion}
const std::shared_ptr<CallInvoker> &jsInvoker) {
auto result = facebook::jsi::Object(rt);
${value.properties
.map((v, i) => {
if (v.optional) {
.map(v => {
if (isDirectRecursiveMember(alias, v.typeAnnotation)) {
return ` if (value.${v.name}) {
result.setProperty(rt, "${v.name}", bridging::toJs(rt, *value.${v.name}, jsInvoker));
}`;
} else if (v.optional) {
return ` if (value.${v.name}) {
result.setProperty(rt, "${v.name}", bridging::toJs(rt, value.${v.name}.value(), jsInvoker));
}`;
Expand Down Expand Up @@ -528,6 +571,7 @@ function translatePropertyToCpp(
const paramTypes = propTypeAnnotation.params.map(param => {
const translatedParam = translatePrimitiveJSTypeToCpp(
moduleName,
null,
param.typeAnnotation,
param.optional,
typeName =>
Expand All @@ -540,6 +584,7 @@ function translatePropertyToCpp(

const returnType = translatePrimitiveJSTypeToCpp(
moduleName,
null,
propTypeAnnotation.returnTypeAnnotation,
false,
typeName => `Unsupported return type for ${prop.name}. Found: ${typeName}`,
Expand Down
32 changes: 32 additions & 0 deletions packages/react-native-codegen/src/generators/modules/Utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ import type {
NativeModuleEnumMembers,
NativeModuleObjectTypeAnnotation,
NativeModuleSchema,
NativeModuleTypeAnnotation,
Nullable,
SchemaType,
} from '../../CodegenSchema';

const {unwrapNullable} = require('../../parsers/parsers-commons');
const invariant = require('invariant');

export type AliasResolver = (
Expand Down Expand Up @@ -48,6 +51,33 @@ function getModules(
);
}

function isDirectRecursiveMember(
parentObjectAliasName: ?string,
nullableTypeAnnotation: Nullable<NativeModuleTypeAnnotation>,
): boolean {
const [typeAnnotation] = unwrapNullable<NativeModuleTypeAnnotation>(
nullableTypeAnnotation,
);
return (
parentObjectAliasName !== undefined &&
typeAnnotation.name === parentObjectAliasName
);
}

function isArrayRecursiveMember(
parentObjectAliasName: ?string,
nullableTypeAnnotation: Nullable<NativeModuleTypeAnnotation>,
): boolean {
const [typeAnnotation] = unwrapNullable<NativeModuleTypeAnnotation>(
nullableTypeAnnotation,
);
return (
parentObjectAliasName !== undefined &&
typeAnnotation.type === 'ArrayTypeAnnotation' &&
typeAnnotation.elementType?.name === parentObjectAliasName
);
}

function getAreEnumMembersInteger(members: NativeModuleEnumMembers): boolean {
return !members.some(m => `${m.value}`.includes('.'));
}
Expand All @@ -56,4 +86,6 @@ module.exports = {
createAliasResolver,
getModules,
getAreEnumMembersInteger,
isDirectRecursiveMember,
isArrayRecursiveMember,
};
80 changes: 7 additions & 73 deletions packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,84 +91,18 @@ struct CustomHostObjectRef {
using CustomHostObject = HostObjectWrapper<CustomHostObjectRef>;

#pragma mark - recursive objects
struct BinaryTreeNode {
std::unique_ptr<BinaryTreeNode> left;
int32_t value;
std::unique_ptr<BinaryTreeNode> right;
};

template <>
struct Bridging<BinaryTreeNode> {
static BinaryTreeNode fromJs(
jsi::Runtime& rt,
const jsi::Object& value,
const std::shared_ptr<CallInvoker>& jsInvoker) {
BinaryTreeNode result{
value.hasProperty(rt, "left")
? std::make_unique<BinaryTreeNode>(bridging::fromJs<BinaryTreeNode>(
rt, value.getProperty(rt, "left"), jsInvoker))
: nullptr,
bridging::fromJs<int32_t>(
rt, value.getProperty(rt, "value"), jsInvoker),
value.hasProperty(rt, "right")
? std::make_unique<BinaryTreeNode>(bridging::fromJs<BinaryTreeNode>(
rt, value.getProperty(rt, "right"), jsInvoker))
: nullptr};
return result;
}
using BinaryTreeNode = NativeCxxModuleExampleCxxBinaryTreeNode<int32_t>;

static jsi::Object toJs(
jsi::Runtime& rt,
const BinaryTreeNode& value,
const std::shared_ptr<CallInvoker>& jsInvoker) {
auto result = facebook::jsi::Object(rt);
if (value.left) {
result.setProperty(
rt, "left", bridging::toJs(rt, *value.left, jsInvoker));
}
result.setProperty(rt, "value", bridging::toJs(rt, value.value, jsInvoker));
if (value.right) {
result.setProperty(
rt, "right", bridging::toJs(rt, *value.right, jsInvoker));
}
return result;
}
};
template <>
struct Bridging<BinaryTreeNode>
: NativeCxxModuleExampleCxxBinaryTreeNodeBridging<BinaryTreeNode> {};

struct GraphNode {
std::string label;
std::optional<std::vector<GraphNode>> neighbors;
};
using GraphNode = NativeCxxModuleExampleCxxGraphNode<std::string>;

template <>
struct Bridging<GraphNode> {
static GraphNode fromJs(
jsi::Runtime& rt,
const jsi::Object& value,
const std::shared_ptr<CallInvoker>& jsInvoker) {
GraphNode result{
bridging::fromJs<std::string>(
rt, value.getProperty(rt, "label"), jsInvoker),
bridging::fromJs<std::optional<std::vector<GraphNode>>>(
rt, value.getProperty(rt, "neighbors"), jsInvoker)};
return result;
}

static jsi::Object toJs(
jsi::Runtime& rt,
const GraphNode value,
const std::shared_ptr<CallInvoker>& jsInvoker) {
auto result = facebook::jsi::Object(rt);
result.setProperty(rt, "label", bridging::toJs(rt, value.label, jsInvoker));
if (value.neighbors) {
result.setProperty(
rt,
"neighbors",
bridging::toJs(rt, value.neighbors.value(), jsInvoker));
}
return result;
}
};
struct Bridging<GraphNode>
: NativeCxxModuleExampleCxxGraphNodeBridging<GraphNode> {};

#pragma mark - implementation
class NativeCxxModuleExample
Expand Down

0 comments on commit 749b8dd

Please sign in to comment.