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