diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.cpp b/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.cpp index 75b7c5d4696..ebc01af6ec7 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.cpp @@ -15,21 +15,6 @@ TurboModule::TurboModule( std::shared_ptr jsInvoker) : name_(std::move(name)), jsInvoker_(std::move(jsInvoker)) {} -jsi::Value TurboModule::createHostFunction( - jsi::Runtime &runtime, - const jsi::PropNameID &propName, - const MethodMetadata &meta) { - return jsi::Function::createFromHostFunction( - runtime, - propName, - static_cast(meta.argCount), - [this, meta]( - jsi::Runtime &rt, - const jsi::Value &thisVal, - const jsi::Value *args, - size_t count) { return meta.invoker(rt, *this, args, count); }); -} - void TurboModule::emitDeviceEvent( jsi::Runtime &runtime, const std::string &eventName, diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.h b/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.h index 8ca27a22b2d..b76f8436df9 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.h +++ b/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.h @@ -48,22 +48,15 @@ class JSI_EXPORT TurboModule : public facebook::jsi::HostObject { facebook::jsi::Runtime &runtime, const facebook::jsi::PropNameID &propName) override { { - std::string propNameUtf8 = propName.utf8(runtime); - auto p = methodMap_.find(propNameUtf8); - if (p == methodMap_.end()) { - // Method was not found, let JS decide what to do. - return facebook::jsi::Value::undefined(); - } else { - auto moduleMethod = createHostFunction(runtime, propName, p->second); - // If we have a JS wrapper, cache the result of this lookup - // We don't cache misses, to allow for methodMap_ to dynamically be - // extended - if (jsRepresentation_) { - jsRepresentation_->lock(runtime).asObject(runtime).setProperty( - runtime, propName, moduleMethod); - } - return moduleMethod; + auto prop = create(runtime, propName); + // If we have a JS wrapper, cache the result of this lookup + // We don't cache misses, to allow for methodMap_ to dynamically be + // extended + if (jsRepresentation_ && !prop.isUndefined()) { + jsRepresentation_->lock(runtime).asObject(runtime).setProperty( + runtime, propName, prop); } + return prop; } } @@ -111,15 +104,32 @@ class JSI_EXPORT TurboModule : public facebook::jsi::HostObject { const std::string &eventName, ArgFactory argFactory = nullptr); + virtual jsi::Value create( + jsi::Runtime &runtime, + const jsi::PropNameID &propName) { + std::string propNameUtf8 = propName.utf8(runtime); + auto p = methodMap_.find(propNameUtf8); + if (p == methodMap_.end()) { + // Method was not found, let JS decide what to do. + return facebook::jsi::Value::undefined(); + } else { + const MethodMetadata &meta = p->second; + return jsi::Function::createFromHostFunction( + runtime, + propName, + static_cast(meta.argCount), + [this, meta]( + jsi::Runtime &rt, + const jsi::Value &thisVal, + const jsi::Value *args, + size_t count) { return meta.invoker(rt, *this, args, count); }); + } + } + private: friend class TurboCxxModule; friend class TurboModuleBinding; std::unique_ptr jsRepresentation_; - - facebook::jsi::Value createHostFunction( - facebook::jsi::Runtime &runtime, - const facebook::jsi::PropNameID &propName, - const MethodMetadata &meta); }; /** diff --git a/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp b/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp index b5afe3b2ca0..b6e9d728cc5 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleBinding.cpp @@ -138,6 +138,13 @@ jsi::Value TurboModuleBinding::getModule( return jsi::Object::createFromHostObject(runtime, std::move(module)); } + // What is jsRepresentation? A cache for the TurboModule's properties + // Henceforth, always return the cache (i.e: jsRepresentation) to JavaScript + // + // If a jsRepresentation is found on the TurboModule, return it. + // + // Note: TurboModules are cached by name in TurboModuleManagers. Hence, + // jsRepresentation is also cached by by name by the TurboModuleManager auto &weakJsRepresentation = module->jsRepresentation_; if (weakJsRepresentation) { auto jsRepresentation = weakJsRepresentation->lock(runtime); @@ -146,20 +153,28 @@ jsi::Value TurboModuleBinding::getModule( } } - // No JS representation found, or object has been collected + // Status: No jsRepresentation found on TurboModule + // Create a brand new jsRepresentation, and attach it to TurboModule jsi::Object jsRepresentation(runtime); weakJsRepresentation = std::make_unique(runtime, jsRepresentation); if (bindingMode_ == TurboModuleBindingMode::Prototype) { - // Option 1: create plain object, with it's prototype mapped back to the - // hostobject. Any properties accessed are stored on the plain object + // Option 1: Lazily populate the jsRepresentation, on property access. + // + // How does this work? + // 1. Initially jsRepresentation is empty: {} + // 2. If property lookup on jsRepresentation fails, the JS runtime will + // search jsRepresentation's prototype: jsi::Object(TurboModule). + // 3. TurboModule::get(runtime, propKey) executes. This creates the + // property, caches it on jsRepresentation, then returns it to + // JavaScript. auto hostObject = jsi::Object::createFromHostObject(runtime, std::move(module)); jsRepresentation.setProperty(runtime, "__proto__", std::move(hostObject)); } else { - // Option 2: eagerly install all hostfunctions at this point, avoids - // prototype + // Option 2: Eagerly populate the jsRepresentation, on create. + // Object.assign(jsRepresentation, jsi::Object(TurboModule)) for (auto &propName : module->getPropertyNames(runtime)) { module->get(runtime, propName); }