From 0351c57e6a585e99a1b1c336886eb4af31b80c28 Mon Sep 17 00:00:00 2001 From: Kevin Gozali Date: Fri, 11 Sep 2020 01:12:25 -0700 Subject: [PATCH] Codegen: Generate module C++ lookup function for JNI next to the spec classes Summary: The TurboModule system requires a lookup function to map the spec name (name used in JS) to the C++ TurboModule subclass. This is a pure C function. For now, generate one lookup function per set of modules found in the codegen schema. Changelog: [Internal] Reviewed By: hramos Differential Revision: D23618281 fbshipit-source-id: 889e07bdd4f2e5e93c4d14e60225f5b0c6683917 --- .gitignore | 2 + .../react/codegen/plugin/CodegenPlugin.java | 25 +++++---- .../plugin/CodegenPluginExtension.java | 16 ++++++ .../modules/GenerateModuleJniCpp.js | 15 ++++++ .../generators/modules/GenerateModuleJniH.js | 7 ++- .../GenerateModuleJniCpp-test.js.snap | 54 +++++++++++++++++++ .../GenerateModuleJniH-test.js.snap | 14 +++++ scripts/generate-native-modules-specs-cli.js | 8 +-- 8 files changed, 122 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index ad00da2eefa7e0..6e14c3fc7561c3 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,8 @@ project.xcworkspace # Gradle /build/ +/packages/react-native-codegen/android/build/ +/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/build /packages/rn-tester/android/app/build/ /packages/rn-tester/android/app/gradle/ /packages/rn-tester/android/app/gradlew diff --git a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPlugin.java b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPlugin.java index 4ea0a13e09f6f3..e9a7c0261c7102 100644 --- a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPlugin.java +++ b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPlugin.java @@ -105,20 +105,19 @@ public void apply(final Project project) { extension.codegenJavaPackageName, new File(generatedSrcDir, "java")); }); - // TODO: generate JNI C++ files. - task.commandLine("echo"); - } else { - ImmutableList execCommands = - new ImmutableList.Builder() - .add("yarn") - .addAll(ImmutableList.copyOf(extension.nodeExecutableAndArgs)) - .add(extension.codegenGenerateNativeModuleSpecsCLI().getAbsolutePath()) - .add("android") - .add(generatedSchemaFile.getAbsolutePath()) - .add(outputDir.getAbsolutePath()) - .build(); - task.commandLine(execCommands); } + + ImmutableList execCommands = + new ImmutableList.Builder() + .add("yarn") + .addAll(ImmutableList.copyOf(extension.nodeExecutableAndArgs)) + .add(extension.codegenGenerateNativeModuleSpecsCLI().getAbsolutePath()) + .add("android") + .add(generatedSchemaFile.getAbsolutePath()) + .add(outputDir.getAbsolutePath()) + .add(extension.libraryName) + .build(); + task.commandLine(execCommands); }); // 4. Add dependencies & generated sources to the project. diff --git a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPluginExtension.java b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPluginExtension.java index 658230bd459337..97817617d947a5 100644 --- a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPluginExtension.java +++ b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPluginExtension.java @@ -7,7 +7,9 @@ package com.facebook.react.codegen.plugin; +import com.google.common.base.CaseFormat; import java.io.File; +import java.util.StringTokenizer; import org.gradle.api.Project; public class CodegenPluginExtension { @@ -15,12 +17,14 @@ public class CodegenPluginExtension { public String codegenJavaPackageName = "com.facebook.fbreact.specs.beta"; public boolean enableCodegen = false; public File jsRootDir; + public String libraryName; public String[] nodeExecutableAndArgs = {"node"}; public File reactNativeRootDir; public boolean useJavaGenerator = false; public CodegenPluginExtension(final Project project) { this.reactNativeRootDir = new File(project.getRootDir(), "node_modules/react-native"); + this.libraryName = projectPathToLibraryName(project.getPath()); } public File codegenDir() { @@ -34,4 +38,16 @@ public File codegenGenerateSchemaCLI() { public File codegenGenerateNativeModuleSpecsCLI() { return new File(this.reactNativeRootDir, "scripts/generate-native-modules-specs-cli.js"); } + + private String projectPathToLibraryName(final String projectPath) { + final StringTokenizer tokenizer = new StringTokenizer(projectPath, ":-_."); + final StringBuilder nameBuilder = new StringBuilder(); + + while (tokenizer.hasMoreTokens()) { + nameBuilder.append(CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, tokenizer.nextToken())); + } + nameBuilder.append("Spec"); + + return nameBuilder.toString(); + } } diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js index 45182b9bbc4c00..1af032bbc26e1e 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js @@ -44,6 +44,10 @@ Native::_MODULE_NAME_::SpecJSI::Native::_MODULE_NAME_::SpecJSI(const JavaTurboMo ::_PROPERTIES_MAP_:: }`.trim(); +const oneModuleLookupTemplate = ` if (moduleName == "::_MODULE_NAME_::") { + return std::make_shared(params); + }`; + const template = ` /** * Copyright (c) Facebook, Inc. and its affiliates. @@ -61,6 +65,10 @@ namespace react { ::_MODULES_:: +std::shared_ptr ::_LIBRARY_NAME_::_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms) { +::_MODULE_LOOKUP_:: +} + } // namespace react } // namespace facebook `; @@ -323,10 +331,17 @@ module.exports = { }) .join('\n'); + const moduleLookup = Object.keys(nativeModules) + .map(name => { + return oneModuleLookupTemplate.replace(/::_MODULE_NAME_::/g, name); + }) + .join('\n'); + const fileName = `${moduleSpecName}-generated.cpp`; const replacedTemplate = template .replace(/::_MODULES_::/g, modules) .replace(/::_LIBRARY_NAME_::/g, libraryName) + .replace(/::_MODULE_LOOKUP_::/g, moduleLookup) .replace(/::_INCLUDE_::/g, `"${moduleSpecName}.h"`); return new Map([[fileName, replacedTemplate]]); }, diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleJniH.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleJniH.js index 8b27ad3cb39b7f..1b50545f5f4071 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleJniH.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleJniH.js @@ -43,6 +43,8 @@ namespace react { ::_MODULES_:: +std::shared_ptr ::_LIBRARY_NAME_::_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms); + } // namespace react } // namespace facebook `; @@ -71,8 +73,9 @@ module.exports = { .join('\n'); const fileName = `${moduleSpecName}.h`; - const replacedTemplate = template.replace(/::_MODULES_::/g, modules); - + 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__/__snapshots__/GenerateModuleJniCpp-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniCpp-test.js.snap index 3baa888f467312..d12482b2b57f7a 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniCpp-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniCpp-test.js.snap @@ -42,6 +42,12 @@ NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const JavaTurboMo methodMap_[\\"getArrays\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getArrays}; } +std::shared_ptr COMPLEX_OBJECTS_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms) { + if (moduleName == \\"SampleTurboModule\\") { + return std::make_shared(params); + } +} + } // namespace react } // namespace facebook ", @@ -72,6 +78,12 @@ NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const JavaTurboMo } +std::shared_ptr EMPTY_NATIVE_MODULES_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms) { + if (moduleName == \\"SampleTurboModule\\") { + return std::make_shared(params); + } +} + } // namespace react } // namespace facebook ", @@ -107,6 +119,12 @@ NativeAliasTurboModuleSpecJSI::NativeAliasTurboModuleSpecJSI(const JavaTurboModu methodMap_[\\"cropImage\\"] = MethodMetadata {1, __hostFunction_NativeAliasTurboModuleSpecJSI_cropImage}; } +std::shared_ptr NATIVE_MODULES_WITH_TYPE_ALIASES_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms) { + if (moduleName == \\"AliasTurboModule\\") { + return std::make_shared(params); + } +} + } // namespace react } // namespace facebook ", @@ -190,6 +208,18 @@ NativeExceptionsManagerSpecJSI::NativeExceptionsManagerSpecJSI(const JavaTurboMo methodMap_[\\"dismissRedbox\\"] = MethodMetadata {0, __hostFunction_NativeExceptionsManagerSpecJSI_dismissRedbox}; } +std::shared_ptr REAL_MODULE_EXAMPLE_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms) { + if (moduleName == \\"CameraRollManager\\") { + return std::make_shared(params); + } + if (moduleName == \\"ImagePickerIOS\\") { + return std::make_shared(params); + } + if (moduleName == \\"ExceptionsManager\\") { + return std::make_shared(params); + } +} + } // namespace react } // namespace facebook ", @@ -273,6 +303,12 @@ NativeSampleTurboModuleSpecJSI::NativeSampleTurboModuleSpecJSI(const JavaTurboMo methodMap_[\\"getValueWithPromise\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleSpecJSI_getValueWithPromise}; } +std::shared_ptr SIMPLE_NATIVE_MODULES_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms) { + if (moduleName == \\"SampleTurboModule\\") { + return std::make_shared(params); + } +} + } // namespace react } // namespace facebook ", @@ -317,6 +353,15 @@ NativeSample2TurboModuleSpecJSI::NativeSample2TurboModuleSpecJSI(const JavaTurbo methodMap_[\\"voidFunc\\"] = MethodMetadata {0, __hostFunction_NativeSample2TurboModuleSpecJSI_voidFunc}; } +std::shared_ptr TWO_MODULES_DIFFERENT_FILES_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms) { + if (moduleName == \\"SampleTurboModule\\") { + return std::make_shared(params); + } + if (moduleName == \\"Sample2TurboModule\\") { + return std::make_shared(params); + } +} + } // namespace react } // namespace facebook ", @@ -359,6 +404,15 @@ NativeSample2TurboModuleSpecJSI::NativeSample2TurboModuleSpecJSI(const JavaTurbo methodMap_[\\"voidFunc\\"] = MethodMetadata {0, __hostFunction_NativeSample2TurboModuleSpecJSI_voidFunc}; } +std::shared_ptr TWO_MODULES_SAME_FILE_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms) { + if (moduleName == \\"SampleTurboModule\\") { + return std::make_shared(params); + } + if (moduleName == \\"Sample2TurboModule\\") { + return std::make_shared(params); + } +} + } // namespace react } // namespace facebook ", diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniH-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniH-test.js.snap index 6308dceddc0b5f..afb55a73886ba9 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniH-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleJniH-test.js.snap @@ -29,6 +29,8 @@ public: }; +std::shared_ptr COMPLEX_OBJECTS_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms); + } // namespace react } // namespace facebook ", @@ -64,6 +66,8 @@ public: }; +std::shared_ptr EMPTY_NATIVE_MODULES_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms); + } // namespace react } // namespace facebook ", @@ -99,6 +103,8 @@ public: }; +std::shared_ptr NATIVE_MODULES_WITH_TYPE_ALIASES_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms); + } // namespace react } // namespace facebook ", @@ -150,6 +156,8 @@ public: }; +std::shared_ptr REAL_MODULE_EXAMPLE_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms); + } // namespace react } // namespace facebook ", @@ -185,6 +193,8 @@ public: }; +std::shared_ptr SIMPLE_NATIVE_MODULES_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms); + } // namespace react } // namespace facebook ", @@ -228,6 +238,8 @@ public: }; +std::shared_ptr TWO_MODULES_DIFFERENT_FILES_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms); + } // namespace react } // namespace facebook ", @@ -271,6 +283,8 @@ public: }; +std::shared_ptr TWO_MODULES_SAME_FILE_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms); + } // namespace react } // namespace facebook ", diff --git a/scripts/generate-native-modules-specs-cli.js b/scripts/generate-native-modules-specs-cli.js index a86d9103577c7b..66b2d60c4f1384 100644 --- a/scripts/generate-native-modules-specs-cli.js +++ b/scripts/generate-native-modules-specs-cli.js @@ -15,9 +15,8 @@ const mkdirp = require('mkdirp'); const os = require('os'); const path = require('path'); -function generateSpec(platform, schemaPath, outputDirectory) { - const libraryName = 'FBReactNativeSpec'; - const moduleSpecName = 'FBReactNativeSpec'; +function generateSpec(platform, schemaPath, outputDirectory, libraryName) { + const moduleSpecName = libraryName; const schemaText = fs.readFileSync(schemaPath, 'utf-8'); if (schemaText == null) { @@ -97,7 +96,8 @@ function main() { const platform = args[0]; const schemaPath = args[1]; const outputDir = args[2]; - generateSpec(platform, schemaPath, outputDir); + const libraryName = args[3] || 'FBReactNativeSpec'; + generateSpec(platform, schemaPath, outputDir, libraryName); } main();