1// Copyright © 2018 650 Industries. All rights reserved. 2 3#import <objc/runtime.h> 4 5#import <ExpoModulesCore/EXModuleRegistry.h> 6#import <ExpoModulesCore/EXModuleRegistryConsumer.h> 7 8@interface EXModuleRegistry () 9 10@property (nonatomic, weak) id<EXModuleRegistryDelegate> delegate; 11 12@property NSMutableSet<id<EXInternalModule>> *internalModulesSet; 13@property NSMapTable<Protocol *, id<EXInternalModule>> *internalModules; 14@property NSMapTable<Protocol *, NSMutableArray<id<EXInternalModule>> *> *internalModulesPreResolution; 15@property NSMapTable<Class, EXExportedModule *> *exportedModulesByClass; 16@property NSMutableDictionary<const NSString *, EXExportedModule *> *exportedModules; 17@property NSMutableDictionary<const NSString *, EXViewManager *> *viewManagerModules; 18@property NSMutableDictionary<const NSString *, id> *singletonModules; 19 20@property NSPointerArray *registryConsumers; 21 22@end 23 24@implementation EXModuleRegistry 25 26# pragma mark - Lifecycle 27 28- (instancetype)init 29{ 30 if (self = [super init]) { 31 _internalModulesPreResolution = [NSMapTable weakToStrongObjectsMapTable]; 32 _exportedModulesByClass = [NSMapTable weakToWeakObjectsMapTable]; 33 _exportedModules = [NSMutableDictionary dictionary]; 34 _viewManagerModules = [NSMutableDictionary dictionary]; 35 _singletonModules = [NSMutableDictionary dictionary]; 36 _registryConsumers = [NSPointerArray weakObjectsPointerArray]; 37 } 38 return self; 39} 40 41- (instancetype)initWithInternalModules:(NSSet<id<EXInternalModule>> *)internalModules 42 exportedModules:(NSSet<EXExportedModule *> *)exportedModules 43 viewManagers:(NSSet<EXViewManager *> *)viewManagers 44 singletonModules:(NSSet *)singletonModules 45{ 46 if (self = [self init]) { 47 for (id<EXInternalModule> internalModule in internalModules) { 48 [self registerInternalModule:internalModule]; 49 } 50 51 for (EXExportedModule *exportedModule in exportedModules) { 52 [self registerExportedModule:exportedModule]; 53 } 54 55 for (EXViewManager *viewManager in viewManagers) { 56 [self registerViewManager:viewManager]; 57 } 58 59 for (id singletonModule in singletonModules) { 60 [self registerSingletonModule:singletonModule]; 61 } 62 } 63 return self; 64} 65 66- (void)setDelegate:(id<EXModuleRegistryDelegate>)delegate 67{ 68 _delegate = delegate; 69} 70 71// Fills in _internalModules and _internalModulesSet 72- (void)resolveInternalModulesConflicts 73{ 74 if (_internalModules) { 75 // Conflict resolution has already been completed. 76 return; 77 } 78 79 _internalModules = [NSMapTable weakToStrongObjectsMapTable]; 80 _internalModulesSet = [NSMutableSet new]; 81 for (Protocol *protocol in _internalModulesPreResolution) { 82 NSArray<id<EXInternalModule>> *conflictingModules = [_internalModulesPreResolution objectForKey:protocol]; 83 84 id<EXInternalModule> resolvedModule; 85 if ([conflictingModules count] > 1 && _delegate) { 86 resolvedModule = [_delegate pickInternalModuleImplementingInterface:protocol fromAmongModules:conflictingModules]; 87 } else { 88 resolvedModule = [conflictingModules lastObject]; 89 } 90 91 [_internalModules setObject:resolvedModule forKey:protocol]; 92 [self maybeAddRegistryConsumer:resolvedModule]; 93 [_internalModulesSet addObject:resolvedModule]; 94 } 95 96 _internalModulesPreResolution = nil; // Remove references to discarded modules 97} 98 99- (void)initialize 100{ 101 [self resolveInternalModulesConflicts]; 102 for (id<EXModuleRegistryConsumer> registryConsumer in _registryConsumers) { 103 [registryConsumer setModuleRegistry:self]; 104 } 105} 106 107# pragma mark - Registering modules 108 109- (void)registerInternalModule:(id<EXInternalModule>)internalModule 110{ 111 for (Protocol *exportedInterface in [[internalModule class] exportedInterfaces]) { 112 if (_internalModules) { 113 id<EXInternalModule> resolvedModule = internalModule; 114 if (_delegate && [_internalModules objectForKey:exportedInterface]) { 115 id<EXInternalModule> oldModule = [_internalModules objectForKey:exportedInterface]; 116 resolvedModule = [_delegate pickInternalModuleImplementingInterface:exportedInterface fromAmongModules:@[oldModule, internalModule]]; 117 } 118 [_internalModules setObject:resolvedModule forKey:exportedInterface]; 119 [self maybeAddRegistryConsumer:resolvedModule]; 120 [_internalModulesSet addObject:resolvedModule]; 121 } else { 122 if (![_internalModulesPreResolution objectForKey:exportedInterface]) { 123 [_internalModulesPreResolution setObject:[NSMutableArray array] forKey:exportedInterface]; 124 } 125 126 [[_internalModulesPreResolution objectForKey:exportedInterface] addObject:internalModule]; 127 } 128 } 129} 130 131- (void)registerExportedModule:(EXExportedModule *)exportedModule 132{ 133 const NSString *exportedModuleName = [[exportedModule class] exportedModuleName]; 134 if (_exportedModules[exportedModuleName]) { 135 EXLogInfo(@"Module %@ overrides %@ as the module exported as %@.", exportedModule, _exportedModules[exportedModuleName], exportedModuleName); 136 } 137 138 _exportedModules[exportedModuleName] = exportedModule; 139 [_exportedModulesByClass setObject:exportedModule forKey:[exportedModule class]]; 140 [self maybeAddRegistryConsumer:exportedModule]; 141} 142 143- (void)registerViewManager:(EXViewManager *)viewManager 144{ 145 const NSString *exportedModuleName = [[viewManager class] exportedModuleName]; 146 if (_viewManagerModules[exportedModuleName]) { 147 EXLogInfo(@"View manager %@ overrides %@ as the module exported as %@.", viewManager, _viewManagerModules[exportedModuleName], exportedModuleName); 148 } 149 150 _viewManagerModules[exportedModuleName] = viewManager; 151 [self maybeAddRegistryConsumer:viewManager]; 152} 153 154- (void)registerSingletonModule:(id)singletonModule 155{ 156 if ([[singletonModule class] respondsToSelector:@selector(name)]) { 157 #pragma clang diagnostic push 158 #pragma clang diagnostic ignored "-Wobjc-method-access" 159 [_singletonModules setObject:singletonModule forKey:[[singletonModule class] name]]; 160 #pragma clang diagnostic pop 161 } else { 162 EXLogWarn(@"One of the singleton modules does not respond to +(NSString *)name selector. This probably means you're either try to pass a strange object as a singleton module (it won't get registered in the module registry, sorry) or the EXSingletonModule interface and the EXModuleRegistry implementations versions are out of sync, which means things will probably not work as expected."); 163 } 164} 165 166- (void)maybeAddRegistryConsumer:(id)maybeConsumer 167{ 168 if ([maybeConsumer conformsToProtocol:@protocol(EXModuleRegistryConsumer)]) { 169 [self addRegistryConsumer:(id<EXModuleRegistryConsumer>)maybeConsumer]; 170 } 171} 172 173- (void)addRegistryConsumer:(id<EXModuleRegistryConsumer>)registryConsumer 174{ 175 void *ptr = (__bridge void * _Nullable)registryConsumer; 176 177 for (id consumerPtr in _registryConsumers) { 178 if (consumerPtr == ptr) { 179 return; 180 } 181 } 182 [_registryConsumers addPointer:ptr]; 183} 184 185# pragma mark - Registry API 186 187- (id)getModuleImplementingProtocol:(Protocol *)protocol 188{ 189 [self resolveInternalModulesConflicts]; 190 return [_internalModules objectForKey:protocol]; 191} 192 193- (EXExportedModule *)getExportedModuleForName:(NSString *)name 194{ 195 return _exportedModules[name]; 196} 197 198- (EXExportedModule *)getExportedModuleOfClass:(Class)moduleClass 199{ 200 return [_exportedModulesByClass objectForKey:moduleClass]; 201} 202 203- (id)getSingletonModuleForName:(NSString *)singletonModuleName 204{ 205 return [_singletonModules objectForKey:singletonModuleName]; 206} 207 208- (NSArray<id<EXInternalModule>> *)getAllInternalModules 209{ 210 [self resolveInternalModulesConflicts]; 211 return [_internalModulesSet allObjects]; 212} 213 214- (NSArray<EXExportedModule *> *)getAllExportedModules 215{ 216 return [_exportedModules allValues]; 217} 218 219- (NSArray<EXViewManager *> *)getAllViewManagers 220{ 221 return [_viewManagerModules allValues]; 222} 223 224- (NSArray *)getAllSingletonModules 225{ 226 return [_singletonModules allValues]; 227} 228 229@end 230