Skip to content

Commit

Permalink
Support optional return types
Browse files Browse the repository at this point in the history
Summary:
This fixes an issue in the C++ TurboModule codegen where optional return types would result in a compiler error because they were not being defaulted to `null` when converting to `jsi::Value`.

Changelog:
Internal

Reviewed By: javache

Differential Revision: D36989312

fbshipit-source-id: 525f9ce7a5638ba5a655fa69ba9647978030ab0b
  • Loading branch information
appden authored and facebook-github-bot committed Jun 9, 2022
1 parent 47bd78f commit 68e4e91
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 12 deletions.
21 changes: 21 additions & 0 deletions ReactCommon/react/bridging/Class.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,27 @@ T callFromJs(
rt, fromJs<Args>(rt, std::forward<JSArgs>(args), jsInvoker)...),
jsInvoker);

} else if constexpr (is_optional_v<T>) {
static_assert(
is_optional_v<R>
? supportsToJs<typename R::value_type, typename T::value_type>
: supportsToJs<R, typename T::value_type>,
"Incompatible return type");

auto result = toJs(
rt,
(instance->*method)(
rt, fromJs<Args>(rt, std::forward<JSArgs>(args), jsInvoker)...),
jsInvoker);

if constexpr (std::is_same_v<decltype(result), jsi::Value>) {
if (result.isNull() || result.isUndefined()) {
return std::nullopt;
}
}

return convert(rt, std::move(result));

} else {
static_assert(std::is_convertible_v<R, T>, "Incompatible return type");

Expand Down
9 changes: 9 additions & 0 deletions ReactCommon/react/bridging/Convert.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ inline constexpr bool is_jsi_v =
std::is_same_v<jsi::String, remove_cvref_t<T>> ||
std::is_base_of_v<jsi::Object, remove_cvref_t<T>>;

template <typename>
struct is_optional : std::false_type {};

template <typename T>
struct is_optional<std::optional<T>> : std::true_type {};

template <typename T>
inline constexpr bool is_optional_v = is_optional<T>::value;

template <typename T>
struct Converter;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type {
NativeModulePropertyShape,
NativeModuleFunctionTypeAnnotation,
NativeModuleParamTypeAnnotation,
NativeModuleTypeAnnotation,
} from '../../CodegenSchema';

import type {AliasResolver} from './Utils';
Expand All @@ -28,21 +29,33 @@ type FilesOutput = Map<string, string>;
const HostFunctionTemplate = ({
hasteModuleName,
methodName,
isVoid,
returnTypeAnnotation,
args,
}: $ReadOnly<{
hasteModuleName: string,
methodName: string,
isVoid: boolean,
returnTypeAnnotation: Nullable<NativeModuleTypeAnnotation>,
args: Array<string>,
}>) => {
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
};
}`;
};

Expand Down Expand Up @@ -173,13 +186,11 @@ function serializePropertyIntoHostFunction(
): string {
const [propertyTypeAnnotation] =
unwrapNullable<NativeModuleFunctionTypeAnnotation>(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),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<NativeSampleTurboModuleCxxSpecJSI *>(&turboModule)->getNullableObject(rt);
auto result = static_cast<NativeSampleTurboModuleCxxSpecJSI *>(&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<NativeSampleTurboModuleCxxSpecJSI *>(&turboModule)->getNullableGenericObject(rt);
auto result = static_cast<NativeSampleTurboModuleCxxSpecJSI *>(&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<NativeSampleTurboModuleCxxSpecJSI *>(&turboModule)->getNullableArray(rt);
auto result = static_cast<NativeSampleTurboModuleCxxSpecJSI *>(&turboModule)->getNullableArray(rt);
return result ? jsi::Value(std::move(*result)) : jsi::Value::null();
}
NativeSampleTurboModuleCxxSpecJSI::NativeSampleTurboModuleCxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker)
Expand Down Expand Up @@ -109,7 +112,8 @@ static jsi::Value __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getMixed(jsi:
return static_cast<NativeSampleTurboModuleCxxSpecJSI *>(&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<NativeSampleTurboModuleCxxSpecJSI *>(&turboModule)->getNullableNumberFromNullableAlias(rt, args[0].isNull() || args[0].isUndefined() ? std::nullopt : std::make_optional(args[0].asObject(rt)));
auto result = static_cast<NativeSampleTurboModuleCxxSpecJSI *>(&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<CallInvoker> jsInvoker)
Expand Down

0 comments on commit 68e4e91

Please sign in to comment.