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