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 [_internalModulesSet addObject:resolvedModule]; 120 } else { 121 if (![_internalModulesPreResolution objectForKey:exportedInterface]) { 122 [_internalModulesPreResolution setObject:[NSMutableArray array] forKey:exportedInterface]; 123 } 124 125 [[_internalModulesPreResolution objectForKey:exportedInterface] addObject:internalModule]; 126 } 127 } 128} 129 130- (void)registerExportedModule:(EXExportedModule *)exportedModule 131{ 132 const NSString *exportedModuleName = [[exportedModule class] exportedModuleName]; 133 if (_exportedModules[exportedModuleName]) { 134 EXLogInfo(@"Module %@ overrides %@ as the module exported as %@.", exportedModule, _exportedModules[exportedModuleName], exportedModuleName); 135 } 136 137 _exportedModules[exportedModuleName] = exportedModule; 138 [_exportedModulesByClass setObject:exportedModule forKey:[exportedModule class]]; 139 [self maybeAddRegistryConsumer:exportedModule]; 140} 141 142- (void)registerViewManager:(EXViewManager *)viewManager 143{ 144 const NSString *exportedModuleName = [[viewManager class] exportedModuleName]; 145 if (_viewManagerModules[exportedModuleName]) { 146 EXLogInfo(@"View manager %@ overrides %@ as the module exported as %@.", viewManager, _viewManagerModules[exportedModuleName], exportedModuleName); 147 } 148 149 _viewManagerModules[exportedModuleName] = viewManager; 150 [self maybeAddRegistryConsumer:viewManager]; 151} 152 153- (void)registerSingletonModule:(id)singletonModule 154{ 155 if ([[singletonModule class] respondsToSelector:@selector(name)]) { 156 #pragma clang diagnostic push 157 #pragma clang diagnostic ignored "-Wobjc-method-access" 158 [_singletonModules setObject:singletonModule forKey:[[singletonModule class] name]]; 159 #pragma clang diagnostic pop 160 } else { 161 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."); 162 } 163} 164 165- (void)maybeAddRegistryConsumer:(id)maybeConsumer 166{ 167 if ([maybeConsumer conformsToProtocol:@protocol(EXModuleRegistryConsumer)]) { 168 [self addRegistryConsumer:(id<EXModuleRegistryConsumer>)maybeConsumer]; 169 } 170} 171 172- (void)addRegistryConsumer:(id<EXModuleRegistryConsumer>)registryConsumer 173{ 174 [_registryConsumers addPointer:(__bridge void * _Nullable)(registryConsumer)]; 175} 176 177# pragma mark - Registry API 178 179- (id)getModuleImplementingProtocol:(Protocol *)protocol 180{ 181 [self resolveInternalModulesConflicts]; 182 return [_internalModules objectForKey:protocol]; 183} 184 185- (EXExportedModule *)getExportedModuleForName:(NSString *)name 186{ 187 return _exportedModules[name]; 188} 189 190- (EXExportedModule *)getExportedModuleOfClass:(Class)moduleClass 191{ 192 return [_exportedModulesByClass objectForKey:moduleClass]; 193} 194 195- (id)getSingletonModuleForName:(NSString *)singletonModuleName 196{ 197 return [_singletonModules objectForKey:singletonModuleName]; 198} 199 200- (NSArray<id<EXInternalModule>> *)getAllInternalModules 201{ 202 [self resolveInternalModulesConflicts]; 203 return [_internalModulesSet allObjects]; 204} 205 206- (NSArray<EXExportedModule *> *)getAllExportedModules 207{ 208 return [_exportedModules allValues]; 209} 210 211- (NSArray<EXViewManager *> *)getAllViewManagers 212{ 213 return [_viewManagerModules allValues]; 214} 215 216- (NSArray *)getAllSingletonModules 217{ 218 return [_singletonModules allValues]; 219} 220 221@end 222