1/* 2 * Copyright (c) Meta Platforms, Inc. and affiliates. 3 * 4 * This source code is licensed under the MIT license found in the 5 * LICENSE file in the root directory of this source tree. 6 */ 7 8#import "ABI48_0_0RCTModuleData.h" 9 10#import <objc/runtime.h> 11#import <atomic> 12#import <mutex> 13 14#import <ABI48_0_0Reactperflogger/ABI48_0_0BridgeNativeModulePerfLogger.h> 15 16#import "ABI48_0_0RCTBridge+Private.h" 17#import "ABI48_0_0RCTBridge.h" 18#import "ABI48_0_0RCTBridgeModuleDecorator.h" 19#import "ABI48_0_0RCTConstants.h" 20#import "ABI48_0_0RCTInitializing.h" 21#import "ABI48_0_0RCTLog.h" 22#import "ABI48_0_0RCTModuleMethod.h" 23#import "ABI48_0_0RCTProfile.h" 24#import "ABI48_0_0RCTUtils.h" 25 26using namespace ABI48_0_0facebook::ABI48_0_0React; 27 28namespace { 29int32_t getUniqueId() 30{ 31 static std::atomic<int32_t> counter{0}; 32 return counter++; 33} 34} 35static BOOL isMainQueueExecutionOfConstantToExportDisabled = NO; 36 37void ABI48_0_0RCTSetIsMainQueueExecutionOfConstantsToExportDisabled(BOOL val) 38{ 39 isMainQueueExecutionOfConstantToExportDisabled = val; 40} 41 42BOOL ABI48_0_0RCTIsMainQueueExecutionOfConstantsToExportDisabled() 43{ 44 return isMainQueueExecutionOfConstantToExportDisabled; 45} 46 47@implementation ABI48_0_0RCTModuleData { 48 NSDictionary<NSString *, id> *_constantsToExport; 49 NSString *_queueName; 50 __weak ABI48_0_0RCTBridge *_bridge; 51 ABI48_0_0RCTBridgeModuleProvider _moduleProvider; 52 std::mutex _instanceLock; 53 BOOL _setupComplete; 54 ABI48_0_0RCTModuleRegistry *_moduleRegistry; 55 ABI48_0_0RCTViewRegistry *_viewRegistry_DEPRECATED; 56 ABI48_0_0RCTBundleManager *_bundleManager; 57 ABI48_0_0RCTCallableJSModules *_callableJSModules; 58 BOOL _isInitialized; 59} 60 61@synthesize methods = _methods; 62@synthesize methodsByName = _methodsByName; 63@synthesize instance = _instance; 64@synthesize methodQueue = _methodQueue; 65 66- (void)setUp 67{ 68 _implementsBatchDidComplete = [_moduleClass instancesRespondToSelector:@selector(batchDidComplete)]; 69 _implementsPartialBatchDidFlush = [_moduleClass instancesRespondToSelector:@selector(partialBatchDidFlush)]; 70 71 // If a module overrides `constantsToExport` and doesn't implement `requiresMainQueueSetup`, then we must assume 72 // that it must be called on the main thread, because it may need to access UIKit. 73 _hasConstantsToExport = [_moduleClass instancesRespondToSelector:@selector(constantsToExport)]; 74 75 const BOOL implementsRequireMainQueueSetup = [_moduleClass respondsToSelector:@selector(requiresMainQueueSetup)]; 76 if (implementsRequireMainQueueSetup) { 77 _requiresMainQueueSetup = [_moduleClass requiresMainQueueSetup]; 78 } else { 79 static IMP objectInitMethod; 80 static dispatch_once_t onceToken; 81 dispatch_once(&onceToken, ^{ 82 objectInitMethod = [NSObject instanceMethodForSelector:@selector(init)]; 83 }); 84 85 // If a module overrides `init` then we must assume that it expects to be 86 // initialized on the main thread, because it may need to access UIKit. 87 const BOOL hasCustomInit = 88 !_instance && [_moduleClass instanceMethodForSelector:@selector(init)] != objectInitMethod; 89 90 _requiresMainQueueSetup = _hasConstantsToExport || hasCustomInit; 91 if (_requiresMainQueueSetup) { 92 const char *methodName = ""; 93 if (_hasConstantsToExport) { 94 methodName = "constantsToExport"; 95 } else if (hasCustomInit) { 96 methodName = "init"; 97 } 98 ABI48_0_0RCTLogWarn( 99 @"Module %@ requires main queue setup since it overrides `%s` but doesn't implement " 100 "`requiresMainQueueSetup`. In a future release ABI48_0_0React Native will default to initializing all native modules " 101 "on a background thread unless explicitly opted-out of.", 102 _moduleClass, 103 methodName); 104 } 105 } 106} 107 108- (instancetype)initWithModuleClass:(Class)moduleClass 109 bridge:(ABI48_0_0RCTBridge *)bridge 110 moduleRegistry:(ABI48_0_0RCTModuleRegistry *)moduleRegistry 111 viewRegistry_DEPRECATED:(ABI48_0_0RCTViewRegistry *)viewRegistry_DEPRECATED 112 bundleManager:(ABI48_0_0RCTBundleManager *)bundleManager 113 callableJSModules:(ABI48_0_0RCTCallableJSModules *)callableJSModules 114{ 115 return [self initWithModuleClass:moduleClass 116 moduleProvider:^id<ABI48_0_0RCTBridgeModule> { 117 return [moduleClass new]; 118 } 119 bridge:bridge 120 moduleRegistry:moduleRegistry 121 viewRegistry_DEPRECATED:viewRegistry_DEPRECATED 122 bundleManager:bundleManager 123 callableJSModules:callableJSModules]; 124} 125 126- (instancetype)initWithModuleClass:(Class)moduleClass 127 moduleProvider:(ABI48_0_0RCTBridgeModuleProvider)moduleProvider 128 bridge:(ABI48_0_0RCTBridge *)bridge 129 moduleRegistry:(ABI48_0_0RCTModuleRegistry *)moduleRegistry 130 viewRegistry_DEPRECATED:(ABI48_0_0RCTViewRegistry *)viewRegistry_DEPRECATED 131 bundleManager:(ABI48_0_0RCTBundleManager *)bundleManager 132 callableJSModules:(ABI48_0_0RCTCallableJSModules *)callableJSModules 133{ 134 if (self = [super init]) { 135 _bridge = bridge; 136 _moduleClass = moduleClass; 137 _moduleProvider = [moduleProvider copy]; 138 _moduleRegistry = moduleRegistry; 139 _viewRegistry_DEPRECATED = viewRegistry_DEPRECATED; 140 _bundleManager = bundleManager; 141 _callableJSModules = callableJSModules; 142 [self setUp]; 143 } 144 return self; 145} 146 147- (instancetype)initWithModuleInstance:(id<ABI48_0_0RCTBridgeModule>)instance 148 bridge:(ABI48_0_0RCTBridge *)bridge 149 moduleRegistry:(ABI48_0_0RCTModuleRegistry *)moduleRegistry 150 viewRegistry_DEPRECATED:(ABI48_0_0RCTViewRegistry *)viewRegistry_DEPRECATED 151 bundleManager:(ABI48_0_0RCTBundleManager *)bundleManager 152 callableJSModules:(ABI48_0_0RCTCallableJSModules *)callableJSModules 153{ 154 if (self = [super init]) { 155 _bridge = bridge; 156 _instance = instance; 157 _moduleClass = [instance class]; 158 _moduleRegistry = moduleRegistry; 159 _viewRegistry_DEPRECATED = viewRegistry_DEPRECATED; 160 _bundleManager = bundleManager; 161 _callableJSModules = callableJSModules; 162 [self setUp]; 163 } 164 return self; 165} 166 167ABI48_0_0RCT_NOT_IMPLEMENTED(-(instancetype)init); 168 169#pragma mark - private setup methods 170 171- (void)setUpInstanceAndBridge:(int32_t)requestId 172{ 173 NSString *moduleName = [self name]; 174 175 ABI48_0_0RCT_PROFILE_BEGIN_EVENT( 176 ABI48_0_0RCTProfileTagAlways, 177 @"[ABI48_0_0RCTModuleData setUpInstanceAndBridge]", 178 @{@"moduleClass" : NSStringFromClass(_moduleClass)}); 179 { 180 std::unique_lock<std::mutex> lock(_instanceLock); 181 BOOL shouldSetup = !_setupComplete && _bridge.valid; 182 183 if (shouldSetup) { 184 if (!_instance) { 185 if (ABI48_0_0RCT_DEBUG && _requiresMainQueueSetup) { 186 ABI48_0_0RCTAssertMainQueue(); 187 } 188 ABI48_0_0RCT_PROFILE_BEGIN_EVENT(ABI48_0_0RCTProfileTagAlways, @"[ABI48_0_0RCTModuleData setUpInstanceAndBridge] Create module", nil); 189 190 BridgeNativeModulePerfLogger::moduleCreateConstructStart([moduleName UTF8String], requestId); 191 _instance = _moduleProvider ? _moduleProvider() : nil; 192 BridgeNativeModulePerfLogger::moduleCreateConstructEnd([moduleName UTF8String], requestId); 193 194 ABI48_0_0RCT_PROFILE_END_EVENT(ABI48_0_0RCTProfileTagAlways, @""); 195 if (!_instance) { 196 // Module init returned nil, probably because automatic instantiation 197 // of the module is not supported, and it is supposed to be passed in to 198 // the bridge constructor. Mark setup complete to avoid doing more work. 199 _setupComplete = YES; 200 ABI48_0_0RCTLogWarn( 201 @"The module %@ is returning nil from its constructor. You " 202 "may need to instantiate it yourself and pass it into the " 203 "bridge.", 204 _moduleClass); 205 } 206 } 207 208 if (_instance && ABI48_0_0RCTProfileIsProfiling()) { 209 ABI48_0_0RCTProfileHookInstance(_instance); 210 } 211 } 212 213 if (_instance) { 214 BridgeNativeModulePerfLogger::moduleCreateSetUpStart([moduleName UTF8String], requestId); 215 } 216 217 if (shouldSetup) { 218 // Bridge must be set before methodQueue is set up, as methodQueue 219 // initialization requires it (View Managers get their queue by calling 220 // self.bridge.uiManager.methodQueue) 221 [self setBridgeForInstance]; 222 223 ABI48_0_0RCTBridgeModuleDecorator *moduleDecorator = 224 [[ABI48_0_0RCTBridgeModuleDecorator alloc] initWithViewRegistry:_viewRegistry_DEPRECATED 225 moduleRegistry:_moduleRegistry 226 bundleManager:_bundleManager 227 callableJSModules:_callableJSModules]; 228 [moduleDecorator attachInteropAPIsToModule:_instance]; 229 } 230 231 [self setUpMethodQueue]; 232 233 if (shouldSetup) { 234 [self _initializeModule]; 235 } 236 } 237 ABI48_0_0RCT_PROFILE_END_EVENT(ABI48_0_0RCTProfileTagAlways, @""); 238 239 // This is called outside of the lock in order to prevent deadlock issues 240 // because the logic in `finishSetupForInstance` can cause 241 // `moduleData.instance` to be accessed re-entrantly. 242 if (_bridge.moduleSetupComplete) { 243 [self finishSetupForInstance]; 244 } else { 245 // If we're here, then the module is completely initialized, 246 // except for what finishSetupForInstance does. When the instance 247 // method is called after moduleSetupComplete, 248 // finishSetupForInstance will run. If _requiresMainQueueSetup 249 // is true, getting the instance will block waiting for the main 250 // thread, which could take a while if the main thread is busy 251 // (I've seen 50ms in testing). So we clear that flag, since 252 // nothing in finishSetupForInstance needs to be run on the main 253 // thread. 254 _requiresMainQueueSetup = NO; 255 } 256 257 if (_instance) { 258 BridgeNativeModulePerfLogger::moduleCreateSetUpEnd([moduleName UTF8String], requestId); 259 } 260} 261 262- (void)setBridgeForInstance 263{ 264 if ([_instance respondsToSelector:@selector(bridge)] && _instance.bridge != _bridge) { 265 ABI48_0_0RCT_PROFILE_BEGIN_EVENT(ABI48_0_0RCTProfileTagAlways, @"[ABI48_0_0RCTModuleData setBridgeForInstance]", nil); 266 @try { 267 [(id)_instance setValue:_bridge forKey:@"bridge"]; 268 } @catch (NSException *exception) { 269 ABI48_0_0RCTLogError( 270 @"%@ has no setter or ivar for its bridge, which is not " 271 "permitted. You must either @synthesize the bridge property, " 272 "or provide your own setter method.", 273 self.name); 274 } 275 ABI48_0_0RCT_PROFILE_END_EVENT(ABI48_0_0RCTProfileTagAlways, @""); 276 } 277} 278 279- (void)_initializeModule 280{ 281 if (!_isInitialized && [_instance respondsToSelector:@selector(initialize)]) { 282 _isInitialized = YES; 283 [(id<ABI48_0_0RCTInitializing>)_instance initialize]; 284 } 285} 286 287- (void)finishSetupForInstance 288{ 289 if (!_setupComplete && _instance) { 290 ABI48_0_0RCT_PROFILE_BEGIN_EVENT(ABI48_0_0RCTProfileTagAlways, @"[ABI48_0_0RCTModuleData finishSetupForInstance]", nil); 291 _setupComplete = YES; 292 [_bridge registerModuleForFrameUpdates:_instance withModuleData:self]; 293 [[NSNotificationCenter defaultCenter] 294 postNotificationName:ABI48_0_0RCTDidInitializeModuleNotification 295 object:_bridge 296 userInfo:@{@"module" : _instance, @"bridge" : ABI48_0_0RCTNullIfNil(_bridge.parentBridge)}]; 297 ABI48_0_0RCT_PROFILE_END_EVENT(ABI48_0_0RCTProfileTagAlways, @""); 298 } 299} 300 301- (void)setUpMethodQueue 302{ 303 if (_instance && !_methodQueue && _bridge.valid) { 304 ABI48_0_0RCT_PROFILE_BEGIN_EVENT(ABI48_0_0RCTProfileTagAlways, @"[ABI48_0_0RCTModuleData setUpMethodQueue]", nil); 305 BOOL implementsMethodQueue = [_instance respondsToSelector:@selector(methodQueue)]; 306 if (implementsMethodQueue && _bridge.valid) { 307 _methodQueue = _instance.methodQueue; 308 } 309 if (!_methodQueue && _bridge.valid) { 310 // Create new queue (store queueName, as it isn't retained by dispatch_queue) 311 _queueName = [NSString stringWithFormat:@"com.facebook.ABI48_0_0React.%@Queue", self.name]; 312 _methodQueue = dispatch_queue_create(_queueName.UTF8String, DISPATCH_QUEUE_SERIAL); 313 314 // assign it to the module 315 if (implementsMethodQueue) { 316 @try { 317 [(id)_instance setValue:_methodQueue forKey:@"methodQueue"]; 318 } @catch (NSException *exception) { 319 ABI48_0_0RCTLogError( 320 @"%@ is returning nil for its methodQueue, which is not " 321 "permitted. You must either return a pre-initialized " 322 "queue, or @synthesize the methodQueue to let the bridge " 323 "create a queue for you.", 324 self.name); 325 } 326 } 327 } 328 ABI48_0_0RCT_PROFILE_END_EVENT(ABI48_0_0RCTProfileTagAlways, @""); 329 } 330} 331 332- (void)calculateMethods 333{ 334 if (_methods && _methodsByName) { 335 return; 336 } 337 338 NSMutableArray<id<ABI48_0_0RCTBridgeMethod>> *moduleMethods = [NSMutableArray new]; 339 NSMutableDictionary<NSString *, id<ABI48_0_0RCTBridgeMethod>> *moduleMethodsByName = [NSMutableDictionary new]; 340 341 if ([_moduleClass instancesRespondToSelector:@selector(methodsToExport)]) { 342 [moduleMethods addObjectsFromArray:[self.instance methodsToExport]]; 343 } 344 345 unsigned int methodCount; 346 Class cls = _moduleClass; 347 while (cls && cls != [NSObject class] && cls != [NSProxy class]) { 348 Method *methods = class_copyMethodList(object_getClass(cls), &methodCount); 349 350 for (unsigned int i = 0; i < methodCount; i++) { 351 Method method = methods[i]; 352 SEL selector = method_getName(method); 353 if ([NSStringFromSelector(selector) hasPrefix:@"__rct_export__"]) { 354 IMP imp = method_getImplementation(method); 355 auto exportedMethod = ((const ABI48_0_0RCTMethodInfo *(*)(id, SEL))imp)(_moduleClass, selector); 356 id<ABI48_0_0RCTBridgeMethod> moduleMethod = [[ABI48_0_0RCTModuleMethod alloc] initWithExportedMethod:exportedMethod 357 moduleClass:_moduleClass]; 358 359 NSString *str = [NSString stringWithUTF8String:moduleMethod.JSMethodName]; 360 [moduleMethodsByName setValue:moduleMethod forKey:str]; 361 [moduleMethods addObject:moduleMethod]; 362 } 363 } 364 365 free(methods); 366 cls = class_getSuperclass(cls); 367 } 368 369 _methods = [moduleMethods copy]; 370 _methodsByName = [moduleMethodsByName copy]; 371} 372 373#pragma mark - public getters 374 375- (BOOL)hasInstance 376{ 377 std::unique_lock<std::mutex> lock(_instanceLock); 378 return _instance != nil; 379} 380 381- (id<ABI48_0_0RCTBridgeModule>)instance 382{ 383 NSString *moduleName = [self name]; 384 int32_t requestId = getUniqueId(); 385 BridgeNativeModulePerfLogger::moduleCreateStart([moduleName UTF8String], requestId); 386 387 if (!_setupComplete) { 388 ABI48_0_0RCT_PROFILE_BEGIN_EVENT( 389 ABI48_0_0RCTProfileTagAlways, ([NSString stringWithFormat:@"[ABI48_0_0RCTModuleData instanceForClass:%@]", _moduleClass]), nil); 390 if (_requiresMainQueueSetup) { 391 // The chances of deadlock here are low, because module init very rarely 392 // calls out to other threads, however we can't control when a module might 393 // get accessed by client code during bridge setup, and a very low risk of 394 // deadlock is better than a fairly high risk of an assertion being thrown. 395 ABI48_0_0RCT_PROFILE_BEGIN_EVENT(ABI48_0_0RCTProfileTagAlways, @"[ABI48_0_0RCTModuleData instance] main thread setup", nil); 396 397 if (!ABI48_0_0RCTIsMainQueue()) { 398 ABI48_0_0RCTLogWarn(@"ABI48_0_0RCTBridge required dispatch_sync to load %@. This may lead to deadlocks", _moduleClass); 399 } 400 401 ABI48_0_0RCTUnsafeExecuteOnMainQueueSync(^{ 402 [self setUpInstanceAndBridge:requestId]; 403 }); 404 ABI48_0_0RCT_PROFILE_END_EVENT(ABI48_0_0RCTProfileTagAlways, @""); 405 } else { 406 [self setUpInstanceAndBridge:requestId]; 407 } 408 ABI48_0_0RCT_PROFILE_END_EVENT(ABI48_0_0RCTProfileTagAlways, @""); 409 } else { 410 BridgeNativeModulePerfLogger::moduleCreateCacheHit([moduleName UTF8String], requestId); 411 } 412 413 if (_instance) { 414 BridgeNativeModulePerfLogger::moduleCreateEnd([moduleName UTF8String], requestId); 415 } else { 416 BridgeNativeModulePerfLogger::moduleCreateFail([moduleName UTF8String], requestId); 417 } 418 return _instance; 419} 420 421- (NSString *)name 422{ 423 return ABI48_0_0RCTBridgeModuleNameForClass(_moduleClass); 424} 425 426- (NSArray<id<ABI48_0_0RCTBridgeMethod>> *)methods 427{ 428 [self calculateMethods]; 429 return _methods; 430} 431 432- (NSDictionary<NSString *, id<ABI48_0_0RCTBridgeMethod>> *)methodsByName 433{ 434 [self calculateMethods]; 435 return _methodsByName; 436} 437 438- (void)gatherConstants 439{ 440 return [self gatherConstantsAndSignalJSRequireEnding:NO]; 441} 442 443- (void)gatherConstantsAndSignalJSRequireEnding:(BOOL)startMarkers 444{ 445 NSString *moduleName = [self name]; 446 447 if (_hasConstantsToExport && !_constantsToExport) { 448 ABI48_0_0RCT_PROFILE_BEGIN_EVENT( 449 ABI48_0_0RCTProfileTagAlways, ([NSString stringWithFormat:@"[ABI48_0_0RCTModuleData gatherConstants] %@", _moduleClass]), nil); 450 (void)[self instance]; 451 452 if (startMarkers) { 453 /** 454 * Why do we instrument moduleJSRequireEndingStart here? 455 * - NativeModule requires from JS go through ModuleRegistry::getConfig(). 456 * - ModuleRegistry::getConfig() calls NativeModule::getConstants() first. 457 * - This delegates to ABI48_0_0RCTNativeModule::getConstants(), which calls ABI48_0_0RCTModuleData gatherConstants(). 458 * - Therefore, this is the first statement that executes after the NativeModule is created/initialized in a JS 459 * require. 460 */ 461 BridgeNativeModulePerfLogger::moduleJSRequireEndingStart([moduleName UTF8String]); 462 } 463 464 if (!ABI48_0_0RCTIsMainQueueExecutionOfConstantsToExportDisabled() && _requiresMainQueueSetup) { 465 if (!ABI48_0_0RCTIsMainQueue()) { 466 ABI48_0_0RCTLogWarn(@"Required dispatch_sync to load constants for %@. This may lead to deadlocks", _moduleClass); 467 } 468 469 ABI48_0_0RCTUnsafeExecuteOnMainQueueSync(^{ 470 self->_constantsToExport = [self->_instance constantsToExport] ?: @{}; 471 }); 472 } else { 473 _constantsToExport = [_instance constantsToExport] ?: @{}; 474 } 475 476 ABI48_0_0RCT_PROFILE_END_EVENT(ABI48_0_0RCTProfileTagAlways, @""); 477 } else if (startMarkers) { 478 /** 479 * If a NativeModule doesn't have constants, it isn't eagerly loaded until its methods are first invoked. 480 * Therefore, we should immediately start JSRequireEnding 481 */ 482 BridgeNativeModulePerfLogger::moduleJSRequireEndingStart([moduleName UTF8String]); 483 } 484} 485 486- (NSDictionary<NSString *, id> *)exportedConstants 487{ 488 [self gatherConstantsAndSignalJSRequireEnding:YES]; 489 NSDictionary<NSString *, id> *constants = _constantsToExport; 490 _constantsToExport = nil; // Not needed anymore 491 return constants; 492} 493 494- (dispatch_queue_t)methodQueue 495{ 496 if (_bridge.valid) { 497 id instance = self.instance; 498 ABI48_0_0RCTAssert(_methodQueue != nullptr, @"Module %@ has no methodQueue (instance: %@)", self, instance); 499 } 500 return _methodQueue; 501} 502 503- (void)invalidate 504{ 505 _methodQueue = nil; 506} 507 508- (NSString *)description 509{ 510 return [NSString stringWithFormat:@"<%@: %p; name=\"%@\">", [self class], self, self.name]; 511} 512 513@end 514