Introduce RCTTurboModulePerformanceLogger

Summary:
In production, we'd like to be able to track the performance metrics of TurboModules. So, I'm introducing a new protocol called `RCTTurboModulePerformanceLogger`. It allows the application to be notified at points during stages of TurboModule creation, and TurboModule method invocation.

**Why can't we use `RCTPerformanceLogger`?**

`RCTPerformanceLogger` is meant to be used to profile RN initialization. It supports a fixed list of markers. Each marker has a type, and there can only be one instance of it. This isn't suitable for logging events in the TurboModule system.

Reviewed By: mdvacca

Differential Revision: D20310976

fbshipit-source-id: a49251b325c94f912cd35ab1cd8fc010f2cfc08f
This commit is contained in:
Ramanpreet Nara
2020-03-10 19:59:34 -07:00
committed by Facebook Github Bot
parent 1c1fa5b909
commit 99ceb6afba
4 changed files with 335 additions and 65 deletions
@@ -22,6 +22,101 @@
((RCTTurboModuleEnabled() && [(klass) conformsToProtocol:@protocol(RCTTurboModule)]))
#define RCT_IS_TURBO_MODULE_INSTANCE(module) RCT_IS_TURBO_MODULE_CLASS([(module) class])
typedef int MethodCallId;
/**
* This interface exists to allow the application to collect performance
* metrics of the TurboModule system. By implementing each function, you can
* hook into various stages of TurboModule creation and method dispatch (both async and sync).
*
* Note:
* - TurboModule async method invocations can interleave, so methodCallId should be used as a unique id for a method
* call.
*/
@protocol RCTTurboModulePerformanceLogger
// Create TurboModule JS Object
- (void)createTurboModuleStart:(const char *)moduleName;
- (void)createTurboModuleEnd:(const char *)moduleName;
- (void)createTurboModuleCacheHit:(const char *)moduleName;
- (void)getCppTurboModuleFromTMMDelegateStart:(const char *)moduleName;
- (void)getCppTurboModuleFromTMMDelegateEnd:(const char *)moduleName;
- (void)getTurboModuleFromRCTTurboModuleStart:(const char *)moduleName;
- (void)getTurboModuleFromRCTTurboModuleEnd:(const char *)moduleName;
- (void)getTurboModuleFromRCTCxxModuleStart:(const char *)moduleName;
- (void)getTurboModuleFromRCTCxxModuleEnd:(const char *)moduleName;
- (void)getTurboModuleFromTMMDelegateStart:(const char *)moduleName;
- (void)getTurboModuleFromTMMDelegateEnd:(const char *)moduleName;
// Create RCTTurboModule object
- (void)createRCTTurboModuleStart:(const char *)moduleName;
- (void)createRCTTurboModuleEnd:(const char *)moduleName;
- (void)createRCTTurboModuleCacheHit:(const char *)moduleName;
- (void)getRCTTurboModuleClassStart:(const char *)moduleName;
- (void)getRCTTurboModuleClassEnd:(const char *)moduleName;
- (void)getRCTTurboModuleInstanceStart:(const char *)moduleName;
- (void)getRCTTurboModuleInstanceEnd:(const char *)moduleName;
- (void)setupRCTTurboModuleDispatch:(const char *)moduleName;
- (void)setupRCTTurboModuleStart:(const char *)moduleName;
- (void)setupRCTTurboModuleEnd:(const char *)moduleName;
- (void)attachRCTBridgeToRCTTurboModuleStart:(const char *)moduleName;
- (void)attachRCTBridgeToRCTTurboModuleEnd:(const char *)moduleName;
- (void)attachMethodQueueToRCTTurboModuleStart:(const char *)moduleName;
- (void)attachMethodQueueToRCTTurboModuleEnd:(const char *)moduleName;
- (void)registerRCTTurboModuleForFrameUpdatesStart:(const char *)moduleName;
- (void)registerRCTTurboModuleForFrameUpdatesEnd:(const char *)moduleName;
- (void)dispatchDidInitializeModuleNotificationForRCTTurboModuleStart:(const char *)moduleName;
- (void)dispatchDidInitializeModuleNotificationForRCTTurboModuleEnd:(const char *)moduleName;
// Sync method invocation
- (void)syncMethodCallStart:(const char *)moduleName
methodName:(const char *)methodName
methodCallId:(MethodCallId)methodCallId;
- (void)syncMethodCallEnd:(const char *)moduleName
methodName:(const char *)methodName
methodCallId:(MethodCallId)methodCallId;
- (void)syncMethodCallArgumentConversionStart:(const char *)moduleName
methodName:(const char *)methodName
methodCallId:(MethodCallId)methodCallId;
- (void)syncMethodCallArgumentConversionEnd:(const char *)moduleName
methodName:(const char *)methodName
methodCallId:(MethodCallId)methodCallId;
- (void)syncRCTTurboModuleMethodCallStart:(const char *)moduleName
methodName:(const char *)methodName
methodCallId:(MethodCallId)methodCallId;
- (void)syncRCTTurboModuleMethodCallEnd:(const char *)moduleName
methodName:(const char *)methodName
methodCallId:(MethodCallId)methodCallId;
- (void)syncMethodCallReturnConversionStart:(const char *)moduleName
methodName:(const char *)methodName
methodCallId:(MethodCallId)methodCallId;
- (void)syncMethodCallReturnConversionEnd:(const char *)moduleName
methodName:(const char *)methodName
methodCallId:(MethodCallId)methodCallId;
// Async method invocation
- (void)asyncMethodCallStart:(const char *)moduleName
methodName:(const char *)methodName
methodCallId:(MethodCallId)methodCallId;
- (void)asyncMethodCallEnd:(const char *)moduleName
methodName:(const char *)methodName
methodCallId:(MethodCallId)methodCallId;
- (void)asyncMethodCallArgumentConversionStart:(const char *)moduleName
methodName:(const char *)methodName
methodCallId:(MethodCallId)methodCallId;
- (void)asyncMethodCallArgumentConversionEnd:(const char *)moduleName
methodName:(const char *)methodName
methodCallId:(MethodCallId)methodCallId;
- (void)asyncRCTTurboModuleMethodCallDispatch:(const char *)moduleName
methodName:(const char *)methodName
methodCallId:(MethodCallId)methodCallId;
- (void)asyncRCTTurboModuleMethodCallStart:(const char *)moduleName
methodName:(const char *)methodName
methodCallId:(MethodCallId)methodCallId;
- (void)asyncRCTTurboModuleMethodCallEnd:(const char *)moduleName
methodName:(const char *)methodName
methodCallId:(MethodCallId)methodCallId;
@end
namespace facebook {
namespace react {
@@ -44,6 +139,11 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule {
id<RCTTurboModule> instance_;
/**
* TODO(T63718299): Move this into the ObjCTurboModule constructor
*/
void setRCTTurboModulePerformanceLogger(id<RCTTurboModulePerformanceLogger> performanceLogger);
protected:
void setMethodArgConversionSelector(NSString *methodName, int argIndex, NSString *fnName);
@@ -55,23 +155,38 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule {
NSMutableDictionary<NSString *, NSMutableArray *> *methodArgConversionSelectors_;
NSDictionary<NSString *, NSArray<NSString *> *> *methodArgumentTypeNames_;
NSString *getArgumentTypeName(NSString *methodName, int argIndex);
id<RCTTurboModulePerformanceLogger> performanceLogger_;
/**
* Required for performance logging async method invocations.
* This field is static because two nth async method calls from different
* TurboModules can interleave, and should therefore be treated as two distinct calls.
*/
static MethodCallId methodCallId_;
static MethodCallId getNewMethodCallId();
NSInvocation *getMethodInvocation(
jsi::Runtime &runtime,
TurboModuleMethodValueKind valueKind,
const id<RCTTurboModule> module,
std::shared_ptr<CallInvoker> jsInvoker,
const std::string &methodName,
TurboModuleMethodValueKind returnType,
const char *methodName,
SEL selector,
const jsi::Value *args,
size_t count,
NSMutableArray *retainedObjectsForInvocation);
NSMutableArray *retainedObjectsForInvocation,
MethodCallId methodCallId);
jsi::Value performMethodInvocation(
jsi::Runtime &runtime,
TurboModuleMethodValueKind returnType,
const char *methodName,
NSInvocation *inv,
NSMutableArray *retainedObjectsForInvocation,
MethodCallId methodCallId);
BOOL hasMethodArgConversionSelector(NSString *methodName, int argIndex);
SEL getMethodArgConversionSelector(NSString *methodName, int argIndex);
using PromiseInvocationBlock =
void (^)(jsi::Runtime &rt, RCTPromiseResolveBlock resolveWrapper, RCTPromiseRejectBlock rejectWrapper);
using PromiseInvocationBlock = void (^)(RCTPromiseResolveBlock resolveWrapper, RCTPromiseRejectBlock rejectWrapper);
jsi::Value
createPromise(jsi::Runtime &runtime, std::shared_ptr<react::CallInvoker> jsInvoker, PromiseInvocationBlock invoke);
};
@@ -303,15 +303,13 @@ jsi::Value ObjCTurboModule::createPromise(
rejectWasCalled = YES;
};
invokeCopy(rt, resolveBlock, rejectBlock);
invokeCopy(resolveBlock, rejectBlock);
return jsi::Value::undefined();
});
return Promise.callAsConstructor(runtime, fn);
}
namespace {
/**
* Perform method invocation on a specific queue as configured by the module class.
* This serves as a backward-compatible support for RCTBridgeModule's methodQueue API.
@@ -321,40 +319,64 @@ namespace {
* - ObjC module methods will be always be called from JS thread.
* They may decide to dispatch to a different queue as needed.
*/
jsi::Value performMethodInvocation(
jsi::Value ObjCTurboModule::performMethodInvocation(
jsi::Runtime &runtime,
TurboModuleMethodValueKind returnType,
const char *methodName,
NSInvocation *inv,
TurboModuleMethodValueKind valueKind,
const id<RCTTurboModule> module,
std::shared_ptr<CallInvoker> jsInvoker,
NSMutableArray *retainedObjectsForInvocation)
NSMutableArray *retainedObjectsForInvocation,
MethodCallId methodCallId)
{
__block id result;
jsi::Runtime *rt = &runtime;
__weak id<RCTTurboModule> weakModule = instance_;
id<RCTTurboModulePerformanceLogger> performanceLogger = performanceLogger_;
const char *moduleName = name_.c_str();
const bool isSync = returnType != VoidKind && returnType != PromiseKind;
void (^block)() = ^{
[inv invokeWithTarget:module];
if (!weakModule) {
return;
}
id<RCTTurboModule> strongModule = weakModule;
if (isSync) {
[performanceLogger syncRCTTurboModuleMethodCallStart:moduleName methodName:methodName methodCallId:methodCallId];
} else {
[performanceLogger asyncRCTTurboModuleMethodCallStart:moduleName methodName:methodName methodCallId:methodCallId];
}
[inv invokeWithTarget:strongModule];
[retainedObjectsForInvocation removeAllObjects];
if (valueKind == VoidKind) {
if (returnType == VoidKind) {
[performanceLogger asyncRCTTurboModuleMethodCallEnd:moduleName methodName:methodName methodCallId:methodCallId];
return;
}
void *rawResult;
[inv getReturnValue:&rawResult];
result = (__bridge id)rawResult;
[performanceLogger syncRCTTurboModuleMethodCallEnd:moduleName methodName:methodName methodCallId:methodCallId];
};
// Backward-compatibility layer for calling module methods on specific queue.
dispatch_queue_t methodQueue = NULL;
if ([module conformsToProtocol:@protocol(RCTBridgeModule)] && [module respondsToSelector:@selector(methodQueue)]) {
methodQueue = [module performSelector:@selector(methodQueue)];
if ([instance_ conformsToProtocol:@protocol(RCTBridgeModule)] &&
[instance_ respondsToSelector:@selector(methodQueue)]) {
methodQueue = [instance_ performSelector:@selector(methodQueue)];
}
if (methodQueue == NULL || methodQueue == RCTJSThread) {
// This is the default mode of execution: on JS thread.
block();
} else if (methodQueue == dispatch_get_main_queue()) {
if (valueKind == VoidKind) {
if (returnType == VoidKind) {
// Void methods are treated as async for now, so there's no need to block here.
[performanceLogger_ asyncRCTTurboModuleMethodCallDispatch:moduleName
methodName:methodName
methodCallId:methodCallId];
RCTExecuteOnMainQueue(block);
} else {
// This is not ideal, but provides the simplest mechanism for now.
@@ -363,7 +385,10 @@ jsi::Value performMethodInvocation(
RCTUnsafeExecuteOnMainQueueSync(block);
}
} else {
if (valueKind == VoidKind) {
if (returnType == VoidKind) {
[performanceLogger_ asyncRCTTurboModuleMethodCallDispatch:moduleName
methodName:methodName
methodCallId:methodCallId];
dispatch_async(methodQueue, block);
} else {
dispatch_sync(methodQueue, block);
@@ -372,33 +397,48 @@ jsi::Value performMethodInvocation(
// VoidKind can't be null
// PromiseKind, and FunctionKind must throw errors always
if (valueKind != VoidKind && valueKind != PromiseKind && valueKind != FunctionKind &&
if (returnType != VoidKind && returnType != PromiseKind && returnType != FunctionKind &&
(result == (id)kCFNull || result == nil)) {
return jsi::Value::null();
}
jsi::Value returnValue = jsi::Value::undefined();
[performanceLogger_ syncMethodCallReturnConversionStart:moduleName methodName:methodName methodCallId:methodCallId];
// TODO: Re-use value conversion logic from existing impl, if possible.
switch (valueKind) {
case VoidKind:
return jsi::Value::undefined();
case BooleanKind:
return convertNSNumberToJSIBoolean(*rt, (NSNumber *)result);
case NumberKind:
return convertNSNumberToJSINumber(*rt, (NSNumber *)result);
case StringKind:
return convertNSStringToJSIString(*rt, (NSString *)result);
case ObjectKind:
return convertNSDictionaryToJSIObject(*rt, (NSDictionary *)result);
case ArrayKind:
return convertNSArrayToJSIArray(*rt, (NSArray *)result);
switch (returnType) {
case VoidKind: {
break;
}
case BooleanKind: {
returnValue = convertNSNumberToJSIBoolean(*rt, (NSNumber *)result);
break;
}
case NumberKind: {
returnValue = convertNSNumberToJSINumber(*rt, (NSNumber *)result);
break;
}
case StringKind: {
returnValue = convertNSStringToJSIString(*rt, (NSString *)result);
break;
}
case ObjectKind: {
returnValue = convertNSDictionaryToJSIObject(*rt, (NSDictionary *)result);
break;
}
case ArrayKind: {
returnValue = convertNSArrayToJSIArray(*rt, (NSArray *)result);
break;
}
case FunctionKind:
throw std::runtime_error("convertInvocationResultToJSIValue: FunctionKind is not supported yet.");
case PromiseKind:
throw std::runtime_error("convertInvocationResultToJSIValue: PromiseKind wasn't handled properly.");
}
}
} // namespace
[performanceLogger_ syncMethodCallReturnConversionEnd:moduleName methodName:methodName methodCallId:methodCallId];
return returnValue;
}
/**
* Given a method name, and an argument index, return type of that argument.
@@ -460,15 +500,28 @@ NSString *ObjCTurboModule::getArgumentTypeName(NSString *methodName, int argInde
NSInvocation *ObjCTurboModule::getMethodInvocation(
jsi::Runtime &runtime,
TurboModuleMethodValueKind valueKind,
const id<RCTTurboModule> module,
std::shared_ptr<CallInvoker> jsInvoker,
const std::string &methodName,
TurboModuleMethodValueKind returnType,
const char *methodName,
SEL selector,
const jsi::Value *args,
size_t count,
NSMutableArray *retainedObjectsForInvocation)
NSMutableArray *retainedObjectsForInvocation,
MethodCallId methodCallId)
{
const bool isSync = returnType != VoidKind && returnType != PromiseKind;
const char *moduleName = name_.c_str();
const id<RCTTurboModule> module = instance_;
if (isSync) {
[performanceLogger_ syncMethodCallArgumentConversionStart:moduleName
methodName:methodName
methodCallId:methodCallId];
} else {
[performanceLogger_ asyncMethodCallArgumentConversionStart:moduleName
methodName:methodName
methodCallId:methodCallId];
}
NSInvocation *inv =
[NSInvocation invocationWithMethodSignature:[[module class] instanceMethodSignatureForSelector:selector]];
[inv setSelector:selector];
@@ -516,10 +569,10 @@ NSInvocation *ObjCTurboModule::getMethodInvocation(
/**
* Convert arg to ObjC objects.
*/
id objCArg = convertJSIValueToObjCObject(runtime, *arg, jsInvoker);
id objCArg = convertJSIValueToObjCObject(runtime, *arg, jsInvoker_);
if (objCArg) {
NSString *methodNameNSString = @(methodName.c_str());
NSString *methodNameNSString = @(methodName);
/**
* Convert objects using RCTConvert.
@@ -570,6 +623,14 @@ NSInvocation *ObjCTurboModule::getMethodInvocation(
}
}
if (isSync) {
[performanceLogger_ syncMethodCallArgumentConversionEnd:moduleName methodName:methodName methodCallId:methodCallId];
} else {
[performanceLogger_ asyncMethodCallArgumentConversionEnd:moduleName
methodName:methodName
methodCallId:methodCallId];
}
return inv;
}
@@ -581,35 +642,62 @@ ObjCTurboModule::ObjCTurboModule(
{
}
void ObjCTurboModule::setRCTTurboModulePerformanceLogger(id<RCTTurboModulePerformanceLogger> performanceLogger)
{
performanceLogger_ = performanceLogger;
}
MethodCallId ObjCTurboModule::methodCallId_{0};
MethodCallId ObjCTurboModule::getNewMethodCallId()
{
return methodCallId_++;
}
jsi::Value ObjCTurboModule::invokeObjCMethod(
jsi::Runtime &runtime,
TurboModuleMethodValueKind valueKind,
const std::string &methodName,
TurboModuleMethodValueKind returnType,
const std::string &methodNameStr,
SEL selector,
const jsi::Value *args,
size_t count)
{
NSMutableArray *retainedObjectsForInvocation = [NSMutableArray arrayWithCapacity:count + 2];
NSInvocation *inv = getMethodInvocation(
runtime, valueKind, instance_, jsInvoker_, methodName, selector, args, count, retainedObjectsForInvocation);
MethodCallId methodCallId = getNewMethodCallId();
const bool isSync = returnType != VoidKind && returnType != PromiseKind;
const char *moduleName = name_.c_str();
const char *methodName = methodNameStr.c_str();
if (valueKind == PromiseKind) {
// Promise return type is special cased today, i.e. it needs extra 2 function args for resolve() and reject(), to
// be passed to the actual ObjC++ class method.
return createPromise(
runtime,
jsInvoker_,
^(jsi::Runtime &rt, RCTPromiseResolveBlock resolveBlock, RCTPromiseRejectBlock rejectBlock) {
[inv setArgument:(void *)&resolveBlock atIndex:count + 2];
[inv setArgument:(void *)&rejectBlock atIndex:count + 3];
[retainedObjectsForInvocation addObject:resolveBlock];
[retainedObjectsForInvocation addObject:rejectBlock];
// The return type becomes void in the ObjC side.
performMethodInvocation(rt, inv, VoidKind, instance_, jsInvoker_, retainedObjectsForInvocation);
});
if (isSync) {
[performanceLogger_ syncMethodCallStart:moduleName methodName:methodName methodCallId:methodCallId];
} else {
[performanceLogger_ asyncMethodCallStart:moduleName methodName:methodName methodCallId:methodCallId];
}
return performMethodInvocation(runtime, inv, valueKind, instance_, jsInvoker_, retainedObjectsForInvocation);
NSMutableArray *retainedObjectsForInvocation = [NSMutableArray arrayWithCapacity:count + 2];
NSInvocation *inv = getMethodInvocation(
runtime, returnType, methodName, selector, args, count, retainedObjectsForInvocation, methodCallId);
jsi::Value returnValue = returnType == PromiseKind
? createPromise(
runtime,
jsInvoker_,
^(RCTPromiseResolveBlock resolveBlock, RCTPromiseRejectBlock rejectBlock) {
[inv setArgument:(void *)&resolveBlock atIndex:count + 2];
[inv setArgument:(void *)&rejectBlock atIndex:count + 3];
[retainedObjectsForInvocation addObject:resolveBlock];
[retainedObjectsForInvocation addObject:rejectBlock];
// The return type becomes void in the ObjC side.
performMethodInvocation(runtime, VoidKind, methodName, inv, retainedObjectsForInvocation, methodCallId);
})
: performMethodInvocation(runtime, returnType, methodName, inv, retainedObjectsForInvocation, methodCallId);
if (isSync) {
[performanceLogger_ syncMethodCallEnd:moduleName methodName:methodName methodCallId:methodCallId];
} else {
[performanceLogger_ asyncMethodCallEnd:moduleName methodName:methodName methodCallId:methodCallId];
}
return returnValue;
}
BOOL ObjCTurboModule::hasMethodArgConversionSelector(NSString *methodName, int argIndex)
@@ -42,6 +42,11 @@
delegate:(id<RCTTurboModuleManagerDelegate>)delegate
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker;
- (instancetype)initWithBridge:(RCTBridge *)bridge
delegate:(id<RCTTurboModuleManagerDelegate>)delegate
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
performanceLogger:(id<RCTTurboModulePerformanceLogger>)performanceLogger;
- (void)installJSBindingWithRuntime:(facebook::jsi::Runtime *)runtime;
- (void)invalidate;
@@ -37,6 +37,7 @@ static Class getFallbackClassFromName(const char *name)
@implementation RCTTurboModuleManager {
jsi::Runtime *_runtime;
std::shared_ptr<facebook::react::CallInvoker> _jsInvoker;
id<RCTTurboModulePerformanceLogger> _performanceLogger;
__weak id<RCTTurboModuleManagerDelegate> _delegate;
__weak RCTBridge *_bridge;
/**
@@ -65,12 +66,21 @@ static Class getFallbackClassFromName(const char *name)
- (instancetype)initWithBridge:(RCTBridge *)bridge
delegate:(id<RCTTurboModuleManagerDelegate>)delegate
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
{
return [self initWithBridge:bridge delegate:delegate jsInvoker:jsInvoker performanceLogger:nil];
}
- (instancetype)initWithBridge:(RCTBridge *)bridge
delegate:(id<RCTTurboModuleManagerDelegate>)delegate
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
performanceLogger:(id<RCTTurboModulePerformanceLogger>)performanceLogger
{
if (self = [super init]) {
_jsInvoker = jsInvoker;
_delegate = delegate;
_bridge = bridge;
_invalidating = false;
_performanceLogger = performanceLogger;
// Necessary to allow NativeModules to lookup TurboModules
[bridge setRCTTurboModuleLookupDelegate:self];
@@ -114,6 +124,7 @@ static Class getFallbackClassFromName(const char *name)
{
auto turboModuleLookup = _turboModuleCache.find(moduleName);
if (turboModuleLookup != _turboModuleCache.end()) {
[_performanceLogger createTurboModuleCacheHit:moduleName];
return turboModuleLookup->second;
}
@@ -122,7 +133,9 @@ static Class getFallbackClassFromName(const char *name)
* Pure C++ modules get priority.
*/
if ([_delegate respondsToSelector:@selector(getTurboModule:jsInvoker:)]) {
[_performanceLogger getCppTurboModuleFromTMMDelegateStart:moduleName];
auto turboModule = [_delegate getTurboModule:moduleName jsInvoker:_jsInvoker];
[_performanceLogger getCppTurboModuleFromTMMDelegateEnd:moduleName];
if (turboModule != nullptr) {
_turboModuleCache.insert({moduleName, turboModule});
return turboModule;
@@ -132,7 +145,9 @@ static Class getFallbackClassFromName(const char *name)
/**
* Step 2: Look for platform-specific modules.
*/
[_performanceLogger createRCTTurboModuleStart:moduleName];
id<RCTTurboModule> module = [self provideRCTTurboModule:moduleName];
[_performanceLogger createRCTTurboModuleEnd:moduleName];
// If we request that a TurboModule be created, its respective ObjC class must exist
// If the class doesn't exist, then provideRCTTurboModule returns nil
@@ -145,7 +160,9 @@ static Class getFallbackClassFromName(const char *name)
// If RCTTurboModule supports creating its own C++ TurboModule object,
// allow it to do so.
if ([module respondsToSelector:@selector(getTurboModuleWithJsInvoker:)]) {
[_performanceLogger getTurboModuleFromRCTTurboModuleStart:moduleName];
auto turboModule = [module getTurboModuleWithJsInvoker:_jsInvoker];
[_performanceLogger getTurboModuleFromRCTTurboModuleEnd:moduleName];
assert(turboModule != nullptr);
_turboModuleCache.insert({moduleName, turboModule});
return turboModule;
@@ -158,7 +175,9 @@ static Class getFallbackClassFromName(const char *name)
if ([moduleClass isSubclassOfClass:RCTCxxModule.class]) {
// Use TurboCxxModule compat class to wrap the CxxModule instance.
// This is only for migration convenience, despite less performant.
[_performanceLogger getTurboModuleFromRCTCxxModuleStart:moduleName];
auto turboModule = std::make_shared<react::TurboCxxModule>([((RCTCxxModule *)module) createModule], _jsInvoker);
[_performanceLogger getTurboModuleFromRCTCxxModuleEnd:moduleName];
_turboModuleCache.insert({moduleName, turboModule});
return turboModule;
}
@@ -166,7 +185,9 @@ static Class getFallbackClassFromName(const char *name)
/**
* Step 2d: Return an exact sub-class of ObjC TurboModule
*/
[_performanceLogger getTurboModuleFromTMMDelegateStart:moduleName];
auto turboModule = [_delegate getTurboModule:moduleName instance:module jsInvoker:_jsInvoker];
[_performanceLogger getTurboModuleFromTMMDelegateEnd:moduleName];
if (turboModule != nullptr) {
_turboModuleCache.insert({moduleName, turboModule});
}
@@ -191,6 +212,7 @@ static Class getFallbackClassFromName(const char *name)
auto rctTurboModuleCacheLookup = _rctTurboModuleCache.find(moduleName);
if (rctTurboModuleCacheLookup != _rctTurboModuleCache.end()) {
[_performanceLogger createRCTTurboModuleCacheHit:moduleName];
return rctTurboModuleCacheLookup->second;
}
@@ -202,6 +224,8 @@ static Class getFallbackClassFromName(const char *name)
/**
* Step 2a: Resolve platform-specific class.
*/
[_performanceLogger getRCTTurboModuleClassStart:moduleName];
if ([_delegate respondsToSelector:@selector(getModuleClassFromName:)]) {
moduleClass = [_delegate getModuleClassFromName:moduleName];
}
@@ -210,6 +234,8 @@ static Class getFallbackClassFromName(const char *name)
moduleClass = getFallbackClassFromName(moduleName);
}
[_performanceLogger getRCTTurboModuleClassEnd:moduleName];
if (![moduleClass conformsToProtocol:@protocol(RCTTurboModule)]) {
return nil;
}
@@ -217,12 +243,16 @@ static Class getFallbackClassFromName(const char *name)
/**
* Step 2b: Ask hosting application/delegate to instantiate this class
*/
[_performanceLogger getRCTTurboModuleInstanceStart:moduleName];
if ([_delegate respondsToSelector:@selector(getModuleInstanceFromClass:)]) {
module = [_delegate getModuleInstanceFromClass:moduleClass];
} else {
module = [moduleClass new];
}
[_performanceLogger getRCTTurboModuleInstanceEnd:moduleName];
if ([module respondsToSelector:@selector(setTurboModuleLookupDelegate:)]) {
[module setTurboModuleLookupDelegate:self];
}
@@ -232,12 +262,15 @@ static Class getFallbackClassFromName(const char *name)
__weak id<RCTBridgeModule> weakModule = (id<RCTBridgeModule>)module;
__weak RCTBridge *weakBridge = _bridge;
id<RCTTurboModulePerformanceLogger> performanceLogger = _performanceLogger;
auto setupTurboModule = ^{
if (!weakModule) {
return;
}
[performanceLogger setupRCTTurboModuleStart:moduleName];
id<RCTBridgeModule> strongModule = weakModule;
RCTBridge *strongBridge = weakBridge;
@@ -251,6 +284,8 @@ static Class getFallbackClassFromName(const char *name)
* NativeModule.
*/
if ([strongModule respondsToSelector:@selector(bridge)] && strongBridge) {
[performanceLogger attachRCTBridgeToRCTTurboModuleStart:moduleName];
/**
* Just because a NativeModule has the `bridge` method, it doesn't mean
* that it has synthesized the bridge in its implementation. Therefore,
@@ -275,6 +310,8 @@ static Class getFallbackClassFromName(const char *name)
"or provide your own setter method.",
RCTBridgeModuleNameForClass(strongModule));
}
[performanceLogger attachRCTBridgeToRCTTurboModuleEnd:moduleName];
}
/**
@@ -283,6 +320,8 @@ static Class getFallbackClassFromName(const char *name)
* `@synthesize methodQueue = _methodQueue`
*/
if ([strongModule respondsToSelector:@selector(methodQueue)]) {
[performanceLogger attachMethodQueueToRCTTurboModuleStart:moduleName];
dispatch_queue_t methodQueue = [strongModule performSelector:@selector(methodQueue)];
if (!methodQueue) {
NSString *moduleClassName = NSStringFromClass(strongModule.class);
@@ -299,6 +338,8 @@ static Class getFallbackClassFromName(const char *name)
moduleClassName);
}
}
[performanceLogger attachMethodQueueToRCTTurboModuleEnd:moduleName];
}
/**
@@ -309,8 +350,10 @@ static Class getFallbackClassFromName(const char *name)
* rollout.
*/
if (strongBridge) {
[performanceLogger registerRCTTurboModuleForFrameUpdatesStart:moduleName];
RCTModuleData *data = [[RCTModuleData alloc] initWithModuleInstance:strongModule bridge:strongBridge];
[strongBridge registerModuleForFrameUpdates:strongModule withModuleData:data];
[performanceLogger registerRCTTurboModuleForFrameUpdatesEnd:moduleName];
}
/**
@@ -319,10 +362,14 @@ static Class getFallbackClassFromName(const char *name)
* TODO(T41180176): Investigate whether we can delete this after TM
* rollout.
*/
[performanceLogger dispatchDidInitializeModuleNotificationForRCTTurboModuleStart:moduleName];
[[NSNotificationCenter defaultCenter]
postNotificationName:RCTDidInitializeModuleNotification
object:strongBridge
userInfo:@{@"module" : module, @"bridge" : RCTNullIfNil([strongBridge parentBridge])}];
[performanceLogger dispatchDidInitializeModuleNotificationForRCTTurboModuleEnd:moduleName];
[performanceLogger setupRCTTurboModuleEnd:moduleName];
};
if ([[module class] respondsToSelector:@selector(requiresMainQueueSetup)] &&
@@ -334,6 +381,7 @@ static Class getFallbackClassFromName(const char *name)
*
* TODO(T63807674): Investigate the right migration plan off of this
*/
[_performanceLogger setupRCTTurboModuleDispatch:moduleName];
RCTUnsafeExecuteOnMainQueueSync(setupTurboModule);
} else {
setupTurboModule();
@@ -354,7 +402,9 @@ static Class getFallbackClassFromName(const char *name)
__weak __typeof(self) weakSelf = self;
react::TurboModuleBinding::install(
*_runtime, [weakSelf](const std::string &name) -> std::shared_ptr<react::TurboModule> {
*_runtime,
[weakSelf,
performanceLogger = _performanceLogger](const std::string &name) -> std::shared_ptr<react::TurboModule> {
if (!weakSelf) {
return nullptr;
}
@@ -367,6 +417,8 @@ static Class getFallbackClassFromName(const char *name)
[strongSelf->_bridge.performanceLogger markStartForTag:RCTPLTurboModuleSetup];
}
[performanceLogger createTurboModuleStart:moduleName];
/**
* By default, all TurboModules are long-lived.
* Additionally, if a TurboModule with the name `name` isn't found, then we
@@ -374,6 +426,16 @@ static Class getFallbackClassFromName(const char *name)
*/
auto turboModule = [strongSelf provideTurboModule:moduleName];
/**
* TODO(T63718299): Move this setter into the ObjCTurboModule constructor
*/
if (std::shared_ptr<facebook::react::ObjCTurboModule> objCTurboModule =
std::dynamic_pointer_cast<facebook::react::ObjCTurboModule>(turboModule)) {
objCTurboModule->setRCTTurboModulePerformanceLogger(performanceLogger);
};
[performanceLogger createTurboModuleEnd:moduleName];
if (moduleWasNotInitialized && [strongSelf moduleIsInitialized:moduleName]) {
[strongSelf->_bridge.performanceLogger markStopForTag:RCTPLTurboModuleSetup];
[strongSelf notifyAboutTurboModuleSetup:moduleName];