mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Add EventEmitter code-gen support for C++ Turbo Modules (#44809)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/44809 Adding react-native-codegen parser support for a new `EventEmitter` property type on C++ Turbo Modules. It is possible to later expand this feature to other languages (Java, ObjC). ## Characteristics An `EventEmitter` must: - be non null: `EventEmitter<string>` works, `?EventEmitter<string>` does NOT - have a non null eventType: `EventEmitter<number>` works, `EventEmitter<?number>` does NOT - have at most 1 eventType, `void` is possible as well: `EventEmitter<>` or `EventEmitter<MyObject>` work - `EventEmitter<number, string>` do NOT - have a concrete eventType, `{}` is not allowed `EventEmitter<{}>` does NOT work - be used in `Cxx` Turbo Modules only at this time ## Example For these 4 eventEmitters in on an RN JS TM spec ``` +onPress: EventEmitter<void>; +onClick: EventEmitter<string>; +onChange: EventEmitter<ObjectStruct>; +onSubmit: EventEmitter<ObjectStruct[]>; ``` We now generate this code: 1.) in the spec based header `{MyModuleName}CxxSpec` in the constructor: ``` ... // existing code eventEmitterMap_["onPress"] = std::make_shared<AsyncEventEmitter<>>(); eventEmitterMap_["onClick"] = std::make_shared<AsyncEventEmitter<OnClickType>>(); eventEmitterMap_["onChange"] = std::make_shared<AsyncEventEmitter<OnChangeType>>(); eventEmitterMap_["onSubmit"] = std::make_shared<AsyncEventEmitter<OnSubmitType>>(); ``` 2.) as `protected` functions ``` void emitOnPress() { std::static_pointer_cast<AsyncEventEmitter<>>(delegate_.eventEmitterMap_["onPress"])->emit(); } void emitOnClick(const OnClickType& value) { std::static_pointer_cast<AsyncEventEmitter<OnClickType>>(delegate_.eventEmitterMap_["onClick"])->emit(value); } void emitOnChange(const OnChangeType& value) { std::static_pointer_cast<AsyncEventEmitter<OnChangeType>>(delegate_.eventEmitterMap_["onChange"])->emit(value); } void emitOnSubmit(const OnSubmitType& value) { std::static_pointer_cast<AsyncEventEmitter<OnSubmitType>>(delegate_.eventEmitterMap_["onSubmit"])->emit(value); } ``` ## Changelog: [General] [Added] - Add EventEmitter code-gen support for C++ Turbo Modules Reviewed By: javache Differential Revision: D57407871 fbshipit-source-id: 2345cc6dacf0cb0d45f8a374ad9d4cbf8082f9d6
This commit is contained in:
committed by
Facebook GitHub Bot
parent
bfb3b7008d
commit
fd618819c7
@@ -18,6 +18,7 @@ import type {
|
||||
NativeModuleEnumMap,
|
||||
NativeModuleEnumMembers,
|
||||
NativeModuleEnumMemberType,
|
||||
NativeModuleEventEmitterShape,
|
||||
NativeModuleFunctionTypeAnnotation,
|
||||
NativeModulePropertyShape,
|
||||
NativeModuleTypeAnnotation,
|
||||
@@ -28,7 +29,7 @@ import type {AliasResolver} from './Utils';
|
||||
|
||||
const {unwrapNullable} = require('../../parsers/parsers-commons');
|
||||
const {wrapOptional} = require('../TypeUtils/Cxx');
|
||||
const {getEnumName, toSafeCppString} = require('../Utils');
|
||||
const {getEnumName, toPascalCase, toSafeCppString} = require('../Utils');
|
||||
const {indent} = require('../Utils');
|
||||
const {
|
||||
createAliasResolver,
|
||||
@@ -64,10 +65,12 @@ public:
|
||||
const ModuleSpecClassDeclarationTemplate = ({
|
||||
hasteModuleName,
|
||||
moduleName,
|
||||
moduleEventEmitters,
|
||||
moduleProperties,
|
||||
}: $ReadOnly<{
|
||||
hasteModuleName: string,
|
||||
moduleName: string,
|
||||
moduleEventEmitters: EventEmitterCpp[],
|
||||
moduleProperties: string[],
|
||||
}>) => {
|
||||
return `template <typename T>
|
||||
@@ -83,16 +86,20 @@ protected:
|
||||
${hasteModuleName}CxxSpec(std::shared_ptr<CallInvoker> jsInvoker)
|
||||
: TurboModule(std::string{${hasteModuleName}CxxSpec::kModuleName}, jsInvoker),
|
||||
delegate_(reinterpret_cast<T*>(this), jsInvoker) {}
|
||||
${moduleEventEmitters.map(e => e.emitFunction).join('\n')}
|
||||
|
||||
private:
|
||||
class Delegate : public ${hasteModuleName}CxxSpecJSI {
|
||||
public:
|
||||
Delegate(T *instance, std::shared_ptr<CallInvoker> jsInvoker) :
|
||||
${hasteModuleName}CxxSpecJSI(std::move(jsInvoker)), instance_(instance) {}
|
||||
${hasteModuleName}CxxSpecJSI(std::move(jsInvoker)), instance_(instance) {
|
||||
${moduleEventEmitters.map(e => e.registerEventEmitter).join('\n')}
|
||||
}
|
||||
|
||||
${indent(moduleProperties.join('\n'), 4)}
|
||||
|
||||
private:
|
||||
friend class ${hasteModuleName}CxxSpec;
|
||||
T *instance_;
|
||||
};
|
||||
|
||||
@@ -460,7 +467,7 @@ function translatePropertyToCpp(
|
||||
resolveAlias: AliasResolver,
|
||||
enumMap: NativeModuleEnumMap,
|
||||
abstract: boolean = false,
|
||||
) {
|
||||
): string {
|
||||
const [propTypeAnnotation] =
|
||||
unwrapNullable<NativeModuleFunctionTypeAnnotation>(prop.typeAnnotation);
|
||||
|
||||
@@ -511,6 +518,69 @@ function translatePropertyToCpp(
|
||||
}`;
|
||||
}
|
||||
|
||||
type EventEmitterCpp = {
|
||||
isVoidTypeAnnotation: boolean,
|
||||
templateName: string,
|
||||
registerEventEmitter: string,
|
||||
emitFunction: string,
|
||||
};
|
||||
|
||||
function translateEventEmitterToCpp(
|
||||
moduleName: string,
|
||||
eventEmitter: NativeModuleEventEmitterShape,
|
||||
resolveAlias: AliasResolver,
|
||||
enumMap: NativeModuleEnumMap,
|
||||
): EventEmitterCpp {
|
||||
const isVoidTypeAnnotation =
|
||||
eventEmitter.typeAnnotation.typeAnnotation.type === 'VoidTypeAnnotation';
|
||||
const templateName = `${toPascalCase(eventEmitter.name)}Type`;
|
||||
const jsiType = translatePrimitiveJSTypeToCpp(
|
||||
moduleName,
|
||||
null,
|
||||
eventEmitter.typeAnnotation.typeAnnotation,
|
||||
false,
|
||||
typeName =>
|
||||
`Unsupported type for eventEmitter "${eventEmitter.name}" in ${moduleName}. Found: ${typeName}`,
|
||||
resolveAlias,
|
||||
enumMap,
|
||||
);
|
||||
const isArray = jsiType === 'jsi::Array';
|
||||
return {
|
||||
isVoidTypeAnnotation: isVoidTypeAnnotation,
|
||||
templateName: isVoidTypeAnnotation ? `/*${templateName}*/` : templateName,
|
||||
registerEventEmitter: ` eventEmitterMap_["${
|
||||
eventEmitter.name
|
||||
}"] = std::make_shared<AsyncEventEmitter<${
|
||||
isVoidTypeAnnotation ? '' : 'jsi::Value'
|
||||
}>>();`,
|
||||
emitFunction: `
|
||||
${
|
||||
isVoidTypeAnnotation ? '' : `template <typename ${templateName}> `
|
||||
}void emit${toPascalCase(eventEmitter.name)}(${
|
||||
isVoidTypeAnnotation
|
||||
? ''
|
||||
: `${isArray ? `std::vector<${templateName}>` : templateName} value`
|
||||
}) {${
|
||||
isVoidTypeAnnotation
|
||||
? ''
|
||||
: `
|
||||
static_assert(bridging::supportsFromJs<${
|
||||
isArray ? `std::vector<${templateName}>` : templateName
|
||||
}, ${jsiType}>, "value cannnot be converted to ${jsiType}");`
|
||||
}
|
||||
std::static_pointer_cast<AsyncEventEmitter<${
|
||||
isVoidTypeAnnotation ? '' : 'jsi::Value'
|
||||
}>>(delegate_.eventEmitterMap_["${eventEmitter.name}"])->emit(${
|
||||
isVoidTypeAnnotation
|
||||
? ''
|
||||
: `[jsInvoker = jsInvoker_, eventValue = value](jsi::Runtime& rt) -> jsi::Value {
|
||||
return bridging::toJs(rt, eventValue, jsInvoker);
|
||||
}`
|
||||
});
|
||||
}`,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
generate(
|
||||
libraryName: string,
|
||||
@@ -522,12 +592,8 @@ module.exports = {
|
||||
const nativeModules = getModules(schema);
|
||||
|
||||
const modules = Object.keys(nativeModules).flatMap(hasteModuleName => {
|
||||
const {
|
||||
aliasMap,
|
||||
enumMap,
|
||||
spec: {properties},
|
||||
moduleName,
|
||||
} = nativeModules[hasteModuleName];
|
||||
const {aliasMap, enumMap, spec, moduleName} =
|
||||
nativeModules[hasteModuleName];
|
||||
const resolveAlias = createAliasResolver(aliasMap);
|
||||
const structs = createStructsString(
|
||||
hasteModuleName,
|
||||
@@ -540,7 +606,7 @@ module.exports = {
|
||||
return [
|
||||
ModuleClassDeclarationTemplate({
|
||||
hasteModuleName,
|
||||
moduleProperties: properties.map(prop =>
|
||||
moduleProperties: spec.properties.map(prop =>
|
||||
translatePropertyToCpp(
|
||||
hasteModuleName,
|
||||
prop,
|
||||
@@ -555,7 +621,15 @@ module.exports = {
|
||||
ModuleSpecClassDeclarationTemplate({
|
||||
hasteModuleName,
|
||||
moduleName,
|
||||
moduleProperties: properties.map(prop =>
|
||||
moduleEventEmitters: spec.eventEmitters.map(eventEmitter =>
|
||||
translateEventEmitterToCpp(
|
||||
moduleName,
|
||||
eventEmitter,
|
||||
resolveAlias,
|
||||
enumMap,
|
||||
),
|
||||
),
|
||||
moduleProperties: spec.properties.map(prop =>
|
||||
translatePropertyToCpp(
|
||||
hasteModuleName,
|
||||
prop,
|
||||
|
||||
Reference in New Issue
Block a user