Skip to content
This repository has been archived by the owner on Feb 22, 2018. It is now read-only.

Commit

Permalink
fix(directive-injector): breaking changes and fixes
Browse files Browse the repository at this point in the history
BREAKING CHANGE:

Regular injectors (aka application injectors) can no longer be used to
retrieve DirectiveInjectors.  The compiler creates the Directive
Injector as part of view creation process.

DEPRECATION:
- DirectiveInjector's parent accessor is now private.  (The typically
  lookup chain as used by the Application Injectors doesn't work for
  Directive Injectors and it's probably wrong for application code to
  access the parent but see the next point.)
- There's a new public method on the DirectiveInjector - `parentGet`.
  With parentGet, you can request types from the parent injector.  Since
  DirectiveInjector.parent is private, you would rewrite
  `injector.parent.get(...)` → `injector.parentGet(...)`.

TestBed does not need DI in its constructor.

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

Prepares for a future change in which ComponentInjectors break the
resolution chain unless called directly.

Closes #1111
  • Loading branch information
rkirov authored and chirayuk committed Aug 22, 2014
1 parent 665e121 commit 600113a
Show file tree
Hide file tree
Showing 29 changed files with 341 additions and 235 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
19 changes: 0 additions & 19 deletions lib/core_dom/common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,22 +44,3 @@ class DirectiveRef {
'type: $type }';
}
}

/**
* 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);
}
186 changes: 84 additions & 102 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 getFromParent(Type type) => getFromParentByKey(new Key(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);
// Future feature: 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 600113a

Please sign in to comment.