Make JS Representation cache all TurboModule properties (#36625)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/36625

## Context
Previously, jsRepresentation would only cache the **HostFunctions** returned from TurboModule::createHostFunction().

## Changes
This diff replaces TurboModule::createHostFunction() with TurboModule::create().

Now, jsRepresentation will cache **all** the **properties** returned from TurboModule::create().

## Motivation
For interop modules, constants will be exported as properties on the TurboModule HostObject. This diff allows those constants (which are non HostFunctions) to be cached.

Changelog: [Internal]

Reviewed By: javache

Differential Revision: D44253229

fbshipit-source-id: d3dd042f4ccb6c076b83503f3712e4d1609388ce
This commit is contained in:
Ramanpreet Nara
2023-03-28 09:11:56 -07:00
committed by Facebook GitHub Bot
parent 70239629e4
commit 51893c4e9f
3 changed files with 50 additions and 40 deletions
@@ -15,21 +15,6 @@ TurboModule::TurboModule(
std::shared_ptr<CallInvoker> 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<unsigned int>(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,
@@ -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<unsigned int>(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<jsi::WeakObject> jsRepresentation_;
facebook::jsi::Value createHostFunction(
facebook::jsi::Runtime &runtime,
const facebook::jsi::PropNameID &propName,
const MethodMetadata &meta);
};
/**
@@ -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<jsi::WeakObject>(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);
}