Add function properties to C++ TM complex datatyes (#41810)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/41810

Changelog: [Internal]

Reviewed By: zeyap

Differential Revision: D51858746

fbshipit-source-id: e45ee68fc40e8b7ae3388ecdcedfec646d022e19
This commit is contained in:
Christoph Purrer
2023-12-05 23:27:06 -08:00
committed by Facebook GitHub Bot
parent 616a907798
commit db609ff0c6
8 changed files with 280 additions and 1 deletions
@@ -1706,6 +1706,65 @@ const CXX_ONLY_NATIVE_MODULES: SchemaType = {
},
],
},
MenuItem: {
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'label',
optional: false,
typeAnnotation: {
type: 'StringTypeAnnotation',
},
},
{
name: 'onPress',
optional: false,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: {
type: 'VoidTypeAnnotation',
},
params: [
{
name: 'value',
optional: false,
typeAnnotation: {
type: 'StringTypeAnnotation',
},
},
{
name: 'flag',
optional: false,
typeAnnotation: {
type: 'BooleanTypeAnnotation',
},
},
],
},
},
{
name: 'shortcut',
optional: true,
typeAnnotation: {
type: 'NullableTypeAnnotation',
typeAnnotation: {
type: 'StringTypeAnnotation',
},
},
},
{
name: 'items',
optional: true,
typeAnnotation: {
type: 'ArrayTypeAnnotation',
elementType: {
type: 'TypeAliasTypeAnnotation',
name: 'MenuItem',
},
},
},
],
},
},
enumMap: {
EnumInt: {
@@ -2236,6 +2295,26 @@ const CXX_ONLY_NATIVE_MODULES: SchemaType = {
params: [],
},
},
{
name: 'setMenu',
optional: false,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: {
type: 'VoidTypeAnnotation',
},
params: [
{
name: 'menuItem',
optional: false,
typeAnnotation: {
type: 'TypeAliasTypeAnnotation',
name: 'MenuItem',
},
},
],
},
},
{
name: 'emitCustomDeviceEvent',
optional: false,
@@ -253,6 +253,13 @@ static jsi::Value __hostFunction_NativeSampleTurboModuleCxxSpecJSI_voidFunc(jsi:
);
return jsi::Value::undefined();
}
static jsi::Value __hostFunction_NativeSampleTurboModuleCxxSpecJSI_setMenu(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
static_cast<NativeSampleTurboModuleCxxSpecJSI *>(&turboModule)->setMenu(
rt,
args[0].asObject(rt)
);
return jsi::Value::undefined();
}
static jsi::Value __hostFunction_NativeSampleTurboModuleCxxSpecJSI_emitCustomDeviceEvent(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
static_cast<NativeSampleTurboModuleCxxSpecJSI *>(&turboModule)->emitCustomDeviceEvent(
rt,
@@ -308,6 +315,7 @@ NativeSampleTurboModuleCxxSpecJSI::NativeSampleTurboModuleCxxSpecJSI(std::shared
methodMap_[\\"getValueWithPromise\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getValueWithPromise};
methodMap_[\\"getWithWithOptionalArgs\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getWithWithOptionalArgs};
methodMap_[\\"voidFunc\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_voidFunc};
methodMap_[\\"setMenu\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_setMenu};
methodMap_[\\"emitCustomDeviceEvent\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_emitCustomDeviceEvent};
methodMap_[\\"voidFuncThrows\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_voidFuncThrows};
methodMap_[\\"getObjectThrows\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getObjectThrows};
@@ -582,6 +582,70 @@ struct [[deprecated(\\"Use SampleTurboModuleCxxValueStructBridging instead.\\")]
};
#pragma mark - SampleTurboModuleCxxBaseMenuItem
template <typename P0, typename P1, typename P2, typename P3>
struct [[deprecated(\\"Use SampleTurboModuleCxxMenuItem instead.\\")]] SampleTurboModuleCxxBaseMenuItem {
P0 label;
P1 onPress;
P2 shortcut;
P3 items;
bool operator==(const SampleTurboModuleCxxBaseMenuItem &other) const {
return label == other.label && onPress == other.onPress && shortcut == other.shortcut && items == other.items;
}
};
template <typename P0, typename P1, typename P2, typename P3>
struct [[deprecated(\\"Use SampleTurboModuleCxxMenuItemBridging instead.\\")]] SampleTurboModuleCxxBaseMenuItemBridging {
static SampleTurboModuleCxxBaseMenuItem<P0, P1, P2, P3> fromJs(
jsi::Runtime &rt,
const jsi::Object &value,
const std::shared_ptr<CallInvoker> &jsInvoker) {
SampleTurboModuleCxxBaseMenuItem<P0, P1, P2, P3> result{
bridging::fromJs<P0>(rt, value.getProperty(rt, \\"label\\"), jsInvoker),
bridging::fromJs<P1>(rt, value.getProperty(rt, \\"onPress\\"), jsInvoker),
bridging::fromJs<P2>(rt, value.getProperty(rt, \\"shortcut\\"), jsInvoker),
bridging::fromJs<P3>(rt, value.getProperty(rt, \\"items\\"), jsInvoker)};
return result;
}
#ifdef DEBUG
static jsi::String labelToJs(jsi::Runtime &rt, P0 value) {
return bridging::toJs(rt, value);
}
static jsi::Function onPressToJs(jsi::Runtime &rt, P1 value) {
return bridging::toJs(rt, value);
}
static std::optional<jsi::String> shortcutToJs(jsi::Runtime &rt, P2 value) {
return bridging::toJs(rt, value);
}
static jsi::Array itemsToJs(jsi::Runtime &rt, P3 value) {
return bridging::toJs(rt, value);
}
#endif
static jsi::Object toJs(
jsi::Runtime &rt,
const SampleTurboModuleCxxBaseMenuItem<P0, P1, P2, P3> &value,
const std::shared_ptr<CallInvoker> &jsInvoker) {
auto result = facebook::jsi::Object(rt);
result.setProperty(rt, \\"label\\", bridging::toJs(rt, value.label, jsInvoker));
result.setProperty(rt, \\"onPress\\", bridging::toJs(rt, value.onPress, jsInvoker));
if (value.shortcut) {
result.setProperty(rt, \\"shortcut\\", bridging::toJs(rt, value.shortcut.value(), jsInvoker));
}
if (value.items) {
result.setProperty(rt, \\"items\\", bridging::toJs(rt, value.items.value(), jsInvoker));
}
return result;
}
};
#pragma mark - SampleTurboModuleCxxConstantsStruct
template <typename P0, typename P1, typename P2>
@@ -857,6 +921,72 @@ struct SampleTurboModuleCxxValueStructBridging {
}
};
#pragma mark - SampleTurboModuleCxxMenuItem
template <typename P0, typename P1, typename P2>
struct SampleTurboModuleCxxMenuItem {
P0 label;
P1 onPress;
P2 shortcut;
std::optional<std::vector<SampleTurboModuleCxxMenuItem<P0, P1, P2>>> items;
bool operator==(const SampleTurboModuleCxxMenuItem &other) const {
return label == other.label && onPress == other.onPress && shortcut == other.shortcut && items == other.items;
}
};
template <typename T>
struct SampleTurboModuleCxxMenuItemBridging {
static T types;
static T fromJs(
jsi::Runtime &rt,
const jsi::Object &value,
const std::shared_ptr<CallInvoker> &jsInvoker) {
T result{
bridging::fromJs<decltype(types.label)>(rt, value.getProperty(rt, \\"label\\"), jsInvoker),
bridging::fromJs<decltype(types.onPress)>(rt, value.getProperty(rt, \\"onPress\\"), jsInvoker),
bridging::fromJs<decltype(types.shortcut)>(rt, value.getProperty(rt, \\"shortcut\\"), jsInvoker),
bridging::fromJs<decltype(types.items)>(rt, value.getProperty(rt, \\"items\\"), jsInvoker)};
return result;
}
#ifdef DEBUG
static jsi::String labelToJs(jsi::Runtime &rt, decltype(types.label) value) {
return bridging::toJs(rt, value);
}
static jsi::Function onPressToJs(jsi::Runtime &rt, decltype(types.onPress) value) {
return bridging::toJs(rt, value);
}
static std::optional<jsi::String> shortcutToJs(jsi::Runtime &rt, decltype(types.shortcut) value) {
return bridging::toJs(rt, value);
}
static jsi::Array itemsToJs(jsi::Runtime &rt, decltype(types.items) value) {
return bridging::toJs(rt, value);
}
#endif
static jsi::Object toJs(
jsi::Runtime &rt,
const T &value,
const std::shared_ptr<CallInvoker> &jsInvoker) {
auto result = facebook::jsi::Object(rt);
result.setProperty(rt, \\"label\\", bridging::toJs(rt, value.label, jsInvoker));
result.setProperty(rt, \\"onPress\\", bridging::toJs(rt, value.onPress, jsInvoker));
if (value.shortcut) {
result.setProperty(rt, \\"shortcut\\", bridging::toJs(rt, value.shortcut.value(), jsInvoker));
}
if (value.items) {
result.setProperty(rt, \\"items\\", bridging::toJs(rt, value.items.value(), jsInvoker));
}
return result;
}
};
class JSI_EXPORT NativeSampleTurboModuleCxxSpecJSI : public TurboModule {
protected:
NativeSampleTurboModuleCxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker);
@@ -883,6 +1013,7 @@ public:
virtual jsi::Value getValueWithPromise(jsi::Runtime &rt, bool error) = 0;
virtual std::optional<bool> getWithWithOptionalArgs(jsi::Runtime &rt, std::optional<bool> optionalArg) = 0;
virtual void voidFunc(jsi::Runtime &rt) = 0;
virtual void setMenu(jsi::Runtime &rt, jsi::Object menuItem) = 0;
virtual void emitCustomDeviceEvent(jsi::Runtime &rt, jsi::String eventName) = 0;
virtual void voidFuncThrows(jsi::Runtime &rt) = 0;
virtual jsi::Object getObjectThrows(jsi::Runtime &rt, jsi::Object arg) = 0;
@@ -1079,6 +1210,14 @@ private:
return bridging::callFromJs<void>(
rt, &T::voidFunc, jsInvoker_, instance_);
}
void setMenu(jsi::Runtime &rt, jsi::Object menuItem) override {
static_assert(
bridging::getParameterCount(&T::setMenu) == 2,
\\"Expected setMenu(...) to have 2 parameters\\");
return bridging::callFromJs<void>(
rt, &T::setMenu, jsInvoker_, instance_, std::move(menuItem));
}
void emitCustomDeviceEvent(jsi::Runtime &rt, jsi::String eventName) override {
static_assert(
bridging::getParameterCount(&T::emitCustomDeviceEvent) == 2,
@@ -235,7 +235,7 @@ function parseObjectProperty(
);
if (
propertyTypeAnnotation.type === 'FunctionTypeAnnotation' ||
(propertyTypeAnnotation.type === 'FunctionTypeAnnotation' && !cxxOnly) ||
propertyTypeAnnotation.type === 'PromiseTypeAnnotation' ||
propertyTypeAnnotation.type === 'VoidTypeAnnotation'
) {
@@ -169,6 +169,15 @@ void NativeCxxModuleExample::voidFunc(jsi::Runtime& rt) {
// Nothing to do
}
void NativeCxxModuleExample::setMenu(jsi::Runtime& rt, MenuItem menuItem) {
menuItem.onPress("value", true);
if (menuItem.items) {
for (auto subMenuItem : *menuItem.items) {
subMenuItem.onPress("another value", false);
}
}
}
void NativeCxxModuleExample::emitCustomDeviceEvent(
jsi::Runtime& rt,
jsi::String eventName) {
@@ -104,6 +104,17 @@ template <>
struct Bridging<GraphNode>
: NativeCxxModuleExampleCxxGraphNodeBridging<GraphNode> {};
#pragma mark - functional object properties
using MenuItem = NativeCxxModuleExampleCxxMenuItem<
std::string,
AsyncCallback<std::string, bool>,
std::optional<std::string>>;
template <>
struct Bridging<MenuItem>
: NativeCxxModuleExampleCxxMenuItemBridging<MenuItem> {};
#pragma mark - implementation
class NativeCxxModuleExample
: public NativeCxxModuleExampleCxxSpec<NativeCxxModuleExample> {
@@ -171,6 +182,8 @@ class NativeCxxModuleExample
void voidFunc(jsi::Runtime& rt);
void setMenu(jsi::Runtime& rt, MenuItem menuItem);
void emitCustomDeviceEvent(jsi::Runtime& rt, jsi::String eventName);
void voidFuncThrows(jsi::Runtime& rt);
@@ -67,6 +67,13 @@ export type GraphNode = {
neighbors?: Array<GraphNode>,
};
export type MenuItem = {
label: string,
onPress: (value: string, flag: boolean) => void,
shortcut?: ?string,
items?: Array<MenuItem>,
};
export interface Spec extends TurboModule {
+getArray: (arg: Array<ObjectStruct | null>) => Array<ObjectStruct | null>;
+getBool: (arg: boolean) => boolean;
@@ -92,6 +99,7 @@ export interface Spec extends TurboModule {
+getValueWithPromise: (error: boolean) => Promise<string>;
+getWithWithOptionalArgs: (optionalArg?: boolean) => ?boolean;
+voidFunc: () => void;
+setMenu: (menuItem: MenuItem) => void;
+emitCustomDeviceEvent: (eventName: string) => void;
+voidFuncThrows: () => void;
+getObjectThrows: (arg: ObjectStruct) => ObjectStruct;
@@ -57,6 +57,7 @@ type Examples =
| 'promise'
| 'rejectPromise'
| 'voidFunc'
| 'setMenuItem'
| 'optionalArgs'
| 'emitDeviceEvent';
@@ -135,6 +136,28 @@ class NativeCxxModuleExampleExample extends React.Component<{||}, State> {
.then(() => {})
.catch(e => this._setResult('rejectPromise', e.message)),
voidFunc: () => NativeCxxModuleExample?.voidFunc(),
setMenuItem: () => {
let curValue = '';
NativeCxxModuleExample?.setMenu({
label: 'File',
onPress: (value: string, flag: boolean) => {
curValue = `${value}: ${flag.toString()}`;
this._setResult('setMenuItem', curValue);
},
items: [
{
label: 'Open',
onPress: (value: string, flag: boolean) => {
this._setResult(
'setMenuItem',
`${curValue} - ${value}: ${flag.toString()}`,
);
},
},
],
shortcut: 'ctrl+shift+f',
});
},
optionalArgs: () => NativeCxxModuleExample?.getWithWithOptionalArgs(),
emitDeviceEvent: () => {
const CUSTOM_EVENT_TYPE = 'myCustomDeviceEvent';