From 451171acb163671ccfa347d865cd00336f199f4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Osadnik?= Date: Wed, 24 Jul 2019 08:23:41 -0700 Subject: [PATCH] generate bindings for objcpp TM Summary: In this diff I add generetion of bindings for objcpp TM. Reviewed By: RSNara Differential Revision: D16438572 fbshipit-source-id: e92163b1b42306eec931e465b89b176f0fd8a658 --- .../generators/modules/GenerateModuleMm.js | 160 +++++++++++++++ .../__tests__/GenerateModuleMm-test.js | 27 +++ .../GenerateModuleMm-test.js.snap | 189 ++++++++++++++++++ 3 files changed, 376 insertions(+) create mode 100644 packages/react-native-codegen/src/generators/modules/GenerateModuleMm.js create mode 100644 packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleMm-test.js create mode 100644 packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleMm-test.js.snap diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleMm.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleMm.js new file mode 100644 index 00000000000..ed1dab82ab2 --- /dev/null +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleMm.js @@ -0,0 +1,160 @@ +/** + * 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} from '../../CodegenSchema'; + +type FilesOutput = Map; + +const propertyHeaderTemplate = + 'static facebook::jsi::Value __hostFunction_Native::_MODULE_NAME_::SpecJSI_::_PROPERTY_NAME_::(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {'; + +const propertyCastTemplate = `static_cast(turboModule) + .invokeObjCMethod(rt, ::_KIND_::, "::_PROPERTY_NAME_::", @selector(::_PROPERTY_NAME_::::_ARGS_::), args, count);`; + +const propertyTemplate = ` +${propertyHeaderTemplate} + return ${propertyCastTemplate} +}`.trim(); + +const proprertyDefTemplate = + ' methodMap_["::_PROPERTY_NAME_::"] = MethodMetadata {::_ARGS_COUNT_::, __hostFunction_Native::_MODULE_NAME_::SpecJSI_::_PROPERTY_NAME_::};'; + +const moduleTemplate = ` +::_TURBOMODULE_METHOD_INVOKERS_:: + +Native::_MODULE_NAME_::SpecJSI::Native::_MODULE_NAME_::SpecJSI(id instance, std::shared_ptr jsInvoker) + : ObjCTurboModule("SampleTurboModule", instance, jsInvoker) { +::_PROPERTIES_MAP_:: +}`.trim(); + +const template = ` +/** + * 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. + */ + +#include + +namespace facebook { +namespace react { + +::_MODULES_:: + + +} // namespace react +} // namespace facebook +`; + +function translateReturnTypeToKind(type): string { + switch (type) { + case 'VoidTypeAnnotation': + return 'VoidKind'; + case 'StringTypeAnnotation': + return 'StringKind'; + case 'BooleanTypeAnnotation': + return 'BooleanKind'; + case 'NumberTypeAnnotation': + case 'FloatTypeAnnotation': + case 'Int32TypeAnnotation': + return 'NumberKind'; + case 'GenericPromiseTypeAnnotation': + return 'PromiseKind'; + case 'GenericObjectTypeAnnotation': + case 'ObjectTypeAnnotation': + return 'ObjectKind'; + case 'ArrayTypeAnnotation': + return 'ArrayKind'; + default: + (type: empty); + throw new Error(`Unknown prop type for returning value, found: ${type}"`); + } +} + +function tranlsateMethodForImplementation(property): string { + const numberOfParams = + property.typeAnnotation.params.length + + (property.typeAnnotation.returnTypeAnnotation.type === + 'GenericPromiseTypeAnnotation' + ? 2 + : 0); + const translatedArguments = property.typeAnnotation.params + .map(param => param.name) + .concat( + property.typeAnnotation.returnTypeAnnotation.type === + 'GenericPromiseTypeAnnotation' + ? ['resolve', 'reject'] + : [], + ) + .slice(1) + .join(':') + .concat(':'); + return propertyTemplate + .replace( + /::_KIND_::/g, + translateReturnTypeToKind( + property.typeAnnotation.returnTypeAnnotation.type, + ), + ) + .replace(/::_PROPERTY_NAME_::/g, property.name) + .replace( + /::_ARGS_::/g, + numberOfParams === 0 + ? '' + : (numberOfParams === 1 ? '' : ':') + translatedArguments, + ); +} + +module.exports = { + generate(libraryName: string, schema: SchemaType): FilesOutput { + const nativeModules = Object.keys(schema.modules) + .map(moduleName => { + const modules = schema.modules[moduleName].nativeModules; + if (modules == null) { + return null; + } + + return modules; + }) + .filter(Boolean) + .reduce((acc, modules) => Object.assign(acc, modules), {}); + + const modules = Object.keys(nativeModules) + .map(name => { + const {properties} = nativeModules[name]; + const translatedMethods = properties + .map(property => tranlsateMethodForImplementation(property)) + .join('\n'); + return moduleTemplate + .replace(/::_TURBOMODULE_METHOD_INVOKERS_::/g, translatedMethods) + .replace( + '::_PROPERTIES_MAP_::', + properties + .map(({name: propertyName, typeAnnotation: {params}}) => + proprertyDefTemplate + .replace(/::_PROPERTY_NAME_::/g, propertyName) + .replace(/::_ARGS_COUNT_::/g, params.length.toString()), + ) + .join('\n'), + ) + .replace(/::_MODULE_NAME_::/g, name); + }) + .join('\n'); + + const fileName = 'RCTNativeModules.mm'; + const replacedTemplate = template + .replace(/::_MODULES_::/g, modules) + .replace(/::_LIBRARY_NAME_::/g, libraryName); + return new Map([[fileName, replacedTemplate]]); + }, +}; diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleMm-test.js b/packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleMm-test.js new file mode 100644 index 00000000000..9edb472a33b --- /dev/null +++ b/packages/react-native-codegen/src/generators/modules/__tests__/GenerateModuleMm-test.js @@ -0,0 +1,27 @@ +/** + * 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. + * + * @emails oncall+react_native + * @flow strict-local + * @format + */ + +'use strict'; + +const fixtures = require('../__test_fixtures__/fixtures.js'); +const generator = require('../GenerateModuleMm.js'); + +describe('GenerateModuleHObjCpp', () => { + Object.keys(fixtures) + .sort() + .forEach(fixtureName => { + const fixture = fixtures[fixtureName]; + + it(`can generate fixture ${fixtureName}`, () => { + expect(generator.generate(fixtureName, fixture)).toMatchSnapshot(); + }); + }); +}); diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleMm-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleMm-test.js.snap new file mode 100644 index 00000000000..b5d62994196 --- /dev/null +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleMm-test.js.snap @@ -0,0 +1,189 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`GenerateModuleHObjCpp can generate fixture EMPTY_NATIVE_MODULES 1`] = ` +Map { + "RCTNativeModules.mm" => " +/** + * 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. + */ + +#include + +namespace facebook { +namespace react { + + + +NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(id instance, std::shared_ptr jsInvoker) + : ObjCTurboModule(\\"SampleTurboModule\\", instance, jsInvoker) { + +} + + +} // namespace react +} // namespace facebook +", +} +`; + +exports[`GenerateModuleHObjCpp can generate fixture SIMPLE_NATIVE_MODULES 1`] = ` +Map { + "RCTNativeModules.mm" => " +/** + * 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. + */ + +#include + +namespace facebook { +namespace react { + +static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getConstants(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule) + .invokeObjCMethod(rt, ObjectKind, \\"getConstants\\", @selector(getConstants), args, count); +} +static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_voidFunc(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule) + .invokeObjCMethod(rt, VoidKind, \\"voidFunc\\", @selector(voidFunc), args, count); +} +static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getBool(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule) + .invokeObjCMethod(rt, BooleanKind, \\"getBool\\", @selector(getBool:), args, count); +} +static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getNumber(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule) + .invokeObjCMethod(rt, NumberKind, \\"getNumber\\", @selector(getNumber:), args, count); +} +static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getString(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule) + .invokeObjCMethod(rt, StringKind, \\"getString\\", @selector(getString:), args, count); +} +static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getArray(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule) + .invokeObjCMethod(rt, ArrayKind, \\"getArray\\", @selector(getArray:), args, count); +} +static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getObject(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule) + .invokeObjCMethod(rt, ObjectKind, \\"getObject\\", @selector(getObject:), args, count); +} +static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getValue(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule) + .invokeObjCMethod(rt, ObjectKind, \\"getValue\\", @selector(getValue:y:z:), args, count); +} +static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getValueWithCallback(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule) + .invokeObjCMethod(rt, VoidKind, \\"getValueWithCallback\\", @selector(getValueWithCallback:), args, count); +} +static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_getValueWithPromise(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule) + .invokeObjCMethod(rt, PromiseKind, \\"getValueWithPromise\\", @selector(getValueWithPromise:resolve:reject:), args, count); +} + +NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(id instance, std::shared_ptr jsInvoker) + : ObjCTurboModule(\\"SampleTurboModule\\", instance, jsInvoker) { + methodMap_[\\"getConstants\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleSpecJSI_getConstants}; + methodMap_[\\"voidFunc\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleSpecJSI_voidFunc}; + methodMap_[\\"getBool\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getBool}; + methodMap_[\\"getNumber\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getNumber}; + methodMap_[\\"getString\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getString}; + methodMap_[\\"getArray\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getArray}; + methodMap_[\\"getObject\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getObject}; + methodMap_[\\"getValue\\"] = MethodMetadata {3, __hostFunction_NativeSampleTurboModuleSpecJSI_getValue}; + methodMap_[\\"getValueWithCallback\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getValueWithCallback}; + methodMap_[\\"getValueWithPromise\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getValueWithPromise}; +} + + +} // namespace react +} // namespace facebook +", +} +`; + +exports[`GenerateModuleHObjCpp can generate fixture TWO_MODULES_DIFFERENT_FILES 1`] = ` +Map { + "RCTNativeModules.mm" => " +/** + * 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. + */ + +#include + +namespace facebook { +namespace react { + +static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_voidFunc(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule) + .invokeObjCMethod(rt, VoidKind, \\"voidFunc\\", @selector(voidFunc), args, count); +} + +NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(id instance, std::shared_ptr jsInvoker) + : ObjCTurboModule(\\"SampleTurboModule\\", instance, jsInvoker) { + methodMap_[\\"voidFunc\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleSpecJSI_voidFunc}; +} +static facebook::jsi::Value __hostFunction_NativeSample2TurboModuleSpecJSI_voidFunc(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule) + .invokeObjCMethod(rt, VoidKind, \\"voidFunc\\", @selector(voidFunc), args, count); +} + +NativeSample2TurboModuleSpecJSI::NativeSample2TurboModuleSpecJSI(id instance, std::shared_ptr jsInvoker) + : ObjCTurboModule(\\"SampleTurboModule\\", instance, jsInvoker) { + methodMap_[\\"voidFunc\\"] = MethodMetadata {0, __hostFunction_NativeSample2TurboModuleSpecJSI_voidFunc}; +} + + +} // namespace react +} // namespace facebook +", +} +`; + +exports[`GenerateModuleHObjCpp can generate fixture TWO_MODULES_SAME_FILE 1`] = ` +Map { + "RCTNativeModules.mm" => " +/** + * 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. + */ + +#include + +namespace facebook { +namespace react { + +static facebook::jsi::Value __hostFunction_NativeSampleTurboModuleSpecJSI_voidFunc(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule) + .invokeObjCMethod(rt, VoidKind, \\"voidFunc\\", @selector(voidFunc), args, count); +} + +NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(id instance, std::shared_ptr jsInvoker) + : ObjCTurboModule(\\"SampleTurboModule\\", instance, jsInvoker) { + methodMap_[\\"voidFunc\\"] = MethodMetadata {0, __hostFunction_NativeSampleTurboModuleSpecJSI_voidFunc}; +} +static facebook::jsi::Value __hostFunction_NativeSample2TurboModuleSpecJSI_voidFunc(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { + return static_cast(turboModule) + .invokeObjCMethod(rt, VoidKind, \\"voidFunc\\", @selector(voidFunc), args, count); +} + +NativeSample2TurboModuleSpecJSI::NativeSample2TurboModuleSpecJSI(id instance, std::shared_ptr jsInvoker) + : ObjCTurboModule(\\"SampleTurboModule\\", instance, jsInvoker) { + methodMap_[\\"voidFunc\\"] = MethodMetadata {0, __hostFunction_NativeSample2TurboModuleSpecJSI_voidFunc}; +} + + +} // namespace react +} // namespace facebook +", +} +`;