Skip to content

Commit

Permalink
fix(directive-injector): Fixes for Direcive Injector.
Browse files Browse the repository at this point in the history
Breaking change: Regular (application) injectors cannot construct
DirectiveInjectors (DI). Only compilers create DI as part of view
creation process.

Deprecation: directive injector parent is now private. New public
method on dependency injector - parentGet, which allows to get through the
usual chain but skipping itself.

Component Injectors now break the resolution chain (except when called
directly.)

TestBed does not need DI in its constructor.

Internal changes:
- Application Injector reference is passed through view creation and
passed into new Directive Injector (instead of using
parentInjector.appInjector, which is wrong when used with ng-view).
- Unwind recursion from the directive injector.
- Remove EventListener from View.
- Replace DefaultDirectiveInjector with DirectiveInjector (with parent = null).
- Component visibility handled outside the visibility enum.
- Removed Shadowless and ShadowDirectiveInjector subclasses.

Closes dart-archive#1111
  • Loading branch information
rkirov committed Aug 21, 2014
1 parent 2b0d9de commit 3607d62
Show file tree
Hide file tree
Showing 29 changed files with 339 additions and 233 deletions.
2 changes: 1 addition & 1 deletion lib/application.dart
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ abstract class Application {
DirectiveMap directiveMap = injector.getByKey(DIRECTIVE_MAP_KEY);
RootScope rootScope = injector.getByKey(ROOT_SCOPE_KEY);
ViewFactory viewFactory = compiler(rootElements, directiveMap);
viewFactory(rootScope, injector.get(DirectiveInjector), rootElements);
viewFactory(rootScope, null, rootElements);
} catch (e, s) {
exceptionHandler(e, s);
}
Expand Down
18 changes: 0 additions & 18 deletions lib/core_dom/common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,3 @@ class DirectiveRef {
}
}

/**
* Creates a child injector that allows loading new directives, formatters and
* services from the provided modules.
*/
Injector forceNewDirectivesAndFormatters(Injector injector, DirectiveInjector dirInjector,
List<Module> modules) {
modules.add(new Module()
..bind(Scope, toFactory: (Injector injector) {
var scope = injector.parent.getByKey(SCOPE_KEY);
return scope.createChild(new PrototypeMap(scope.context));
}, inject: [INJECTOR_KEY])
..bind(DirectiveMap)
..bind(FormatterMap)
..bind(DirectiveInjector,
toFactory: () => new DefaultDirectiveInjector.newAppInjector(dirInjector, injector)));

return new ModuleInjector(modules, injector);
}
184 changes: 83 additions & 101 deletions lib/core_dom/directive_injector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,11 @@ final CONTENT_PORT_KEY = new Key(ContentPort);
final TEMPLATE_LOADER_KEY = new Key(TemplateLoader);
final SHADOW_ROOT_KEY = new Key(ShadowRoot);

final num MAX_DEPTH = 1 << 30;

const int VISIBILITY_LOCAL = -1;
const int VISIBILITY_DIRECT_CHILD = -2;
const int VISIBILITY_CHILDREN = -3;
const int VISIBILITY_COMPONENT_OFFSET = VISIBILITY_CHILDREN;
const int VISIBILITY_COMPONENT_LOCAL = VISIBILITY_LOCAL + VISIBILITY_COMPONENT_OFFSET;
const int VISIBILITY_COMPONENT_DIRECT_CHILD = VISIBILITY_DIRECT_CHILD + VISIBILITY_COMPONENT_OFFSET;
const int VISIBILITY_COMPONENT_CHILDREN = VISIBILITY_CHILDREN + VISIBILITY_COMPONENT_OFFSET;

const int UNDEFINED_ID = 0;
const int INJECTOR_KEY_ID = 1;
Expand All @@ -52,6 +50,8 @@ const int CONTENT_PORT_KEY_ID = 16;
const int EVENT_HANDLER_KEY_ID = 17;
const int KEEP_ME_LAST = 18;

EventHandler eventHandler(DirectiveInjector di) => di._eventHandler;

class DirectiveInjector implements DirectiveBinder {
static bool _isInit = false;
static initUID() {
Expand Down Expand Up @@ -99,9 +99,9 @@ class DirectiveInjector implements DirectiveBinder {
, EVENT_HANDLER_KEY
, KEEP_ME_LAST
];

final DirectiveInjector _parent;
final Injector appInjector;
final Injector _appInjector;
final Node _node;
final NodeAttrs _nodeAttrs;
final Animate _animate;
Expand Down Expand Up @@ -134,21 +134,17 @@ class DirectiveInjector implements DirectiveBinder {
case VISIBILITY_LOCAL: return Visibility.LOCAL;
case VISIBILITY_DIRECT_CHILD: return Visibility.DIRECT_CHILD;
case VISIBILITY_CHILDREN: return Visibility.CHILDREN;
case VISIBILITY_COMPONENT_LOCAL: return Visibility.LOCAL;
case VISIBILITY_COMPONENT_DIRECT_CHILD: return Visibility.DIRECT_CHILD;
case VISIBILITY_COMPONENT_CHILDREN: return Visibility.CHILDREN;
default: return null;
}
}

static Binding _tempBinding = new Binding();

DirectiveInjector(DirectiveInjector parent, Injector appInjector, this._node, this._nodeAttrs,
DirectiveInjector(this._parent, appInjector, this._node, this._nodeAttrs,
this._eventHandler, this.scope, this._animate)
: appInjector = appInjector,
_parent = parent == null ? new DefaultDirectiveInjector(appInjector) : parent;
: _appInjector = appInjector;

DirectiveInjector._default(this._parent, this.appInjector)
DirectiveInjector._default(this._parent, this._appInjector)
: _node = null,
_nodeAttrs = null,
_eventHandler = null,
Expand Down Expand Up @@ -195,54 +191,72 @@ class DirectiveInjector implements DirectiveBinder {
else { throw 'Maximum number of directives per element reached.'; }
}

// Get a key from the directive injector chain. When it is exhausted, get from
// the current application injector chain.
Object get(Type type) => getByKey(new Key(type));
Object getFromParent(Type type) => _parent.get(type);

Object getByKey(Key key) {
var oldTag = _TAG_GET.makeCurrent();
try {
return _getByKey(key);
return _getByKey(key, _appInjector);
} on ResolvingError catch (e, s) {
e.appendKey(key);
rethrow;
} finally {
oldTag.makeCurrent();
}
}
Object getFromParentByKey(Key key) => _parent.getByKey(key);

Object _getByKey(Key key) {
Object getFromParentByKey(Key key) {
if (_parent == null) {
return _appInjector.getByKey(key);
} else {
return _parent._getByKey(key, _appInjector);
}
}

Object _getByKey(Key key, Injector appInjector) {
int uid = key.uid;
if (uid == null || uid == UNDEFINED_ID) return appInjector.getByKey(key);
bool isDirective = uid < 0;
return isDirective ? _getDirectiveByKey(key, uid, appInjector) : _getById(uid);
}

Object _getDirectiveByKey(Key k, int visType, Injector i) {
do {
if (_key0 == null) break; if (identical(_key0, k)) return _obj0 == null ? _obj0 = _new(_pKeys0, _factory0) : _obj0;
if (_key1 == null) break; if (identical(_key1, k)) return _obj1 == null ? _obj1 = _new(_pKeys1, _factory1) : _obj1;
if (_key2 == null) break; if (identical(_key2, k)) return _obj2 == null ? _obj2 = _new(_pKeys2, _factory2) : _obj2;
if (_key3 == null) break; if (identical(_key3, k)) return _obj3 == null ? _obj3 = _new(_pKeys3, _factory3) : _obj3;
if (_key4 == null) break; if (identical(_key4, k)) return _obj4 == null ? _obj4 = _new(_pKeys4, _factory4) : _obj4;
if (_key5 == null) break; if (identical(_key5, k)) return _obj5 == null ? _obj5 = _new(_pKeys5, _factory5) : _obj5;
if (_key6 == null) break; if (identical(_key6, k)) return _obj6 == null ? _obj6 = _new(_pKeys6, _factory6) : _obj6;
if (_key7 == null) break; if (identical(_key7, k)) return _obj7 == null ? _obj7 = _new(_pKeys7, _factory7) : _obj7;
if (_key8 == null) break; if (identical(_key8, k)) return _obj8 == null ? _obj8 = _new(_pKeys8, _factory8) : _obj8;
if (_key9 == null) break; if (identical(_key9, k)) return _obj9 == null ? _obj9 = _new(_pKeys9, _factory9) : _obj9;
} while (false);
switch (visType) {
case VISIBILITY_LOCAL: return appInjector.getByKey(k);
case VISIBILITY_DIRECT_CHILD: return _parent._getDirectiveByKey(k, VISIBILITY_LOCAL, i);
case VISIBILITY_CHILDREN: return _parent._getDirectiveByKey(k, VISIBILITY_CHILDREN, i);
// SHADOW
case VISIBILITY_COMPONENT_LOCAL: return _parent._getDirectiveByKey(k, VISIBILITY_LOCAL, i);
case VISIBILITY_COMPONENT_DIRECT_CHILD: return _parent._getDirectiveByKey(k, VISIBILITY_DIRECT_CHILD, i);
case VISIBILITY_COMPONENT_CHILDREN: return _parent._getDirectiveByKey(k, VISIBILITY_CHILDREN, i);
num _getDepth(int visType) {
switch(visType) {
case VISIBILITY_LOCAL: return 0;
case VISIBILITY_DIRECT_CHILD: return 1;
case VISIBILITY_CHILDREN: return MAX_DEPTH;
default: throw null;
}
}

_getDirectiveByKey(Key k, int visType, Injector appInjector) {
num depth = _getDepth(visType);
// ci stands for currentInjector, abbreviated for readability.
var ci = this;
while (ci != null && depth >= 0) {
do {
if (ci._key0 == null) break; if (identical(ci._key0, k)) return ci._obj0 == null ? ci._obj0 = ci._new(ci._pKeys0, ci._factory0) : ci._obj0;
if (ci._key1 == null) break; if (identical(ci._key1, k)) return ci._obj1 == null ? ci._obj1 = ci._new(ci._pKeys1, ci._factory1) : ci._obj1;
if (ci._key2 == null) break; if (identical(ci._key2, k)) return ci._obj2 == null ? ci._obj2 = ci._new(ci._pKeys2, ci._factory2) : ci._obj2;
if (ci._key3 == null) break; if (identical(ci._key3, k)) return ci._obj3 == null ? ci._obj3 = ci._new(ci._pKeys3, ci._factory3) : ci._obj3;
if (ci._key4 == null) break; if (identical(ci._key4, k)) return ci._obj4 == null ? ci._obj4 = ci._new(ci._pKeys4, ci._factory4) : ci._obj4;
if (ci._key5 == null) break; if (identical(ci._key5, k)) return ci._obj5 == null ? ci._obj5 = ci._new(ci._pKeys5, ci._factory5) : ci._obj5;
if (ci._key6 == null) break; if (identical(ci._key6, k)) return ci._obj6 == null ? ci._obj6 = ci._new(ci._pKeys6, ci._factory6) : ci._obj6;
if (ci._key7 == null) break; if (identical(ci._key7, k)) return ci._obj7 == null ? ci._obj7 = ci._new(ci._pKeys7, ci._factory7) : ci._obj7;
if (ci._key8 == null) break; if (identical(ci._key8, k)) return ci._obj8 == null ? ci._obj8 = ci._new(ci._pKeys8, ci._factory8) : ci._obj8;
if (ci._key9 == null) break; if (identical(ci._key9, k)) return ci._obj9 == null ? ci._obj9 = ci._new(ci._pKeys9, ci._factory9) : ci._obj9;
} while (false);
// Component Injectors fall-through only if directly called.
if ((ci is ComponentDirectiveInjector) && !identical(ci, this)) break;
ci = ci._parent;
depth--;
}
return appInjector.getByKey(k);
}

List get directives {
var directives = [];
if (_obj0 != null) directives.add(_obj0);
Expand All @@ -260,7 +274,7 @@ class DirectiveInjector implements DirectiveBinder {

Object _getById(int keyId) {
switch(keyId) {
case INJECTOR_KEY_ID: return appInjector;
case INJECTOR_KEY_ID: return _appInjector;
case DIRECTIVE_INJECTOR_KEY_ID: return this;
case NODE_KEY_ID: return _node;
case ELEMENT_KEY_ID: return _node;
Expand All @@ -270,7 +284,13 @@ class DirectiveInjector implements DirectiveBinder {
case ELEMENT_PROBE_KEY_ID: return elementProbe;
case NG_ELEMENT_KEY_ID: return ngElement;
case EVENT_HANDLER_KEY_ID: return _eventHandler;
case CONTENT_PORT_KEY_ID: return _parent._getById(keyId);
case CONTENT_PORT_KEY_ID:
var currentInjector = _parent;
while (currentInjector != null) {
if (currentInjector is ComponentDirectiveInjector) return currentInjector._contentPort;
currentInjector = currentInjector._parent;
}
return null;
default: new NoProviderError(_KEYS[keyId]);
}
}
Expand All @@ -279,29 +299,30 @@ class DirectiveInjector implements DirectiveBinder {
var oldTag = _TAG_GET.makeCurrent();
int size = paramKeys.length;
var obj;
var appInjector = this._appInjector;
if (size > 15) {
var params = new List(paramKeys.length);
for(var i = 0; i < paramKeys.length; i++) {
params[i] = _getByKey(paramKeys[i]);
params[i] = _getByKey(paramKeys[i], appInjector);
}
_TAG_INSTANTIATE.makeCurrent();
obj = Function.apply(fn, params);
} else {
var a01 = size >= 01 ? _getByKey(paramKeys[00]) : null;
var a02 = size >= 02 ? _getByKey(paramKeys[01]) : null;
var a03 = size >= 03 ? _getByKey(paramKeys[02]) : null;
var a04 = size >= 04 ? _getByKey(paramKeys[03]) : null;
var a05 = size >= 05 ? _getByKey(paramKeys[04]) : null;
var a06 = size >= 06 ? _getByKey(paramKeys[05]) : null;
var a07 = size >= 07 ? _getByKey(paramKeys[06]) : null;
var a08 = size >= 08 ? _getByKey(paramKeys[07]) : null;
var a09 = size >= 09 ? _getByKey(paramKeys[08]) : null;
var a10 = size >= 10 ? _getByKey(paramKeys[09]) : null;
var a11 = size >= 11 ? _getByKey(paramKeys[10]) : null;
var a12 = size >= 12 ? _getByKey(paramKeys[11]) : null;
var a13 = size >= 13 ? _getByKey(paramKeys[12]) : null;
var a14 = size >= 14 ? _getByKey(paramKeys[13]) : null;
var a15 = size >= 15 ? _getByKey(paramKeys[14]) : null;
var a01 = size >= 01 ? _getByKey(paramKeys[00], appInjector) : null;
var a02 = size >= 02 ? _getByKey(paramKeys[01], appInjector) : null;
var a03 = size >= 03 ? _getByKey(paramKeys[02], appInjector) : null;
var a04 = size >= 04 ? _getByKey(paramKeys[03], appInjector) : null;
var a05 = size >= 05 ? _getByKey(paramKeys[04], appInjector) : null;
var a06 = size >= 06 ? _getByKey(paramKeys[05], appInjector) : null;
var a07 = size >= 07 ? _getByKey(paramKeys[06], appInjector) : null;
var a08 = size >= 08 ? _getByKey(paramKeys[07], appInjector) : null;
var a09 = size >= 09 ? _getByKey(paramKeys[08], appInjector) : null;
var a10 = size >= 10 ? _getByKey(paramKeys[09], appInjector) : null;
var a11 = size >= 11 ? _getByKey(paramKeys[10], appInjector) : null;
var a12 = size >= 12 ? _getByKey(paramKeys[11], appInjector) : null;
var a13 = size >= 13 ? _getByKey(paramKeys[12], appInjector) : null;
var a14 = size >= 14 ? _getByKey(paramKeys[13], appInjector) : null;
var a15 = size >= 15 ? _getByKey(paramKeys[14], appInjector) : null;
_TAG_INSTANTIATE.makeCurrent();
switch(size) {
case 00: obj = fn(); break;
Expand Down Expand Up @@ -367,14 +388,15 @@ class TemplateDirectiveInjector extends DirectiveInjector {

}

abstract class ComponentDirectiveInjector extends DirectiveInjector {
class ComponentDirectiveInjector extends DirectiveInjector {

final TemplateLoader _templateLoader;
final ShadowRoot _shadowRoot;
final ContentPort _contentPort;

ComponentDirectiveInjector(DirectiveInjector parent, Injector appInjector,
EventHandler eventHandler, Scope scope,
this._templateLoader, this._shadowRoot)
this._templateLoader, this._shadowRoot, this._contentPort)
: super(parent, appInjector, parent._node, parent._nodeAttrs, eventHandler, scope,
parent._animate);

Expand All @@ -386,56 +408,16 @@ abstract class ComponentDirectiveInjector extends DirectiveInjector {
}
}

_getDirectiveByKey(Key k, int visType, Injector i) =>
super._getDirectiveByKey(k, visType + VISIBILITY_COMPONENT_OFFSET, i);
}

class ShadowlessComponentDirectiveInjector extends ComponentDirectiveInjector {
final ContentPort _contentPort;

ShadowlessComponentDirectiveInjector(DirectiveInjector parent, Injector appInjector,
EventHandler eventHandler, Scope scope,
templateLoader, shadowRoot, this._contentPort)
: super(parent, appInjector, eventHandler, scope, templateLoader, shadowRoot);

Object _getById(int keyId) {
switch(keyId) {
case CONTENT_PORT_KEY_ID: return _contentPort;
default: return super._getById(keyId);
}
}
}

class ShadowDomComponentDirectiveInjector extends ComponentDirectiveInjector {
ShadowDomComponentDirectiveInjector(DirectiveInjector parent, Injector appInjector,
Scope scope, templateLoader, shadowRoot)
: super(parent, appInjector, new ShadowRootEventHandler(shadowRoot,
parent.getByKey(EXPANDO_KEY),
parent.getByKey(EXCEPTION_HANDLER_KEY)),
scope, templateLoader, shadowRoot);

ElementProbe get elementProbe {
if (_elementProbe == null) {
ElementProbe parentProbe = _parent == null ? null : _parent.elementProbe;
_elementProbe = new ElementProbe(parentProbe, _shadowRoot, this, scope);
}
return _elementProbe;
}
}

@Injectable()
class DefaultDirectiveInjector extends DirectiveInjector {
DefaultDirectiveInjector(Injector appInjector): super._default(null, appInjector);
DefaultDirectiveInjector.newAppInjector(DirectiveInjector parent, Injector appInjector)
: super._default(parent, appInjector);

Object getByKey(Key key) => appInjector.getByKey(key);
_getDirectiveByKey(Key key, int visType, Injector i) =>
_parent == null ? i.getByKey(key) : _parent._getDirectiveByKey(key, visType, i);
_getById(int keyId) {
switch (keyId) {
case CONTENT_PORT_KEY_ID: return null;
default: throw new NoProviderError(DirectiveInjector._KEYS[keyId]);
}
}
// Add 1 to visibility to allow to skip over current component injector.
// For example, a local directive is visible from its component injector children.
num _getDepth(int visType) => super._getDepth(visType) + 1;
}

7 changes: 4 additions & 3 deletions lib/core_dom/directive_map.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ class DirectiveMap {
final DirectiveSelectorFactory _directiveSelectorFactory;
FormatterMap _formatters;
DirectiveSelector _selector;
Injector _injector;

DirectiveMap(Injector injector,
DirectiveMap(Injector this._injector,
this._formatters,
MetadataExtractor metadataExtractor,
this._directiveSelectorFactory) {
(injector as ModuleInjector).types.forEach((type) {
(_injector as ModuleInjector).types.forEach((type) {
metadataExtractor(type)
.where((annotation) => annotation is Directive)
.forEach((Directive dir) {
Expand All @@ -29,7 +30,7 @@ class DirectiveMap {

DirectiveSelector get selector {
if (_selector != null) return _selector;
return _selector = _directiveSelectorFactory.selector(this, _formatters);
return _selector = _directiveSelectorFactory.selector(this, _injector, _formatters);
}

List<DirectiveTypeTuple> operator[](String key) {
Expand Down
Loading

0 comments on commit 3607d62

Please sign in to comment.