diff --git a/ReactCommon/react/bridging/Class.h b/ReactCommon/react/bridging/Class.h index c188334df83..d0cc976f543 100644 --- a/ReactCommon/react/bridging/Class.h +++ b/ReactCommon/react/bridging/Class.h @@ -50,6 +50,27 @@ T callFromJs( rt, fromJs(rt, std::forward(args), jsInvoker)...), jsInvoker); + } else if constexpr (is_optional_v) { + static_assert( + is_optional_v + ? supportsToJs + : supportsToJs, + "Incompatible return type"); + + auto result = toJs( + rt, + (instance->*method)( + rt, fromJs(rt, std::forward(args), jsInvoker)...), + jsInvoker); + + if constexpr (std::is_same_v) { + if (result.isNull() || result.isUndefined()) { + return std::nullopt; + } + } + + return convert(rt, std::move(result)); + } else { static_assert(std::is_convertible_v, "Incompatible return type"); diff --git a/ReactCommon/react/bridging/Convert.h b/ReactCommon/react/bridging/Convert.h index c11250a80d7..d2e0f8ed223 100644 --- a/ReactCommon/react/bridging/Convert.h +++ b/ReactCommon/react/bridging/Convert.h @@ -24,6 +24,15 @@ inline constexpr bool is_jsi_v = std::is_same_v> || std::is_base_of_v>; +template +struct is_optional : std::false_type {}; + +template +struct is_optional> : std::true_type {}; + +template +inline constexpr bool is_optional_v = is_optional::value; + template struct Converter; diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleCpp.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleCpp.js index 5bc19c9b8f7..5d7d481383b 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleCpp.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleCpp.js @@ -17,6 +17,7 @@ import type { NativeModulePropertyShape, NativeModuleFunctionTypeAnnotation, NativeModuleParamTypeAnnotation, + NativeModuleTypeAnnotation, } from '../../CodegenSchema'; import type {AliasResolver} from './Utils'; @@ -28,21 +29,33 @@ type FilesOutput = Map; const HostFunctionTemplate = ({ hasteModuleName, methodName, - isVoid, + returnTypeAnnotation, args, }: $ReadOnly<{ hasteModuleName: string, methodName: string, - isVoid: boolean, + returnTypeAnnotation: Nullable, args: Array, }>) => { + const isNullable = returnTypeAnnotation.type === 'NullableTypeAnnotation'; + const isVoid = returnTypeAnnotation.type === 'VoidTypeAnnotation'; const methodCallArgs = ['rt', ...args].join(', '); - const methodCall = `static_cast<${hasteModuleName}CxxSpecJSI *>(&turboModule)->${methodName}(${methodCallArgs});`; + const methodCall = `static_cast<${hasteModuleName}CxxSpecJSI *>(&turboModule)->${methodName}(${methodCallArgs})`; return `static jsi::Value __hostFunction_${hasteModuleName}CxxSpecJSI_${methodName}(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {${ - isVoid ? `\n ${methodCall}` : '' + isVoid + ? `\n ${methodCall};` + : isNullable + ? `\n auto result = ${methodCall};` + : '' } - return ${isVoid ? 'jsi::Value::undefined();' : methodCall} + return ${ + isVoid + ? 'jsi::Value::undefined()' + : isNullable + ? 'result ? jsi::Value(std::move(*result)) : jsi::Value::null()' + : methodCall + }; }`; }; @@ -173,13 +186,11 @@ function serializePropertyIntoHostFunction( ): string { const [propertyTypeAnnotation] = unwrapNullable(property.typeAnnotation); - const isVoid = - propertyTypeAnnotation.returnTypeAnnotation.type === 'VoidTypeAnnotation'; return HostFunctionTemplate({ hasteModuleName, methodName: property.name, - isVoid, + returnTypeAnnotation: propertyTypeAnnotation.returnTypeAnnotation, args: propertyTypeAnnotation.params.map((p, i) => serializeArg(p, i, resolveAlias), ), diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleCpp-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleCpp-test.js.snap index 02017d09bf4..93a57fc91bc 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleCpp-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleCpp-test.js.snap @@ -62,13 +62,16 @@ static jsi::Value __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getArrays(jsi return jsi::Value::undefined(); } static jsi::Value __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getNullableObject(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { - return static_cast(&turboModule)->getNullableObject(rt); + auto result = static_cast(&turboModule)->getNullableObject(rt); + return result ? jsi::Value(std::move(*result)) : jsi::Value::null(); } static jsi::Value __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getNullableGenericObject(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { - return static_cast(&turboModule)->getNullableGenericObject(rt); + auto result = static_cast(&turboModule)->getNullableGenericObject(rt); + return result ? jsi::Value(std::move(*result)) : jsi::Value::null(); } static jsi::Value __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getNullableArray(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { - return static_cast(&turboModule)->getNullableArray(rt); + auto result = static_cast(&turboModule)->getNullableArray(rt); + return result ? jsi::Value(std::move(*result)) : jsi::Value::null(); } NativeSampleTurboModuleCxxSpecJSI::NativeSampleTurboModuleCxxSpecJSI(std::shared_ptr jsInvoker) @@ -109,7 +112,8 @@ static jsi::Value __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getMixed(jsi: return static_cast(&turboModule)->getMixed(rt, jsi::Value(rt, args[0])); } static jsi::Value __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getNullableNumberFromNullableAlias(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { - return static_cast(&turboModule)->getNullableNumberFromNullableAlias(rt, args[0].isNull() || args[0].isUndefined() ? std::nullopt : std::make_optional(args[0].asObject(rt))); + auto result = static_cast(&turboModule)->getNullableNumberFromNullableAlias(rt, args[0].isNull() || args[0].isUndefined() ? std::nullopt : std::make_optional(args[0].asObject(rt))); + return result ? jsi::Value(std::move(*result)) : jsi::Value::null(); } NativeSampleTurboModuleCxxSpecJSI::NativeSampleTurboModuleCxxSpecJSI(std::shared_ptr jsInvoker)