mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Summary: Reserved type annotations can appear in three different contexts: commands, props, and NativeModules. For now, commands and NativeModules share the same reserved type annotations. In the future, we may want to merge these reserved type annotations with the props reserved type annotations. **Motivation:** The meaning of FunctionValue in FunctionValueTypeAnnotation isn't clear - in fact, it's downright confusing. Therefore, this diff renames this Flow type to ReservedTypeAnnotation, which I believe sufficiently captures the intent of the type annotation. Changelog: [Internal] Reviewed By: yungsters Differential Revision: D24701322 fbshipit-source-id: bde0273b4a89c9e7175c60ed3468ed870b320044
234 lines
6.3 KiB
JavaScript
234 lines
6.3 KiB
JavaScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow strict
|
|
* @format
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
import type {
|
|
SchemaType,
|
|
NativeModulePropertySchema,
|
|
NativeModuleMethodParamSchema,
|
|
NativeModuleFunctionTypeAnnotation,
|
|
NativeModuleParamTypeAnnotation,
|
|
} from '../../CodegenSchema';
|
|
|
|
import type {AliasResolver} from './Utils';
|
|
const {createAliasResolver, getModules} = require('./Utils');
|
|
const {unwrapNullable} = require('../../parsers/flow/modules/utils');
|
|
|
|
type FilesOutput = Map<string, string>;
|
|
|
|
const HostFunctionTemplate = ({
|
|
hasteModuleName,
|
|
methodName,
|
|
isVoid,
|
|
args,
|
|
}: $ReadOnly<{
|
|
hasteModuleName: string,
|
|
methodName: string,
|
|
isVoid: boolean,
|
|
args: Array<string>,
|
|
}>) => {
|
|
const methodCallArgs = ['rt', ...args].join(', ');
|
|
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}` : ''
|
|
}
|
|
return ${isVoid ? 'jsi::Value::undefined();' : methodCall}
|
|
}`;
|
|
};
|
|
|
|
const ModuleTemplate = ({
|
|
hasteModuleName,
|
|
hostFunctions,
|
|
moduleName,
|
|
methods,
|
|
}: $ReadOnly<{|
|
|
hasteModuleName: string,
|
|
hostFunctions: $ReadOnlyArray<string>,
|
|
moduleName: string,
|
|
methods: $ReadOnlyArray<
|
|
$ReadOnly<{|methodName: string, paramCount: number|}>,
|
|
>,
|
|
|}>) => {
|
|
return `${hostFunctions.join('\n')}
|
|
|
|
${hasteModuleName}CxxSpecJSI::${hasteModuleName}CxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker)
|
|
: TurboModule("${moduleName}", jsInvoker) {
|
|
${methods
|
|
.map(({methodName, paramCount}) => {
|
|
return ` methodMap_["${methodName}"] = MethodMetadata {${paramCount}, __hostFunction_${hasteModuleName}CxxSpecJSI_${methodName}};`;
|
|
})
|
|
.join('\n')}
|
|
}`;
|
|
};
|
|
|
|
const FileTemplate = ({
|
|
libraryName,
|
|
modules,
|
|
}: $ReadOnly<{
|
|
libraryName: string,
|
|
modules: string,
|
|
}>) => {
|
|
return `/**
|
|
* ${'C'}opyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* ${'@'}generated by codegen project: GenerateModuleH.js
|
|
*/
|
|
|
|
#include <react/modules/${libraryName}/NativeModules.h>
|
|
|
|
namespace facebook {
|
|
namespace react {
|
|
|
|
${modules}
|
|
|
|
|
|
} // namespace react
|
|
} // namespace facebook
|
|
`;
|
|
};
|
|
|
|
function serializeArg(
|
|
arg: NativeModuleMethodParamSchema,
|
|
index: number,
|
|
resolveAlias: AliasResolver,
|
|
): string {
|
|
function wrap(suffix) {
|
|
return `args[${index}]${suffix}`;
|
|
}
|
|
const {typeAnnotation: nullableTypeAnnotation} = arg;
|
|
const [typeAnnotation] = unwrapNullable<NativeModuleParamTypeAnnotation>(
|
|
nullableTypeAnnotation,
|
|
);
|
|
|
|
let realTypeAnnotation = typeAnnotation;
|
|
if (realTypeAnnotation.type === 'TypeAliasTypeAnnotation') {
|
|
realTypeAnnotation = resolveAlias(realTypeAnnotation.name);
|
|
}
|
|
|
|
switch (realTypeAnnotation.type) {
|
|
case 'ReservedTypeAnnotation':
|
|
switch (realTypeAnnotation.name) {
|
|
case 'RootTag':
|
|
return wrap('.getNumber()');
|
|
default:
|
|
(realTypeAnnotation.name: empty);
|
|
throw new Error(
|
|
`Unknown prop type for "${arg.name}, found: ${realTypeAnnotation.name}"`,
|
|
);
|
|
}
|
|
case 'StringTypeAnnotation':
|
|
return wrap('.getString(rt)');
|
|
case 'BooleanTypeAnnotation':
|
|
return wrap('.getBool()');
|
|
case 'NumberTypeAnnotation':
|
|
return wrap('.getNumber()');
|
|
case 'FloatTypeAnnotation':
|
|
return wrap('.getNumber()');
|
|
case 'DoubleTypeAnnotation':
|
|
return wrap('.getNumber()');
|
|
case 'Int32TypeAnnotation':
|
|
return wrap('.getNumber()');
|
|
case 'ArrayTypeAnnotation':
|
|
return wrap('.getObject(rt).getArray(rt)');
|
|
case 'FunctionTypeAnnotation':
|
|
return `std::move(${wrap('.getObject(rt).getFunction(rt)')})`;
|
|
case 'GenericObjectTypeAnnotation':
|
|
return wrap('.getObject(rt)');
|
|
case 'ObjectTypeAnnotation':
|
|
return wrap('.getObject(rt)');
|
|
default:
|
|
(realTypeAnnotation.type: empty);
|
|
throw new Error(
|
|
`Unknown prop type for "${arg.name}, found: ${realTypeAnnotation.type}"`,
|
|
);
|
|
}
|
|
}
|
|
|
|
function serializePropertyIntoHostFunction(
|
|
hasteModuleName: string,
|
|
property: NativeModulePropertySchema,
|
|
resolveAlias: AliasResolver,
|
|
): string {
|
|
const [
|
|
propertyTypeAnnotation,
|
|
] = unwrapNullable<NativeModuleFunctionTypeAnnotation>(
|
|
property.typeAnnotation,
|
|
);
|
|
const isVoid =
|
|
propertyTypeAnnotation.returnTypeAnnotation.type === 'VoidTypeAnnotation';
|
|
|
|
return HostFunctionTemplate({
|
|
hasteModuleName,
|
|
methodName: property.name,
|
|
isVoid,
|
|
args: propertyTypeAnnotation.params.map((p, i) =>
|
|
serializeArg(p, i, resolveAlias),
|
|
),
|
|
});
|
|
}
|
|
|
|
module.exports = {
|
|
generate(
|
|
libraryName: string,
|
|
schema: SchemaType,
|
|
moduleSpecName: string,
|
|
packageName?: string,
|
|
): FilesOutput {
|
|
const nativeModules = getModules(schema);
|
|
|
|
const modules = Object.keys(nativeModules)
|
|
.map((hasteModuleName: string) => {
|
|
const nativeModule = nativeModules[hasteModuleName];
|
|
const {
|
|
aliases,
|
|
spec: {properties},
|
|
moduleNames,
|
|
} = nativeModule;
|
|
const resolveAlias = createAliasResolver(aliases);
|
|
const hostFunctions = properties.map(property =>
|
|
serializePropertyIntoHostFunction(
|
|
hasteModuleName,
|
|
property,
|
|
resolveAlias,
|
|
),
|
|
);
|
|
|
|
return ModuleTemplate({
|
|
hasteModuleName,
|
|
hostFunctions,
|
|
// TODO: What happens when there are more than one NativeModule requires?
|
|
moduleName: moduleNames[0],
|
|
methods: properties.map(
|
|
({name: propertyName, typeAnnotation: nullableTypeAnnotation}) => {
|
|
const [{params}] = unwrapNullable(nullableTypeAnnotation);
|
|
return {
|
|
methodName: propertyName,
|
|
paramCount: params.length,
|
|
};
|
|
},
|
|
),
|
|
});
|
|
})
|
|
.join('\n');
|
|
|
|
const fileName = 'NativeModules.cpp';
|
|
const replacedTemplate = FileTemplate({
|
|
modules,
|
|
libraryName,
|
|
});
|
|
return new Map([[fileName, replacedTemplate]]);
|
|
},
|
|
};
|