@@ -26,7 +26,14 @@ final SOURCE_LIGHT_DOM_KEY = new Key(SourceLightDom);
26
26
final TEMPLATE_LOADER_KEY = new Key (TemplateLoader );
27
27
final SHADOW_ROOT_KEY = new Key (ShadowRoot );
28
28
29
- final num MAX_DEPTH = 1 << 30 ;
29
+ final int NO_CONSTRUCTION = 0 ;
30
+
31
+ // Maximum parent directive injectors that would be traversed.
32
+ final int MAX_TREE_DEPTH = 1 << 30 ;
33
+
34
+ // Maximum number of concurrent constructions a directive injector would
35
+ // support before throwing an error.
36
+ final int MAX_CONSTRUCTION_DEPTH = 50 ;
30
37
31
38
const int VISIBILITY_LOCAL = - 1 ;
32
39
const int VISIBILITY_DIRECT_CHILD = - 2 ;
@@ -132,6 +139,12 @@ class DirectiveInjector implements DirectiveBinder {
132
139
Key _key8 = null ; dynamic _obj8; List <Key > _pKeys8; Function _factory8;
133
140
Key _key9 = null ; dynamic _obj9; List <Key > _pKeys9; Function _factory9;
134
141
142
+ // Keeps track of how many dependent constructions are being performed
143
+ // in the directive injector.
144
+ // It should be NO_CONSTRUCTION, until first call into _new sets it to 1
145
+ // incremented on after each next call.
146
+ int _constructionDepth;
147
+
135
148
@Deprecated ("Deprecated. Use getFromParent instead." )
136
149
Object get parent => this ._parent;
137
150
@@ -153,16 +166,18 @@ class DirectiveInjector implements DirectiveBinder {
153
166
DirectiveInjector (DirectiveInjector parent, appInjector, this ._node, this ._nodeAttrs,
154
167
this ._eventHandler, this .scope, this ._animate, [View view])
155
168
: _parent = parent,
156
- _appInjector = appInjector,
157
- _view = view == null && parent != null ? parent._view : view;
169
+ _appInjector = appInjector,
170
+ _view = view == null && parent != null ? parent._view : view,
171
+ _constructionDepth = NO_CONSTRUCTION ;
158
172
159
173
DirectiveInjector ._default (this ._parent, this ._appInjector)
160
174
: _node = null ,
161
175
_nodeAttrs = null ,
162
176
_eventHandler = null ,
163
177
scope = null ,
164
178
_view = null ,
165
- _animate = null ;
179
+ _animate = null ,
180
+ _constructionDepth = NO_CONSTRUCTION ;
166
181
167
182
void bind (key, {dynamic toValue: DEFAULT_VALUE ,
168
183
Function toFactory: DEFAULT_VALUE ,
@@ -213,9 +228,6 @@ class DirectiveInjector implements DirectiveBinder {
213
228
var oldTag = _TAG_GET .makeCurrent ();
214
229
try {
215
230
return _getByKey (key, _appInjector);
216
- } on ResolvingError catch (e, s) {
217
- e.appendKey (key);
218
- rethrow ;
219
231
} finally {
220
232
oldTag.makeCurrent ();
221
233
}
@@ -230,42 +242,47 @@ class DirectiveInjector implements DirectiveBinder {
230
242
}
231
243
232
244
Object _getByKey (Key key, Injector appInjector) {
233
- int uid = key.uid;
234
- if (uid == null || uid == UNDEFINED_ID ) return appInjector.getByKey (key);
235
- bool isDirective = uid < 0 ;
236
- return isDirective ? _getDirectiveByKey (key, uid, appInjector) : _getById (uid);
245
+ try {
246
+ int uid = key.uid;
247
+ if (uid == null || uid == UNDEFINED_ID ) return appInjector.getByKey (key);
248
+ bool isDirective = uid < 0 ;
249
+ return isDirective ? _getDirectiveByKey (key, uid, appInjector) : _getById (uid);
250
+ } on ResolvingError catch (e) {
251
+ e.appendKey (key);
252
+ rethrow ;
253
+ }
237
254
}
238
255
239
256
num _getDepth (int visType) {
240
257
switch (visType) {
241
258
case VISIBILITY_LOCAL : return 0 ;
242
259
case VISIBILITY_DIRECT_CHILD : return 1 ;
243
- case VISIBILITY_CHILDREN : return MAX_DEPTH ;
260
+ case VISIBILITY_CHILDREN : return MAX_TREE_DEPTH ;
244
261
default : throw null ;
245
262
}
246
263
}
247
264
248
265
_getDirectiveByKey (Key k, int visType, Injector appInjector) {
249
- num depth = _getDepth (visType);
266
+ num treeDepth = _getDepth (visType);
250
267
// ci stands for currentInjector, abbreviated for readability.
251
268
var ci = this ;
252
- while (ci != null && depth >= 0 ) {
269
+ while (ci != null && treeDepth >= 0 ) {
253
270
do {
254
- if (ci._key0 == null ) break ; if (identical (ci._key0, k)) return ci._obj0 == null ? ci._obj0 = ci._new (ci._pKeys0, ci._factory0) : ci._obj0;
255
- if (ci._key1 == null ) break ; if (identical (ci._key1, k)) return ci._obj1 == null ? ci._obj1 = ci._new (ci._pKeys1, ci._factory1) : ci._obj1;
256
- if (ci._key2 == null ) break ; if (identical (ci._key2, k)) return ci._obj2 == null ? ci._obj2 = ci._new (ci._pKeys2, ci._factory2) : ci._obj2;
257
- if (ci._key3 == null ) break ; if (identical (ci._key3, k)) return ci._obj3 == null ? ci._obj3 = ci._new (ci._pKeys3, ci._factory3) : ci._obj3;
258
- if (ci._key4 == null ) break ; if (identical (ci._key4, k)) return ci._obj4 == null ? ci._obj4 = ci._new (ci._pKeys4, ci._factory4) : ci._obj4;
259
- if (ci._key5 == null ) break ; if (identical (ci._key5, k)) return ci._obj5 == null ? ci._obj5 = ci._new (ci._pKeys5, ci._factory5) : ci._obj5;
260
- if (ci._key6 == null ) break ; if (identical (ci._key6, k)) return ci._obj6 == null ? ci._obj6 = ci._new (ci._pKeys6, ci._factory6) : ci._obj6;
261
- if (ci._key7 == null ) break ; if (identical (ci._key7, k)) return ci._obj7 == null ? ci._obj7 = ci._new (ci._pKeys7, ci._factory7) : ci._obj7;
262
- if (ci._key8 == null ) break ; if (identical (ci._key8, k)) return ci._obj8 == null ? ci._obj8 = ci._new (ci._pKeys8, ci._factory8) : ci._obj8;
263
- if (ci._key9 == null ) break ; if (identical (ci._key9, k)) return ci._obj9 == null ? ci._obj9 = ci._new (ci._pKeys9, ci._factory9) : ci._obj9;
271
+ if (ci._key0 == null ) break ; if (identical (ci._key0, k)) return ci._obj0 == null ? ci._obj0 = ci._new (k, ci._pKeys0, ci._factory0) : ci._obj0;
272
+ if (ci._key1 == null ) break ; if (identical (ci._key1, k)) return ci._obj1 == null ? ci._obj1 = ci._new (k, ci._pKeys1, ci._factory1) : ci._obj1;
273
+ if (ci._key2 == null ) break ; if (identical (ci._key2, k)) return ci._obj2 == null ? ci._obj2 = ci._new (k, ci._pKeys2, ci._factory2) : ci._obj2;
274
+ if (ci._key3 == null ) break ; if (identical (ci._key3, k)) return ci._obj3 == null ? ci._obj3 = ci._new (k, ci._pKeys3, ci._factory3) : ci._obj3;
275
+ if (ci._key4 == null ) break ; if (identical (ci._key4, k)) return ci._obj4 == null ? ci._obj4 = ci._new (k, ci._pKeys4, ci._factory4) : ci._obj4;
276
+ if (ci._key5 == null ) break ; if (identical (ci._key5, k)) return ci._obj5 == null ? ci._obj5 = ci._new (k, ci._pKeys5, ci._factory5) : ci._obj5;
277
+ if (ci._key6 == null ) break ; if (identical (ci._key6, k)) return ci._obj6 == null ? ci._obj6 = ci._new (k, ci._pKeys6, ci._factory6) : ci._obj6;
278
+ if (ci._key7 == null ) break ; if (identical (ci._key7, k)) return ci._obj7 == null ? ci._obj7 = ci._new (k, ci._pKeys7, ci._factory7) : ci._obj7;
279
+ if (ci._key8 == null ) break ; if (identical (ci._key8, k)) return ci._obj8 == null ? ci._obj8 = ci._new (k, ci._pKeys8, ci._factory8) : ci._obj8;
280
+ if (ci._key9 == null ) break ; if (identical (ci._key9, k)) return ci._obj9 == null ? ci._obj9 = ci._new (k, ci._pKeys9, ci._factory9) : ci._obj9;
264
281
} while (false );
265
282
// Future feature: Component Injectors fall-through only if directly called.
266
283
// if ((ci is ComponentDirectiveInjector) && !identical(ci, this)) break;
267
284
ci = ci._parent;
268
- depth -- ;
285
+ treeDepth -- ;
269
286
}
270
287
return appInjector.getByKey (k);
271
288
}
@@ -304,7 +321,12 @@ class DirectiveInjector implements DirectiveBinder {
304
321
}
305
322
}
306
323
307
- dynamic _new (List <Key > paramKeys, Function fn) {
324
+ dynamic _new (Key k, List <Key > paramKeys, Function fn) {
325
+ if (_constructionDepth > MAX_CONSTRUCTION_DEPTH ) {
326
+ _constructionDepth = NO_CONSTRUCTION ;
327
+ throw new DiCircularDependencyError (key);
328
+ }
329
+ bool isFirstConstruction = (_constructionDepth++ == NO_CONSTRUCTION );
308
330
var oldTag = _TAG_GET .makeCurrent ();
309
331
int size = paramKeys.length;
310
332
var obj;
@@ -353,6 +375,7 @@ class DirectiveInjector implements DirectiveBinder {
353
375
}
354
376
}
355
377
oldTag.makeCurrent ();
378
+ if (isFirstConstruction) _constructionDepth = NO_CONSTRUCTION ;
356
379
return obj;
357
380
}
358
381
@@ -451,3 +474,26 @@ class ComponentDirectiveInjector extends DirectiveInjector {
451
474
// For example, a local directive is visible from its component injector children.
452
475
num _getDepth (int visType) => super ._getDepth (visType) + 1 ;
453
476
}
477
+
478
+
479
+ // For efficiency we run through the maximum resolving depth and unwind
480
+ // instead of setting 'resolving' key per type.
481
+ class DiCircularDependencyError extends ResolvingError {
482
+ DiCircularDependencyError (key) : super (key);
483
+
484
+ // strips the cyclical part of the chain.
485
+ List <Key > get stripCycle {
486
+ List <Key > rkeys = keys.reversed.toList ();
487
+ for (num i = 0 ; i < rkeys.length; i++ ) {
488
+ // Skipping 'cycles' of length 1, which represent resolution
489
+ // switching injectors and not true cycles.
490
+ for (num j = i + 2 ; j < rkeys.length; j++ ) {
491
+ if (rkeys[i] == rkeys[j]) return rkeys.sublist (0 , j + 1 );
492
+ }
493
+ }
494
+ return rkeys;
495
+ }
496
+
497
+ String get resolveChain => stripCycle.join (' -> ' );
498
+ String toString () => "circular dependency (${resolveChain })" ;
499
+ }
0 commit comments