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