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