Skip to content

Commit 79d270c

Browse files
committed
feat(ElementInjector): add support for "special" objects
1 parent e3548b4 commit 79d270c

File tree

5 files changed

+114
-38
lines changed

5 files changed

+114
-38
lines changed

modules/benchmarks/src/element_injector/instantiate_benchmark.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export function run () {
99
var bindings = [A, B, C];
1010
var proto = new ProtoElementInjector(null, bindings, []);
1111
for (var i = 0; i < 20000; ++i) {
12-
var ei = proto.instantiate();
12+
var ei = proto.instantiate({view:null});
1313
ei.instantiateDirectives(appInjector);
1414
}
1515
}

modules/benchmarks/src/element_injector/instantiate_directive_benchmark.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export function run () {
88

99
var bindings = [A, B, C];
1010
var proto = new ProtoElementInjector(null, bindings, []);
11-
var ei = proto.instantiate();
11+
var ei = proto.instantiate({view:null});
1212

1313
for (var i = 0; i < 20000; ++i) {
1414
ei.clearDirectives();

modules/core/src/compiler/element_injector.js

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ import {Math} from 'facade/math';
33
import {List, ListWrapper} from 'facade/collection';
44
import {Injector, Key, Dependency, bind, Binding, NoProviderError, ProviderError} from 'di/di';
55
import {Parent, Ancestor} from 'core/annotations/visibility';
6+
import {StaticKeys} from './static_keys';
67

78
var MAX_DEPTH = Math.pow(2, 30) - 1;
9+
var _undefined = new Object();
810

911
class TreeNode {
1012
@FIELD('_parent:TreeNode')
@@ -180,12 +182,13 @@ export class ProtoElementInjector extends TreeNode {
180182
this.hasProperties = false;
181183
}
182184

183-
instantiate():ElementInjector {
185+
instantiate({view}):ElementInjector {
184186
var p = this._parent;
185-
var parentElementInjector = p == null ? null : p._elementInjector;
187+
var parentElementInjector = p === null ? null : p._elementInjector;
186188
this._elementInjector = new ElementInjector({
187189
proto: this,
188-
parent: parentElementInjector
190+
parent: parentElementInjector,
191+
view: view
189192
});
190193
return this._elementInjector;
191194
}
@@ -261,9 +264,11 @@ export class ElementInjector extends TreeNode {
261264
@FIELD('_obj7:Object')
262265
@FIELD('_obj8:Object')
263266
@FIELD('_obj9:Object')
264-
constructor({proto, parent}) {
267+
@FIELD('_view:View')
268+
constructor({proto, parent, view}) {
265269
super(parent);
266270
this._proto = proto;
271+
this._view = view;
267272

268273
//we cannot call clearDirectives because fields won't be detected
269274
this._appInjector = null;
@@ -358,20 +363,46 @@ export class ElementInjector extends TreeNode {
358363
return this._getByKey(dep.key, dep.depth);
359364
}
360365

366+
367+
/*
368+
* It is fairly easy to annotate keys with metadata.
369+
* For example, key.metadata = 'directive'.
370+
*
371+
* This would allows to do the lookup more efficiently.
372+
*
373+
* for example
374+
* we would lookup special objects only when metadata = 'special'
375+
* we would lookup directives only when metadata = 'directive'
376+
*
377+
* Write benchmarks before doing this optimization.
378+
*/
361379
_getByKey(key:Key, depth:int) {
362380
var ei = this;
363381
while (ei != null && depth >= 0) {
364-
var obj = ei._getDirectiveByKey(key);
365-
if (isPresent(obj)) return obj;
382+
var specObj = ei._getSpecialObjectByKey(key);
383+
if (specObj !== _undefined) return specObj;
384+
385+
var dir = ei._getDirectiveByKey(key);
386+
if (dir !== _undefined) return dir;
387+
366388
ei = ei._parent;
367389
depth -= 1;
368390
}
369391
return this._appInjector.get(key);
370392
}
371393

394+
_getSpecialObjectByKey(key:Key) {
395+
var staticKeys = StaticKeys.instance();
396+
var keyId = key.id;
397+
398+
if (keyId === staticKeys.viewId) return this._view;
399+
//TODO add other objects as needed
400+
return _undefined;
401+
}
402+
372403
_getDirectiveByKey(key:Key) {
373404
var p = this._proto;
374-
var keyId= key.id;
405+
var keyId = key.id;
375406
if (p._keyId0 === keyId) return this._obj0;
376407
if (p._keyId1 === keyId) return this._obj1;
377408
if (p._keyId2 === keyId) return this._obj2;
@@ -382,7 +413,7 @@ export class ElementInjector extends TreeNode {
382413
if (p._keyId7 === keyId) return this._obj7;
383414
if (p._keyId8 === keyId) return this._obj8;
384415
if (p._keyId9 === keyId) return this._obj9;
385-
return null;
416+
return _undefined;
386417
}
387418
}
388419

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import {View} from 'core/compiler/view';
2+
import {Key} from 'di/di';
3+
import {isBlank} from 'facade/lang';
4+
5+
var _staticKeys;
6+
7+
export class StaticKeys {
8+
constructor() {
9+
//TODO: vsavkin Key.annotate(Key.get(View), 'static')
10+
this.viewId = Key.get(View).id;
11+
}
12+
13+
static instance() {
14+
if (isBlank(_staticKeys)) _staticKeys = new StaticKeys();
15+
return _staticKeys;
16+
}
17+
}

modules/core/test/compiler/element_injector_spec.js

Lines changed: 56 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import {describe, ddescribe, it, iit, xit, xdescribe, expect, beforeEach} from 'test_lib/test_lib';
2-
import {isBlank, FIELD} from 'facade/lang';
2+
import {isBlank, FIELD, IMPLEMENTS} from 'facade/lang';
33
import {ListWrapper, MapWrapper, List} from 'facade/collection';
4-
import {ProtoElementInjector} from 'core/compiler/element_injector';
4+
import {ProtoElementInjector, VIEW_KEY} from 'core/compiler/element_injector';
55
import {Parent, Ancestor} from 'core/annotations/visibility';
66
import {Injector, Inject, bind} from 'di/di';
7+
import {View} from 'core/compiler/view';
8+
9+
@IMPLEMENTS(View)
10+
class DummyView {}
711

812
class Directive {
913
}
@@ -36,6 +40,13 @@ class NeedsService {
3640
}
3741
}
3842

43+
class NeedsView {
44+
@FIELD("view:Object")
45+
constructor(@Inject(View) view) {
46+
this.view = view;
47+
}
48+
}
49+
3950
export function main() {
4051
function humanize(tree, names:List) {
4152
var lookupName = (item) =>
@@ -47,6 +58,30 @@ export function main() {
4758
return [lookupName(tree), children];
4859
}
4960

61+
function injector(bindings, appInjector = null, props = null) {
62+
if (isBlank(appInjector)) appInjector = new Injector([]);
63+
if (isBlank(props)) props = {};
64+
65+
var proto = new ProtoElementInjector(null, bindings, []);
66+
var inj = proto.instantiate({view: props["view"]});
67+
inj.instantiateDirectives(appInjector);
68+
return inj;
69+
}
70+
71+
function parentChildInjectors(parentBindings, childBindings) {
72+
var inj = new Injector([]);
73+
74+
var protoParent = new ProtoElementInjector(null, parentBindings, []);
75+
var parent = protoParent.instantiate({view: null});
76+
parent.instantiateDirectives(inj);
77+
78+
var protoChild = new ProtoElementInjector(protoParent, childBindings, []);
79+
var child = protoChild.instantiate({view: null});
80+
child.instantiateDirectives(inj);
81+
82+
return child;
83+
}
84+
5085
describe("ElementInjector", function () {
5186
describe("proto injectors", function () {
5287
it("should construct a proto tree", function () {
@@ -68,9 +103,9 @@ export function main() {
68103
var protoChild1 = new ProtoElementInjector(protoParent, [], []);
69104
var protoChild2 = new ProtoElementInjector(protoParent, [], []);
70105

71-
var p = protoParent.instantiate();
72-
var c1 = protoChild1.instantiate();
73-
var c2 = protoChild2.instantiate();
106+
var p = protoParent.instantiate({view: null});
107+
var c1 = protoChild1.instantiate({view: null});
108+
var c2 = protoChild2.instantiate({view: null});
74109

75110
expect(humanize(p, [
76111
[p, 'parent'],
@@ -93,29 +128,6 @@ export function main() {
93128
});
94129

95130
describe("instantiateDirectives", function () {
96-
function injector(bindings, appInjector = null) {
97-
var proto = new ProtoElementInjector(null, bindings, []);
98-
var inj = proto.instantiate();
99-
100-
if (isBlank(appInjector)) appInjector = new Injector([]);
101-
inj.instantiateDirectives(appInjector);
102-
return inj;
103-
}
104-
105-
function parentChildInjectors(parentBindings, childBindings) {
106-
var inj = new Injector([]);
107-
108-
var protoParent = new ProtoElementInjector(null, parentBindings, []);
109-
var parent = protoParent.instantiate();
110-
parent.instantiateDirectives(inj);
111-
112-
var protoChild = new ProtoElementInjector(protoParent, childBindings, []);
113-
var child = protoChild.instantiate();
114-
child.instantiateDirectives(inj);
115-
116-
return child;
117-
}
118-
119131
it("should instantiate directives that have no dependencies", function () {
120132
var inj = injector([Directive]);
121133
expect(inj.get(Directive)).toBeAnInstanceOf(Directive);
@@ -141,6 +153,13 @@ export function main() {
141153
expect(d.service).toEqual("service");
142154
});
143155

156+
it("should instantiate directives that depend on speical objects", function () {
157+
var view = new DummyView();
158+
var inj = injector([NeedsView], null, {"view" : view});
159+
160+
expect(inj.get(NeedsView).view).toBe(view);
161+
});
162+
144163
it("should return app services", function () {
145164
var appInjector = new Injector([
146165
bind("service").toValue("service")
@@ -173,5 +192,14 @@ export function main() {
173192
toThrowError('No provider for Directive! (NeedDirectiveFromParent -> Directive)');
174193
});
175194
});
195+
196+
describe("special objects", function () {
197+
it("should return view", function () {
198+
var view = new DummyView();
199+
var inj = injector([], null, {"view" : view});
200+
201+
expect(inj.get(View)).toEqual(view);
202+
});
203+
});
176204
});
177205
}

0 commit comments

Comments
 (0)