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