@@ -52,6 +52,8 @@ const int CONTENT_PORT_KEY_ID = 16;
52
52
const int EVENT_HANDLER_KEY_ID = 17 ;
53
53
const int KEEP_ME_LAST = 18 ;
54
54
55
+ const int MAX_RESOLVING_DEPTH = 50 ;
56
+
55
57
class DirectiveInjector implements DirectiveBinder {
56
58
static bool _isInit = false ;
57
59
static initUID () {
@@ -122,6 +124,11 @@ class DirectiveInjector implements DirectiveBinder {
122
124
Key _key8 = null ; dynamic _obj8; List <Key > _pKeys8; Function _factory8;
123
125
Key _key9 = null ; dynamic _obj9; List <Key > _pKeys9; Function _factory9;
124
126
127
+ // This value needs to be reset on every entry method into a given instance
128
+ // of directiveInjector. Currently those methods are only _getByKey
129
+ // and _enterGetDirectiveByKey.
130
+ int _resolvingDepth;
131
+
125
132
@Deprecated ("Deprecated. Use getFromParent instead." )
126
133
Object get parent => this ._parent;
127
134
@@ -199,46 +206,62 @@ class DirectiveInjector implements DirectiveBinder {
199
206
Object getFromParent (Type type) => _parent.get (type);
200
207
201
208
Object getByKey (Key key) {
209
+ _resolvingDepth = MAX_RESOLVING_DEPTH ;
202
210
var oldTag = _TAG_GET .makeCurrent ();
203
211
try {
204
212
return _getByKey (key);
205
- } on ResolvingError catch (e, s) {
206
- e.appendKey (key);
207
- rethrow ;
208
213
} finally {
209
214
oldTag.makeCurrent ();
210
215
}
211
216
}
212
217
Object getFromParentByKey (Key key) => _parent.getByKey (key);
213
218
214
219
Object _getByKey (Key key) {
215
- int uid = key.uid;
216
- if (uid == null || uid == UNDEFINED_ID ) return appInjector.getByKey (key);
217
- bool isDirective = uid < 0 ;
218
- return isDirective ? _getDirectiveByKey (key, uid, appInjector) : _getById (uid);
220
+ if (_resolvingDepth-- == 0 ) {
221
+ throw new DiCircularDependencyError (key);
222
+ }
223
+ try {
224
+ int uid = key.uid;
225
+ if (uid == null || uid == UNDEFINED_ID ) return appInjector.getByKey (key);
226
+ bool isDirective = uid < 0 ;
227
+ return isDirective ? _getDirectiveByKey (key, uid, appInjector) : _getById (uid);
228
+ } on ResolvingError catch (e, s) {
229
+ e.appendKey (key);
230
+ rethrow ;
231
+ }
232
+ }
233
+
234
+ Object _enterGetDirectiveByKey (Key k, int visType, Injector i) {
235
+ try {
236
+ _resolvingDepth = MAX_RESOLVING_DEPTH ;
237
+ return _getDirectiveByKey (k, visType, i);
238
+ } on ResolvingError catch (e, s) {
239
+ e.appendKey (k);
240
+ rethrow ;
241
+ }
219
242
}
220
243
221
244
Object _getDirectiveByKey (Key k, int visType, Injector i) {
222
245
do {
223
- if (_key0 == null ) break ; if (identical (_key0, k)) return _obj0 == null ? _obj0 = _new (_pKeys0, _factory0) : _obj0;
224
- if (_key1 == null ) break ; if (identical (_key1, k)) return _obj1 == null ? _obj1 = _new (_pKeys1, _factory1) : _obj1;
225
- if (_key2 == null ) break ; if (identical (_key2, k)) return _obj2 == null ? _obj2 = _new (_pKeys2, _factory2) : _obj2;
226
- if (_key3 == null ) break ; if (identical (_key3, k)) return _obj3 == null ? _obj3 = _new (_pKeys3, _factory3) : _obj3;
227
- if (_key4 == null ) break ; if (identical (_key4, k)) return _obj4 == null ? _obj4 = _new (_pKeys4, _factory4) : _obj4;
228
- if (_key5 == null ) break ; if (identical (_key5, k)) return _obj5 == null ? _obj5 = _new (_pKeys5, _factory5) : _obj5;
229
- if (_key6 == null ) break ; if (identical (_key6, k)) return _obj6 == null ? _obj6 = _new (_pKeys6, _factory6) : _obj6;
230
- if (_key7 == null ) break ; if (identical (_key7, k)) return _obj7 == null ? _obj7 = _new (_pKeys7, _factory7) : _obj7;
231
- if (_key8 == null ) break ; if (identical (_key8, k)) return _obj8 == null ? _obj8 = _new (_pKeys8, _factory8) : _obj8;
232
- if (_key9 == null ) break ; if (identical (_key9, k)) return _obj9 == null ? _obj9 = _new (_pKeys9, _factory9) : _obj9;
246
+ if (_key0 == null ) break ; if (identical (_key0, k)) return _obj0 == null ? _obj0 = _new (k, _pKeys0, _factory0) : _obj0;
247
+ if (_key1 == null ) break ; if (identical (_key1, k)) return _obj1 == null ? _obj1 = _new (k, _pKeys1, _factory1) : _obj1;
248
+ if (_key2 == null ) break ; if (identical (_key2, k)) return _obj2 == null ? _obj2 = _new (k, _pKeys2, _factory2) : _obj2;
249
+ if (_key3 == null ) break ; if (identical (_key3, k)) return _obj3 == null ? _obj3 = _new (k, _pKeys3, _factory3) : _obj3;
250
+ if (_key4 == null ) break ; if (identical (_key4, k)) return _obj4 == null ? _obj4 = _new (k, _pKeys4, _factory4) : _obj4;
251
+ if (_key5 == null ) break ; if (identical (_key5, k)) return _obj5 == null ? _obj5 = _new (k, _pKeys5, _factory5) : _obj5;
252
+ if (_key6 == null ) break ; if (identical (_key6, k)) return _obj6 == null ? _obj6 = _new (k, _pKeys6, _factory6) : _obj6;
253
+ if (_key7 == null ) break ; if (identical (_key7, k)) return _obj7 == null ? _obj7 = _new (k, _pKeys7, _factory7) : _obj7;
254
+ if (_key8 == null ) break ; if (identical (_key8, k)) return _obj8 == null ? _obj8 = _new (k, _pKeys8, _factory8) : _obj8;
255
+ if (_key9 == null ) break ; if (identical (_key9, k)) return _obj9 == null ? _obj9 = _new (k, _pKeys9, _factory9) : _obj9;
233
256
} while (false );
234
257
switch (visType) {
235
258
case VISIBILITY_LOCAL : return appInjector.getByKey (k);
236
- case VISIBILITY_DIRECT_CHILD : return _parent._getDirectiveByKey (k, VISIBILITY_LOCAL , i);
237
- case VISIBILITY_CHILDREN : return _parent._getDirectiveByKey (k, VISIBILITY_CHILDREN , i);
259
+ case VISIBILITY_DIRECT_CHILD : return _parent._enterGetDirectiveByKey (k, VISIBILITY_LOCAL , i);
260
+ case VISIBILITY_CHILDREN : return _parent._enterGetDirectiveByKey (k, VISIBILITY_CHILDREN , i);
238
261
// SHADOW
239
- case VISIBILITY_COMPONENT_LOCAL : return _parent._getDirectiveByKey (k, VISIBILITY_LOCAL , i);
240
- case VISIBILITY_COMPONENT_DIRECT_CHILD : return _parent._getDirectiveByKey (k, VISIBILITY_DIRECT_CHILD , i);
241
- case VISIBILITY_COMPONENT_CHILDREN : return _parent._getDirectiveByKey (k, VISIBILITY_CHILDREN , i);
262
+ case VISIBILITY_COMPONENT_LOCAL : return _parent._enterGetDirectiveByKey (k, VISIBILITY_LOCAL , i);
263
+ case VISIBILITY_COMPONENT_DIRECT_CHILD : return _parent._enterGetDirectiveByKey (k, VISIBILITY_DIRECT_CHILD , i);
264
+ case VISIBILITY_COMPONENT_CHILDREN : return _parent._enterGetDirectiveByKey (k, VISIBILITY_CHILDREN , i);
242
265
default : throw null ;
243
266
}
244
267
}
@@ -275,7 +298,7 @@ class DirectiveInjector implements DirectiveBinder {
275
298
}
276
299
}
277
300
278
- dynamic _new (List <Key > paramKeys, Function fn) {
301
+ dynamic _new (Key k, List <Key > paramKeys, Function fn) {
279
302
var oldTag = _TAG_GET .makeCurrent ();
280
303
int size = paramKeys.length;
281
304
var obj;
@@ -439,3 +462,26 @@ class DefaultDirectiveInjector extends DirectiveInjector {
439
462
}
440
463
}
441
464
}
465
+
466
+ // For efficiency we run through the maximum resolving depth and unwind
467
+ // instead of setting 'resolving' key per type.
468
+ class DiCircularDependencyError extends ResolvingError {
469
+ DiCircularDependencyError (key) : super (key);
470
+
471
+ // strips the cyclical part of the chain.
472
+ List <Key > get stripCycle {
473
+ List <Key > rkeys = keys.reversed.toList ();
474
+ for (num i = 0 ; i < rkeys.length; i++ ) {
475
+ // Skipping 'cycles' of length 1, which represent resolution
476
+ // switching injectors and not true cycles.
477
+ for (num j = i + 2 ; j < rkeys.length; j++ ) {
478
+ if (rkeys[i] == rkeys[j]) return rkeys.sublist (0 , j + 1 );
479
+ }
480
+ }
481
+ return rkeys;
482
+ }
483
+
484
+ String get resolveChain => stripCycle.join (' -> ' );
485
+ String toString () => "circular dependency (${resolveChain })" ;
486
+ }
487
+
0 commit comments