RCTModuleData.mm 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. /*
  2. * Copyright (c) Facebook, Inc. and its 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. #import "RCTModuleData.h"
  8. #import <objc/runtime.h>
  9. #include <mutex>
  10. #import "RCTBridge+Private.h"
  11. #import "RCTBridge.h"
  12. #import "RCTLog.h"
  13. #import "RCTModuleMethod.h"
  14. #import "RCTProfile.h"
  15. #import "RCTUtils.h"
  16. @implementation RCTModuleData {
  17. NSDictionary<NSString *, id> *_constantsToExport;
  18. NSString *_queueName;
  19. __weak RCTBridge *_bridge;
  20. RCTBridgeModuleProvider _moduleProvider;
  21. std::mutex _instanceLock;
  22. BOOL _setupComplete;
  23. }
  24. @synthesize methods = _methods;
  25. @synthesize methodsByName = _methodsByName;
  26. @synthesize instance = _instance;
  27. @synthesize methodQueue = _methodQueue;
  28. - (void)setUp
  29. {
  30. _implementsBatchDidComplete = [_moduleClass instancesRespondToSelector:@selector(batchDidComplete)];
  31. _implementsPartialBatchDidFlush = [_moduleClass instancesRespondToSelector:@selector(partialBatchDidFlush)];
  32. // If a module overrides `constantsToExport` and doesn't implement `requiresMainQueueSetup`, then we must assume
  33. // that it must be called on the main thread, because it may need to access UIKit.
  34. _hasConstantsToExport = [_moduleClass instancesRespondToSelector:@selector(constantsToExport)];
  35. const BOOL implementsRequireMainQueueSetup = [_moduleClass respondsToSelector:@selector(requiresMainQueueSetup)];
  36. if (implementsRequireMainQueueSetup) {
  37. _requiresMainQueueSetup = [_moduleClass requiresMainQueueSetup];
  38. } else {
  39. static IMP objectInitMethod;
  40. static dispatch_once_t onceToken;
  41. dispatch_once(&onceToken, ^{
  42. objectInitMethod = [NSObject instanceMethodForSelector:@selector(init)];
  43. });
  44. // If a module overrides `init` then we must assume that it expects to be
  45. // initialized on the main thread, because it may need to access UIKit.
  46. const BOOL hasCustomInit =
  47. !_instance && [_moduleClass instanceMethodForSelector:@selector(init)] != objectInitMethod;
  48. _requiresMainQueueSetup = _hasConstantsToExport || hasCustomInit;
  49. if (_requiresMainQueueSetup) {
  50. const char *methodName = "";
  51. if (_hasConstantsToExport) {
  52. methodName = "constantsToExport";
  53. } else if (hasCustomInit) {
  54. methodName = "init";
  55. }
  56. RCTLogWarn(
  57. @"Module %@ requires main queue setup since it overrides `%s` but doesn't implement "
  58. "`requiresMainQueueSetup`. In a future release React Native will default to initializing all native modules "
  59. "on a background thread unless explicitly opted-out of.",
  60. _moduleClass,
  61. methodName);
  62. }
  63. }
  64. }
  65. - (instancetype)initWithModuleClass:(Class)moduleClass bridge:(RCTBridge *)bridge
  66. {
  67. return [self initWithModuleClass:moduleClass
  68. moduleProvider:^id<RCTBridgeModule> {
  69. return [moduleClass new];
  70. }
  71. bridge:bridge];
  72. }
  73. - (instancetype)initWithModuleClass:(Class)moduleClass
  74. moduleProvider:(RCTBridgeModuleProvider)moduleProvider
  75. bridge:(RCTBridge *)bridge
  76. {
  77. if (self = [super init]) {
  78. _bridge = bridge;
  79. _moduleClass = moduleClass;
  80. _moduleProvider = [moduleProvider copy];
  81. [self setUp];
  82. }
  83. return self;
  84. }
  85. - (instancetype)initWithModuleInstance:(id<RCTBridgeModule>)instance bridge:(RCTBridge *)bridge
  86. {
  87. if (self = [super init]) {
  88. _bridge = bridge;
  89. _instance = instance;
  90. _moduleClass = [instance class];
  91. [self setUp];
  92. }
  93. return self;
  94. }
  95. RCT_NOT_IMPLEMENTED(-(instancetype)init);
  96. #pragma mark - private setup methods
  97. - (void)setUpInstanceAndBridge
  98. {
  99. RCT_PROFILE_BEGIN_EVENT(
  100. RCTProfileTagAlways,
  101. @"[RCTModuleData setUpInstanceAndBridge]",
  102. @{@"moduleClass" : NSStringFromClass(_moduleClass)});
  103. {
  104. std::unique_lock<std::mutex> lock(_instanceLock);
  105. if (!_setupComplete && _bridge.valid) {
  106. if (!_instance) {
  107. if (RCT_DEBUG && _requiresMainQueueSetup) {
  108. RCTAssertMainQueue();
  109. }
  110. RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"[RCTModuleData setUpInstanceAndBridge] Create module", nil);
  111. _instance = _moduleProvider ? _moduleProvider() : nil;
  112. RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
  113. if (!_instance) {
  114. // Module init returned nil, probably because automatic instantiation
  115. // of the module is not supported, and it is supposed to be passed in to
  116. // the bridge constructor. Mark setup complete to avoid doing more work.
  117. _setupComplete = YES;
  118. RCTLogWarn(
  119. @"The module %@ is returning nil from its constructor. You "
  120. "may need to instantiate it yourself and pass it into the "
  121. "bridge.",
  122. _moduleClass);
  123. }
  124. }
  125. if (_instance && RCTProfileIsProfiling()) {
  126. RCTProfileHookInstance(_instance);
  127. }
  128. // Bridge must be set before methodQueue is set up, as methodQueue
  129. // initialization requires it (View Managers get their queue by calling
  130. // self.bridge.uiManager.methodQueue)
  131. [self setBridgeForInstance];
  132. }
  133. [self setUpMethodQueue];
  134. }
  135. RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
  136. // This is called outside of the lock in order to prevent deadlock issues
  137. // because the logic in `finishSetupForInstance` can cause
  138. // `moduleData.instance` to be accessed re-entrantly.
  139. if (_bridge.moduleSetupComplete) {
  140. [self finishSetupForInstance];
  141. } else {
  142. // If we're here, then the module is completely initialized,
  143. // except for what finishSetupForInstance does. When the instance
  144. // method is called after moduleSetupComplete,
  145. // finishSetupForInstance will run. If _requiresMainQueueSetup
  146. // is true, getting the instance will block waiting for the main
  147. // thread, which could take a while if the main thread is busy
  148. // (I've seen 50ms in testing). So we clear that flag, since
  149. // nothing in finishSetupForInstance needs to be run on the main
  150. // thread.
  151. _requiresMainQueueSetup = NO;
  152. }
  153. }
  154. - (void)setBridgeForInstance
  155. {
  156. if ([_instance respondsToSelector:@selector(bridge)] && _instance.bridge != _bridge) {
  157. RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"[RCTModuleData setBridgeForInstance]", nil);
  158. @try {
  159. [(id)_instance setValue:_bridge forKey:@"bridge"];
  160. } @catch (NSException *exception) {
  161. RCTLogError(
  162. @"%@ has no setter or ivar for its bridge, which is not "
  163. "permitted. You must either @synthesize the bridge property, "
  164. "or provide your own setter method.",
  165. self.name);
  166. }
  167. RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
  168. }
  169. }
  170. - (void)finishSetupForInstance
  171. {
  172. if (!_setupComplete && _instance) {
  173. RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"[RCTModuleData finishSetupForInstance]", nil);
  174. _setupComplete = YES;
  175. [_bridge registerModuleForFrameUpdates:_instance withModuleData:self];
  176. [[NSNotificationCenter defaultCenter]
  177. postNotificationName:RCTDidInitializeModuleNotification
  178. object:_bridge
  179. userInfo:@{@"module" : _instance, @"bridge" : RCTNullIfNil(_bridge.parentBridge)}];
  180. RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
  181. }
  182. }
  183. - (void)setUpMethodQueue
  184. {
  185. if (_instance && !_methodQueue && _bridge.valid) {
  186. RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"[RCTModuleData setUpMethodQueue]", nil);
  187. BOOL implementsMethodQueue = [_instance respondsToSelector:@selector(methodQueue)];
  188. if (implementsMethodQueue && _bridge.valid) {
  189. _methodQueue = _instance.methodQueue;
  190. }
  191. if (!_methodQueue && _bridge.valid) {
  192. // Create new queue (store queueName, as it isn't retained by dispatch_queue)
  193. _queueName = [NSString stringWithFormat:@"com.facebook.react.%@Queue", self.name];
  194. _methodQueue = dispatch_queue_create(_queueName.UTF8String, DISPATCH_QUEUE_SERIAL);
  195. // assign it to the module
  196. if (implementsMethodQueue) {
  197. @try {
  198. [(id)_instance setValue:_methodQueue forKey:@"methodQueue"];
  199. } @catch (NSException *exception) {
  200. RCTLogError(
  201. @"%@ is returning nil for its methodQueue, which is not "
  202. "permitted. You must either return a pre-initialized "
  203. "queue, or @synthesize the methodQueue to let the bridge "
  204. "create a queue for you.",
  205. self.name);
  206. }
  207. }
  208. }
  209. RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
  210. }
  211. }
  212. - (void)calculateMethods
  213. {
  214. if (_methods && _methodsByName) {
  215. return;
  216. }
  217. NSMutableArray<id<RCTBridgeMethod>> *moduleMethods = [NSMutableArray new];
  218. NSMutableDictionary<NSString *, id<RCTBridgeMethod>> *moduleMethodsByName = [NSMutableDictionary new];
  219. if ([_moduleClass instancesRespondToSelector:@selector(methodsToExport)]) {
  220. [moduleMethods addObjectsFromArray:[self.instance methodsToExport]];
  221. }
  222. unsigned int methodCount;
  223. Class cls = _moduleClass;
  224. while (cls && cls != [NSObject class] && cls != [NSProxy class]) {
  225. Method *methods = class_copyMethodList(object_getClass(cls), &methodCount);
  226. for (unsigned int i = 0; i < methodCount; i++) {
  227. Method method = methods[i];
  228. SEL selector = method_getName(method);
  229. if ([NSStringFromSelector(selector) hasPrefix:@"__rct_export__"]) {
  230. IMP imp = method_getImplementation(method);
  231. auto exportedMethod = ((const RCTMethodInfo *(*)(id, SEL))imp)(_moduleClass, selector);
  232. id<RCTBridgeMethod> moduleMethod = [[RCTModuleMethod alloc] initWithExportedMethod:exportedMethod
  233. moduleClass:_moduleClass];
  234. NSString *str = [NSString stringWithUTF8String:moduleMethod.JSMethodName];
  235. [moduleMethodsByName setValue:moduleMethod forKey:str];
  236. [moduleMethods addObject:moduleMethod];
  237. }
  238. }
  239. free(methods);
  240. cls = class_getSuperclass(cls);
  241. }
  242. _methods = [moduleMethods copy];
  243. _methodsByName = [moduleMethodsByName copy];
  244. }
  245. #pragma mark - public getters
  246. - (BOOL)hasInstance
  247. {
  248. std::unique_lock<std::mutex> lock(_instanceLock);
  249. return _instance != nil;
  250. }
  251. - (id<RCTBridgeModule>)instance
  252. {
  253. if (!_setupComplete) {
  254. RCT_PROFILE_BEGIN_EVENT(
  255. RCTProfileTagAlways, ([NSString stringWithFormat:@"[RCTModuleData instanceForClass:%@]", _moduleClass]), nil);
  256. if (_requiresMainQueueSetup) {
  257. // The chances of deadlock here are low, because module init very rarely
  258. // calls out to other threads, however we can't control when a module might
  259. // get accessed by client code during bridge setup, and a very low risk of
  260. // deadlock is better than a fairly high risk of an assertion being thrown.
  261. RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"[RCTModuleData instance] main thread setup", nil);
  262. if (!RCTIsMainQueue()) {
  263. RCTLogWarn(@"RCTBridge required dispatch_sync to load %@. This may lead to deadlocks", _moduleClass);
  264. }
  265. RCTUnsafeExecuteOnMainQueueSync(^{
  266. [self setUpInstanceAndBridge];
  267. });
  268. RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
  269. } else {
  270. [self setUpInstanceAndBridge];
  271. }
  272. RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
  273. }
  274. return _instance;
  275. }
  276. - (NSString *)name
  277. {
  278. return RCTBridgeModuleNameForClass(_moduleClass);
  279. }
  280. - (NSArray<id<RCTBridgeMethod>> *)methods
  281. {
  282. [self calculateMethods];
  283. return _methods;
  284. }
  285. - (NSDictionary<NSString *, id<RCTBridgeMethod>> *)methodsByName
  286. {
  287. [self calculateMethods];
  288. return _methodsByName;
  289. }
  290. - (void)gatherConstants
  291. {
  292. if (_hasConstantsToExport && !_constantsToExport) {
  293. RCT_PROFILE_BEGIN_EVENT(
  294. RCTProfileTagAlways, ([NSString stringWithFormat:@"[RCTModuleData gatherConstants] %@", _moduleClass]), nil);
  295. (void)[self instance];
  296. if (_requiresMainQueueSetup) {
  297. if (!RCTIsMainQueue()) {
  298. RCTLogWarn(@"Required dispatch_sync to load constants for %@. This may lead to deadlocks", _moduleClass);
  299. }
  300. RCTUnsafeExecuteOnMainQueueSync(^{
  301. self->_constantsToExport = [self->_instance constantsToExport] ?: @{};
  302. });
  303. } else {
  304. _constantsToExport = [_instance constantsToExport] ?: @{};
  305. }
  306. RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
  307. }
  308. }
  309. - (NSDictionary<NSString *, id> *)exportedConstants
  310. {
  311. [self gatherConstants];
  312. NSDictionary<NSString *, id> *constants = _constantsToExport;
  313. _constantsToExport = nil; // Not needed anymore
  314. return constants;
  315. }
  316. - (dispatch_queue_t)methodQueue
  317. {
  318. if (_bridge.valid) {
  319. id instance = self.instance;
  320. RCTAssert(_methodQueue != nullptr, @"Module %@ has no methodQueue (instance: %@)", self, instance);
  321. }
  322. return _methodQueue;
  323. }
  324. - (void)invalidate
  325. {
  326. _methodQueue = nil;
  327. }
  328. - (NSString *)description
  329. {
  330. return [NSString stringWithFormat:@"<%@: %p; name=\"%@\">", [self class], self, self.name];
  331. }
  332. @end