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