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 *, id> *singletonModules; 18 19@property NSPointerArray *registryConsumers; 20 21@end 22 23@implementation EXModuleRegistry 24 25# pragma mark - Lifecycle 26 27- (instancetype)init 28{ 29 if (self = [super init]) { 30 _internalModulesPreResolution = [NSMapTable weakToStrongObjectsMapTable]; 31 _exportedModulesByClass = [NSMapTable weakToWeakObjectsMapTable]; 32 _exportedModules = [NSMutableDictionary dictionary]; 33 _singletonModules = [NSMutableDictionary dictionary]; 34 _registryConsumers = [NSPointerArray weakObjectsPointerArray]; 35 } 36 return self; 37} 38 39- (instancetype)initWithInternalModules:(NSSet<id<EXInternalModule>> *)internalModules 40 exportedModules:(NSSet<EXExportedModule *> *)exportedModules 41 singletonModules:(NSSet *)singletonModules 42{ 43 if (self = [self init]) { 44 for (id<EXInternalModule> internalModule in internalModules) { 45 [self registerInternalModule:internalModule]; 46 } 47 48 for (EXExportedModule *exportedModule in exportedModules) { 49 [self registerExportedModule:exportedModule]; 50 } 51 52 for (id singletonModule in singletonModules) { 53 [self registerSingletonModule:singletonModule]; 54 } 55 } 56 return self; 57} 58 59- (void)setDelegate:(id<EXModuleRegistryDelegate>)delegate 60{ 61 _delegate = delegate; 62} 63 64// Fills in _internalModules and _internalModulesSet 65- (void)resolveInternalModulesConflicts 66{ 67 if (_internalModules) { 68 // Conflict resolution has already been completed. 69 return; 70 } 71 72 _internalModules = [NSMapTable weakToStrongObjectsMapTable]; 73 _internalModulesSet = [NSMutableSet new]; 74 for (Protocol *protocol in _internalModulesPreResolution) { 75 NSArray<id<EXInternalModule>> *conflictingModules = [_internalModulesPreResolution objectForKey:protocol]; 76 77 id<EXInternalModule> resolvedModule; 78 if ([conflictingModules count] > 1 && _delegate) { 79 resolvedModule = [_delegate pickInternalModuleImplementingInterface:protocol fromAmongModules:conflictingModules]; 80 } else { 81 resolvedModule = [conflictingModules lastObject]; 82 } 83 84 [_internalModules setObject:resolvedModule forKey:protocol]; 85 [self maybeAddRegistryConsumer:resolvedModule]; 86 [_internalModulesSet addObject:resolvedModule]; 87 } 88 89 _internalModulesPreResolution = nil; // Remove references to discarded modules 90} 91 92- (void)initialize 93{ 94 [self resolveInternalModulesConflicts]; 95 for (id<EXModuleRegistryConsumer> registryConsumer in _registryConsumers) { 96 [registryConsumer setModuleRegistry:self]; 97 } 98} 99 100# pragma mark - Registering modules 101 102- (void)registerInternalModule:(id<EXInternalModule>)internalModule 103{ 104 for (Protocol *exportedInterface in [[internalModule class] exportedInterfaces]) { 105 if (_internalModules) { 106 id<EXInternalModule> resolvedModule = internalModule; 107 if (_delegate && [_internalModules objectForKey:exportedInterface]) { 108 id<EXInternalModule> oldModule = [_internalModules objectForKey:exportedInterface]; 109 resolvedModule = [_delegate pickInternalModuleImplementingInterface:exportedInterface fromAmongModules:@[oldModule, internalModule]]; 110 } 111 [_internalModules setObject:resolvedModule forKey:exportedInterface]; 112 [self maybeAddRegistryConsumer:resolvedModule]; 113 [_internalModulesSet addObject:resolvedModule]; 114 } else { 115 if (![_internalModulesPreResolution objectForKey:exportedInterface]) { 116 [_internalModulesPreResolution setObject:[NSMutableArray array] forKey:exportedInterface]; 117 } 118 119 [[_internalModulesPreResolution objectForKey:exportedInterface] addObject:internalModule]; 120 } 121 } 122} 123 124- (void)registerExportedModule:(EXExportedModule *)exportedModule 125{ 126 const NSString *exportedModuleName = [[exportedModule class] exportedModuleName]; 127 if (_exportedModules[exportedModuleName]) { 128 EXLogInfo(@"Module %@ overrides %@ as the module exported as %@.", exportedModule, _exportedModules[exportedModuleName], exportedModuleName); 129 } 130 131 _exportedModules[exportedModuleName] = exportedModule; 132 [_exportedModulesByClass setObject:exportedModule forKey:[exportedModule class]]; 133 [self maybeAddRegistryConsumer:exportedModule]; 134} 135 136- (void)registerSingletonModule:(id)singletonModule 137{ 138 if ([[singletonModule class] respondsToSelector:@selector(name)]) { 139 #pragma clang diagnostic push 140 #pragma clang diagnostic ignored "-Wobjc-method-access" 141 [_singletonModules setObject:singletonModule forKey:[[singletonModule class] name]]; 142 #pragma clang diagnostic pop 143 } else { 144 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."); 145 } 146} 147 148- (void)maybeAddRegistryConsumer:(id)maybeConsumer 149{ 150 if ([maybeConsumer conformsToProtocol:@protocol(EXModuleRegistryConsumer)]) { 151 [self addRegistryConsumer:(id<EXModuleRegistryConsumer>)maybeConsumer]; 152 } 153} 154 155- (void)addRegistryConsumer:(id<EXModuleRegistryConsumer>)registryConsumer 156{ 157 void *ptr = (__bridge void * _Nullable)registryConsumer; 158 159 for (id consumerPtr in _registryConsumers) { 160 if (consumerPtr == ptr) { 161 return; 162 } 163 } 164 [_registryConsumers addPointer:ptr]; 165} 166 167# pragma mark - Registry API 168 169- (id)getModuleImplementingProtocol:(Protocol *)protocol 170{ 171 [self resolveInternalModulesConflicts]; 172 return [_internalModules objectForKey:protocol]; 173} 174 175- (EXExportedModule *)getExportedModuleForName:(NSString *)name 176{ 177 return _exportedModules[name]; 178} 179 180- (EXExportedModule *)getExportedModuleOfClass:(Class)moduleClass 181{ 182 return [_exportedModulesByClass objectForKey:moduleClass]; 183} 184 185- (id)getSingletonModuleForName:(NSString *)singletonModuleName 186{ 187 return [_singletonModules objectForKey:singletonModuleName]; 188} 189 190- (NSArray<id<EXInternalModule>> *)getAllInternalModules 191{ 192 [self resolveInternalModulesConflicts]; 193 return [_internalModulesSet allObjects]; 194} 195 196- (NSArray<EXExportedModule *> *)getAllExportedModules 197{ 198 return [_exportedModules allValues]; 199} 200 201- (NSArray *)getAllSingletonModules 202{ 203 return [_singletonModules allValues]; 204} 205 206@end 207