From ed941c6cd818407aea7a5919eff96b04e65befa8 Mon Sep 17 00:00:00 2001 From: Marc Rousavy Date: Mon, 23 Sep 2024 12:26:59 +0200 Subject: [PATCH] fix: Make `NativeNitroModules` a code-gen'd TurboModule again --- .../cpp/turbomodule/NativeNitroModules.cpp | 159 ++++++------------ .../cpp/turbomodule/NativeNitroModules.hpp | 22 ++- .../react-native-nitro-modules/package.json | 8 + ...esTurboModule.ts => NativeNitroModules.ts} | 16 +- ...odule.web.ts => NativeNitroModules.web.ts} | 0 .../src/NitroModules.ts | 4 +- 6 files changed, 79 insertions(+), 130 deletions(-) rename packages/react-native-nitro-modules/src/{NitroModulesTurboModule.ts => NativeNitroModules.ts} (65%) rename packages/react-native-nitro-modules/src/{NitroModulesTurboModule.web.ts => NativeNitroModules.web.ts} (100%) diff --git a/packages/react-native-nitro-modules/cpp/turbomodule/NativeNitroModules.cpp b/packages/react-native-nitro-modules/cpp/turbomodule/NativeNitroModules.cpp index 19427955b..df6c15791 100644 --- a/packages/react-native-nitro-modules/cpp/turbomodule/NativeNitroModules.cpp +++ b/packages/react-native-nitro-modules/cpp/turbomodule/NativeNitroModules.cpp @@ -17,133 +17,40 @@ namespace facebook::react { using namespace margelo::nitro; NativeNitroModules::NativeNitroModules(std::shared_ptr jsInvoker) - : TurboModule(kModuleName, jsInvoker), _callInvoker(jsInvoker) {} + : NativeNitroModulesCxxSpec(jsInvoker), _callInvoker(jsInvoker) {} NativeNitroModules::~NativeNitroModules() {} -jsi::Value NativeNitroModules::get(jsi::Runtime& runtime, const jsi::PropNameID& propName) { - std::string name = propName.utf8(runtime); - - if (name == "install") { - return jsi::Function::createFromHostFunction( - runtime, jsi::PropNameID::forUtf8(runtime, "install"), 0, - [this](jsi::Runtime& runtime, const jsi::Value& thisArg, const jsi::Value* args, size_t count) -> jsi::Value { - install(runtime); - return jsi::Value::undefined(); - }); - } - if (name == "createHybridObject") { - return jsi::Function::createFromHostFunction( - runtime, jsi::PropNameID::forUtf8(runtime, "createHybridObject"), 2, - [this](jsi::Runtime& runtime, const jsi::Value& thisArg, const jsi::Value* args, size_t count) -> jsi::Value { -#ifdef NITRO_DEBUG - if (count != 1 && count != 2) [[unlikely]] { - throw jsi::JSError(runtime, "NitroModules.createHybridObject(..) expects 1 or 2 arguments, but " + std::to_string(count) + - " were supplied!"); - } -#endif - jsi::String objectName = args[0].asString(runtime); - std::optional optionalArgs = std::nullopt; - if (count > 1) { - optionalArgs = args[1].asObject(runtime); - } - - return createHybridObject(runtime, objectName, optionalArgs); - }); - } - if (name == "hasHybridObject") { - return jsi::Function::createFromHostFunction( - runtime, jsi::PropNameID::forUtf8(runtime, "hasHybridObject"), 1, - [this](jsi::Runtime& runtime, const jsi::Value& thisArg, const jsi::Value* args, size_t count) -> jsi::Value { -#ifdef NITRO_DEBUG - if (count != 1) [[unlikely]] { - throw jsi::JSError(runtime, - "NitroModules.hasHybridObject(..) expects 1 argument (name), but received " + std::to_string(count) + "!"); - } -#endif - jsi::String objectName = args[0].asString(runtime); - return hasHybridObject(runtime, objectName); - }); - } - if (name == "getAllHybridObjectNames") { - return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "getAllHybridObjectNames"), 0, - [this](jsi::Runtime& runtime, const jsi::Value& thisArg, const jsi::Value* args, - size_t count) -> jsi::Value { return getAllHybridObjectNames(runtime); }); - } - if (name == "hasNativeState") { - return jsi::Function::createFromHostFunction( - runtime, jsi::PropNameID::forUtf8(runtime, "hasNativeState"), 1, - [](jsi::Runtime& runtime, const jsi::Value& thisArg, const jsi::Value* args, size_t count) -> jsi::Value { - jsi::Object object = args[0].asObject(runtime); - bool has = object.hasNativeState(runtime) && object.getNativeState(runtime) != nullptr; - return jsi::Value(has); - }); - } - if (name == "removeNativeState") { - return jsi::Function::createFromHostFunction( - runtime, jsi::PropNameID::forUtf8(runtime, "removeNativeState"), 1, - [](jsi::Runtime& runtime, const jsi::Value& thisArg, const jsi::Value* args, size_t count) -> jsi::Value { - jsi::Object object = args[0].asObject(runtime); - object.setNativeState(runtime, nullptr); - return jsi::Value::undefined(); - }); - } - if (name == "box") { - return jsi::Function::createFromHostFunction( - runtime, jsi::PropNameID::forUtf8(runtime, "box"), 1, - [](jsi::Runtime& runtime, const jsi::Value& thisArg, const jsi::Value* args, size_t count) -> jsi::Value { - jsi::Object object = args[0].asObject(runtime); -#ifdef NITRO_DEBUG - if (!object.hasNativeState(runtime)) { - std::string stringified = args[0].toString(runtime).utf8(runtime); - throw std::runtime_error("Cannot box object " + stringified + " - it does not have a NativeState!"); - } -#endif - - std::shared_ptr nativeState = object.getNativeState(runtime); - std::shared_ptr maybeHybridObject = std::dynamic_pointer_cast(nativeState); - if (maybeHybridObject == nullptr) { - std::string stringified = args[0].toString(runtime).utf8(runtime); - throw std::runtime_error("Cannot box object " + stringified + " - it has a NativeState, but it's not a HybridObject!"); - } +// Setup +void NativeNitroModules::install(jsi::Runtime& runtime) { + // Installs the global Dispatcher mechanism into this Runtime. + // This allows creating Promises and calling back to JS. + auto dispatcher = std::make_shared(_callInvoker); + Dispatcher::installRuntimeGlobalDispatcher(runtime, dispatcher); +} - auto boxed = std::make_shared(maybeHybridObject); - return jsi::Object::createFromHostObject(runtime, boxed); - }); - } - if (name == "buildType") { +jsi::String NativeNitroModules::getBuildType(jsi::Runtime& runtime) { #ifdef NITRO_DEBUG return jsi::String::createFromAscii(runtime, "debug"); #else return jsi::String::createFromAscii(runtime, "release"); #endif - } - - return jsi::Value::undefined(); } -void NativeNitroModules::install(jsi::Runtime& runtime) { - // Installs the global Dispatcher mechanism into this Runtime. - // This allows creating Promises and calling back to JS. - auto dispatcher = std::make_shared(_callInvoker); - Dispatcher::installRuntimeGlobalDispatcher(runtime, dispatcher); -} - -jsi::Value NativeNitroModules::createHybridObject(jsi::Runtime& runtime, const jsi::String& hybridObjectName, - const std::optional&) { - auto name = hybridObjectName.utf8(runtime); - // TODO: Pass args? Do we need that? - auto hybridObject = HybridObjectRegistry::createHybridObject(name.c_str()); - return hybridObject->toObject(runtime); +// Creating Hybrid Objects +jsi::Object NativeNitroModules::createHybridObject(jsi::Runtime& runtime, jsi::String name) { + auto hybridObjectName = name.utf8(runtime); + auto hybridObject = HybridObjectRegistry::createHybridObject(hybridObjectName); + return hybridObject->toObject(runtime).getObject(runtime); } -jsi::Value NativeNitroModules::hasHybridObject(jsi::Runtime& runtime, const jsi::String& hybridObjectName) { - std::string name = hybridObjectName.utf8(runtime); - bool exists = HybridObjectRegistry::hasHybridObject(name); +bool NativeNitroModules::hasHybridObject(jsi::Runtime& runtime, jsi::String name) { + std::string hybridObjectName = name.utf8(runtime); + bool exists = HybridObjectRegistry::hasHybridObject(hybridObjectName); return exists; } -jsi::Value NativeNitroModules::getAllHybridObjectNames(jsi::Runtime& runtime) { +jsi::Array NativeNitroModules::getAllHybridObjectNames(jsi::Runtime& runtime) { std::vector keys = HybridObjectRegistry::getAllHybridObjectNames(); jsi::Array array(runtime, keys.size()); for (size_t i = 0; i < keys.size(); i++) { @@ -152,4 +59,34 @@ jsi::Value NativeNitroModules::getAllHybridObjectNames(jsi::Runtime& runtime) { return array; } +// Boxing +jsi::Object NativeNitroModules::box(jsi::Runtime& runtime, jsi::Object object) { +#ifdef NITRO_DEBUG + if (!object.hasNativeState(runtime)) { + std::string stringified = jsi::Value(runtime, object).toString(runtime).utf8(runtime); + throw std::runtime_error("Cannot box object " + stringified + " - it does not have a NativeState!"); + } +#endif + + std::shared_ptr nativeState = object.getNativeState(runtime); + std::shared_ptr maybeHybridObject = std::dynamic_pointer_cast(nativeState); + if (maybeHybridObject == nullptr) { + std::string stringified = jsi::Value(runtime, object).toString(runtime).utf8(runtime); + throw std::runtime_error("Cannot box object " + stringified + " - it has a NativeState, but it's not a HybridObject!"); + } + + auto boxed = std::make_shared(maybeHybridObject); + return jsi::Object::createFromHostObject(runtime, boxed); +} + +// NativeState Helpers +bool NativeNitroModules::hasNativeState(jsi::Runtime& runtime, jsi::Object object) { + bool has = object.hasNativeState(runtime) && object.getNativeState(runtime) != nullptr; + return has; +} + +void NativeNitroModules::removeNativeState(jsi::Runtime& runtime, jsi::Object object) { + object.setNativeState(runtime, nullptr); +} + } // namespace facebook::react diff --git a/packages/react-native-nitro-modules/cpp/turbomodule/NativeNitroModules.hpp b/packages/react-native-nitro-modules/cpp/turbomodule/NativeNitroModules.hpp index bb3774c5b..fe43b2790 100644 --- a/packages/react-native-nitro-modules/cpp/turbomodule/NativeNitroModules.hpp +++ b/packages/react-native-nitro-modules/cpp/turbomodule/NativeNitroModules.hpp @@ -8,26 +8,34 @@ #pragma once #include +#include namespace facebook::react { using namespace facebook; // The base C++-based TurboModule. This is the entry point where all nitro modules get initialized. -class NativeNitroModules : public TurboModule { +class NativeNitroModules : public NativeNitroModulesCxxSpec { public: NativeNitroModules(std::shared_ptr jsInvoker); ~NativeNitroModules(); public: - jsi::Value get(jsi::Runtime& runtime, const jsi::PropNameID& propName) override; - // Setup void install(jsi::Runtime& runtime); - // Hybrid Objects stuff - jsi::Value createHybridObject(jsi::Runtime& runtime, const jsi::String& hybridObjectName, const std::optional& args); - jsi::Value hasHybridObject(jsi::Runtime& runtime, const jsi::String& hybridObjectName); - jsi::Value getAllHybridObjectNames(jsi::Runtime& runtime); + jsi::String getBuildType(jsi::Runtime &rt); + + // Creating Hybrid Objects + jsi::Object createHybridObject(jsi::Runtime &rt, jsi::String name); + bool hasHybridObject(jsi::Runtime &rt, jsi::String name); + jsi::Array getAllHybridObjectNames(jsi::Runtime &rt); + + // Boxing + jsi::Object box(jsi::Runtime &rt, jsi::Object obj); + + // NativeState Helpers + bool hasNativeState(jsi::Runtime &rt, jsi::Object obj); + void removeNativeState(jsi::Runtime &rt, jsi::Object obj); public: constexpr static auto kModuleName = "NitroModulesCxx"; diff --git a/packages/react-native-nitro-modules/package.json b/packages/react-native-nitro-modules/package.json index e69bb743f..ab86ca9d7 100644 --- a/packages/react-native-nitro-modules/package.json +++ b/packages/react-native-nitro-modules/package.json @@ -147,5 +147,13 @@ } ] ] + }, + "codegenConfig": { + "name": "NitroModules", + "type": "modules", + "jsSrcsDir": "src", + "android": { + "javaPackageName": "com.margelo.nitro" + } } } diff --git a/packages/react-native-nitro-modules/src/NitroModulesTurboModule.ts b/packages/react-native-nitro-modules/src/NativeNitroModules.ts similarity index 65% rename from packages/react-native-nitro-modules/src/NitroModulesTurboModule.ts rename to packages/react-native-nitro-modules/src/NativeNitroModules.ts index 20f318548..95ab481fe 100644 --- a/packages/react-native-nitro-modules/src/NitroModulesTurboModule.ts +++ b/packages/react-native-nitro-modules/src/NativeNitroModules.ts @@ -3,30 +3,26 @@ import { TurboModuleRegistry } from 'react-native' import type { UnsafeObject } from 'react-native/Libraries/Types/CodegenTypes' import { ModuleNotFoundError } from './ModuleNotFoundError' -// This TurboModule is *not* codegen'd. -// It's handwritten, because otherwise the app's CMakeLists wants to build it. -// Instead, we want to build it ourselves and have full control over the CMakeLists. -export interface NativeNitroSpec extends TurboModule { +export interface Spec extends TurboModule { // Set up install(): void // Hybrid Objects stuff - createHybridObject(name: string, args?: UnsafeObject): UnsafeObject + createHybridObject(name: string): UnsafeObject hasHybridObject(name: string): boolean getAllHybridObjectNames(): string[] // JSI Helpers hasNativeState(obj: UnsafeObject): boolean removeNativeState(obj: UnsafeObject): void - buildType: 'debug' | 'release' + getBuildType(): 'debug' | 'release' box(obj: UnsafeObject): UnsafeObject } -let turboModule: NativeNitroSpec | undefined -export function getNativeNitroModules(): NativeNitroSpec { +let turboModule: Spec | undefined +export function getNativeNitroModules(): Spec { if (turboModule == null) { try { // 1. Get (and initialize) the C++ TurboModule - turboModule = - TurboModuleRegistry.getEnforcing('NitroModulesCxx') + turboModule = TurboModuleRegistry.getEnforcing('NitroModulesCxx') // 2. Install Dispatcher and required bindings into the Runtime turboModule.install() diff --git a/packages/react-native-nitro-modules/src/NitroModulesTurboModule.web.ts b/packages/react-native-nitro-modules/src/NativeNitroModules.web.ts similarity index 100% rename from packages/react-native-nitro-modules/src/NitroModulesTurboModule.web.ts rename to packages/react-native-nitro-modules/src/NativeNitroModules.web.ts diff --git a/packages/react-native-nitro-modules/src/NitroModules.ts b/packages/react-native-nitro-modules/src/NitroModules.ts index 08574188b..f100cc466 100644 --- a/packages/react-native-nitro-modules/src/NitroModules.ts +++ b/packages/react-native-nitro-modules/src/NitroModules.ts @@ -1,4 +1,4 @@ -import { getNativeNitroModules } from './NitroModulesTurboModule' +import { getNativeNitroModules } from './NativeNitroModules' import type { HybridObject } from './HybridObject' /** @@ -76,7 +76,7 @@ export const NitroModules = { */ get buildType(): 'debug' | 'release' { const nitro = getNativeNitroModules() - return nitro.buildType + return nitro.getBuildType() }, /** * Boxes the given {@linkcode hybridObject} into a {@linkcode BoxedHybridObject}, which can