feat(ElementInjector): add support for "special" objects
This commit is contained in:
parent
e3548b497f
commit
79d270c3dd
@ -9,7 +9,7 @@ export function run () {
|
|||||||
var bindings = [A, B, C];
|
var bindings = [A, B, C];
|
||||||
var proto = new ProtoElementInjector(null, bindings, []);
|
var proto = new ProtoElementInjector(null, bindings, []);
|
||||||
for (var i = 0; i < 20000; ++i) {
|
for (var i = 0; i < 20000; ++i) {
|
||||||
var ei = proto.instantiate();
|
var ei = proto.instantiate({view:null});
|
||||||
ei.instantiateDirectives(appInjector);
|
ei.instantiateDirectives(appInjector);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ export function run () {
|
|||||||
|
|
||||||
var bindings = [A, B, C];
|
var bindings = [A, B, C];
|
||||||
var proto = new ProtoElementInjector(null, bindings, []);
|
var proto = new ProtoElementInjector(null, bindings, []);
|
||||||
var ei = proto.instantiate();
|
var ei = proto.instantiate({view:null});
|
||||||
|
|
||||||
for (var i = 0; i < 20000; ++i) {
|
for (var i = 0; i < 20000; ++i) {
|
||||||
ei.clearDirectives();
|
ei.clearDirectives();
|
||||||
|
@ -3,8 +3,10 @@ import {Math} from 'facade/math';
|
|||||||
import {List, ListWrapper} from 'facade/collection';
|
import {List, ListWrapper} from 'facade/collection';
|
||||||
import {Injector, Key, Dependency, bind, Binding, NoProviderError, ProviderError} from 'di/di';
|
import {Injector, Key, Dependency, bind, Binding, NoProviderError, ProviderError} from 'di/di';
|
||||||
import {Parent, Ancestor} from 'core/annotations/visibility';
|
import {Parent, Ancestor} from 'core/annotations/visibility';
|
||||||
|
import {StaticKeys} from './static_keys';
|
||||||
|
|
||||||
var MAX_DEPTH = Math.pow(2, 30) - 1;
|
var MAX_DEPTH = Math.pow(2, 30) - 1;
|
||||||
|
var _undefined = new Object();
|
||||||
|
|
||||||
class TreeNode {
|
class TreeNode {
|
||||||
@FIELD('_parent:TreeNode')
|
@FIELD('_parent:TreeNode')
|
||||||
@ -180,12 +182,13 @@ export class ProtoElementInjector extends TreeNode {
|
|||||||
this.hasProperties = false;
|
this.hasProperties = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
instantiate():ElementInjector {
|
instantiate({view}):ElementInjector {
|
||||||
var p = this._parent;
|
var p = this._parent;
|
||||||
var parentElementInjector = p == null ? null : p._elementInjector;
|
var parentElementInjector = p === null ? null : p._elementInjector;
|
||||||
this._elementInjector = new ElementInjector({
|
this._elementInjector = new ElementInjector({
|
||||||
proto: this,
|
proto: this,
|
||||||
parent: parentElementInjector
|
parent: parentElementInjector,
|
||||||
|
view: view
|
||||||
});
|
});
|
||||||
return this._elementInjector;
|
return this._elementInjector;
|
||||||
}
|
}
|
||||||
@ -261,9 +264,11 @@ export class ElementInjector extends TreeNode {
|
|||||||
@FIELD('_obj7:Object')
|
@FIELD('_obj7:Object')
|
||||||
@FIELD('_obj8:Object')
|
@FIELD('_obj8:Object')
|
||||||
@FIELD('_obj9:Object')
|
@FIELD('_obj9:Object')
|
||||||
constructor({proto, parent}) {
|
@FIELD('_view:View')
|
||||||
|
constructor({proto, parent, view}) {
|
||||||
super(parent);
|
super(parent);
|
||||||
this._proto = proto;
|
this._proto = proto;
|
||||||
|
this._view = view;
|
||||||
|
|
||||||
//we cannot call clearDirectives because fields won't be detected
|
//we cannot call clearDirectives because fields won't be detected
|
||||||
this._appInjector = null;
|
this._appInjector = null;
|
||||||
@ -358,20 +363,46 @@ export class ElementInjector extends TreeNode {
|
|||||||
return this._getByKey(dep.key, dep.depth);
|
return this._getByKey(dep.key, dep.depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* It is fairly easy to annotate keys with metadata.
|
||||||
|
* For example, key.metadata = 'directive'.
|
||||||
|
*
|
||||||
|
* This would allows to do the lookup more efficiently.
|
||||||
|
*
|
||||||
|
* for example
|
||||||
|
* we would lookup special objects only when metadata = 'special'
|
||||||
|
* we would lookup directives only when metadata = 'directive'
|
||||||
|
*
|
||||||
|
* Write benchmarks before doing this optimization.
|
||||||
|
*/
|
||||||
_getByKey(key:Key, depth:int) {
|
_getByKey(key:Key, depth:int) {
|
||||||
var ei = this;
|
var ei = this;
|
||||||
while (ei != null && depth >= 0) {
|
while (ei != null && depth >= 0) {
|
||||||
var obj = ei._getDirectiveByKey(key);
|
var specObj = ei._getSpecialObjectByKey(key);
|
||||||
if (isPresent(obj)) return obj;
|
if (specObj !== _undefined) return specObj;
|
||||||
|
|
||||||
|
var dir = ei._getDirectiveByKey(key);
|
||||||
|
if (dir !== _undefined) return dir;
|
||||||
|
|
||||||
ei = ei._parent;
|
ei = ei._parent;
|
||||||
depth -= 1;
|
depth -= 1;
|
||||||
}
|
}
|
||||||
return this._appInjector.get(key);
|
return this._appInjector.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_getSpecialObjectByKey(key:Key) {
|
||||||
|
var staticKeys = StaticKeys.instance();
|
||||||
|
var keyId = key.id;
|
||||||
|
|
||||||
|
if (keyId === staticKeys.viewId) return this._view;
|
||||||
|
//TODO add other objects as needed
|
||||||
|
return _undefined;
|
||||||
|
}
|
||||||
|
|
||||||
_getDirectiveByKey(key:Key) {
|
_getDirectiveByKey(key:Key) {
|
||||||
var p = this._proto;
|
var p = this._proto;
|
||||||
var keyId= key.id;
|
var keyId = key.id;
|
||||||
if (p._keyId0 === keyId) return this._obj0;
|
if (p._keyId0 === keyId) return this._obj0;
|
||||||
if (p._keyId1 === keyId) return this._obj1;
|
if (p._keyId1 === keyId) return this._obj1;
|
||||||
if (p._keyId2 === keyId) return this._obj2;
|
if (p._keyId2 === keyId) return this._obj2;
|
||||||
@ -382,7 +413,7 @@ export class ElementInjector extends TreeNode {
|
|||||||
if (p._keyId7 === keyId) return this._obj7;
|
if (p._keyId7 === keyId) return this._obj7;
|
||||||
if (p._keyId8 === keyId) return this._obj8;
|
if (p._keyId8 === keyId) return this._obj8;
|
||||||
if (p._keyId9 === keyId) return this._obj9;
|
if (p._keyId9 === keyId) return this._obj9;
|
||||||
return null;
|
return _undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
17
modules/core/src/compiler/static_keys.js
Normal file
17
modules/core/src/compiler/static_keys.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import {View} from 'core/compiler/view';
|
||||||
|
import {Key} from 'di/di';
|
||||||
|
import {isBlank} from 'facade/lang';
|
||||||
|
|
||||||
|
var _staticKeys;
|
||||||
|
|
||||||
|
export class StaticKeys {
|
||||||
|
constructor() {
|
||||||
|
//TODO: vsavkin Key.annotate(Key.get(View), 'static')
|
||||||
|
this.viewId = Key.get(View).id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static instance() {
|
||||||
|
if (isBlank(_staticKeys)) _staticKeys = new StaticKeys();
|
||||||
|
return _staticKeys;
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,13 @@
|
|||||||
import {describe, ddescribe, it, iit, xit, xdescribe, expect, beforeEach} from 'test_lib/test_lib';
|
import {describe, ddescribe, it, iit, xit, xdescribe, expect, beforeEach} from 'test_lib/test_lib';
|
||||||
import {isBlank, FIELD} from 'facade/lang';
|
import {isBlank, FIELD, IMPLEMENTS} from 'facade/lang';
|
||||||
import {ListWrapper, MapWrapper, List} from 'facade/collection';
|
import {ListWrapper, MapWrapper, List} from 'facade/collection';
|
||||||
import {ProtoElementInjector} from 'core/compiler/element_injector';
|
import {ProtoElementInjector, VIEW_KEY} from 'core/compiler/element_injector';
|
||||||
import {Parent, Ancestor} from 'core/annotations/visibility';
|
import {Parent, Ancestor} from 'core/annotations/visibility';
|
||||||
import {Injector, Inject, bind} from 'di/di';
|
import {Injector, Inject, bind} from 'di/di';
|
||||||
|
import {View} from 'core/compiler/view';
|
||||||
|
|
||||||
|
@IMPLEMENTS(View)
|
||||||
|
class DummyView {}
|
||||||
|
|
||||||
class Directive {
|
class Directive {
|
||||||
}
|
}
|
||||||
@ -36,6 +40,13 @@ class NeedsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class NeedsView {
|
||||||
|
@FIELD("view:Object")
|
||||||
|
constructor(@Inject(View) view) {
|
||||||
|
this.view = view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
function humanize(tree, names:List) {
|
function humanize(tree, names:List) {
|
||||||
var lookupName = (item) =>
|
var lookupName = (item) =>
|
||||||
@ -47,6 +58,30 @@ export function main() {
|
|||||||
return [lookupName(tree), children];
|
return [lookupName(tree), children];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function injector(bindings, appInjector = null, props = null) {
|
||||||
|
if (isBlank(appInjector)) appInjector = new Injector([]);
|
||||||
|
if (isBlank(props)) props = {};
|
||||||
|
|
||||||
|
var proto = new ProtoElementInjector(null, bindings, []);
|
||||||
|
var inj = proto.instantiate({view: props["view"]});
|
||||||
|
inj.instantiateDirectives(appInjector);
|
||||||
|
return inj;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parentChildInjectors(parentBindings, childBindings) {
|
||||||
|
var inj = new Injector([]);
|
||||||
|
|
||||||
|
var protoParent = new ProtoElementInjector(null, parentBindings, []);
|
||||||
|
var parent = protoParent.instantiate({view: null});
|
||||||
|
parent.instantiateDirectives(inj);
|
||||||
|
|
||||||
|
var protoChild = new ProtoElementInjector(protoParent, childBindings, []);
|
||||||
|
var child = protoChild.instantiate({view: null});
|
||||||
|
child.instantiateDirectives(inj);
|
||||||
|
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
describe("ElementInjector", function () {
|
describe("ElementInjector", function () {
|
||||||
describe("proto injectors", function () {
|
describe("proto injectors", function () {
|
||||||
it("should construct a proto tree", function () {
|
it("should construct a proto tree", function () {
|
||||||
@ -68,9 +103,9 @@ export function main() {
|
|||||||
var protoChild1 = new ProtoElementInjector(protoParent, [], []);
|
var protoChild1 = new ProtoElementInjector(protoParent, [], []);
|
||||||
var protoChild2 = new ProtoElementInjector(protoParent, [], []);
|
var protoChild2 = new ProtoElementInjector(protoParent, [], []);
|
||||||
|
|
||||||
var p = protoParent.instantiate();
|
var p = protoParent.instantiate({view: null});
|
||||||
var c1 = protoChild1.instantiate();
|
var c1 = protoChild1.instantiate({view: null});
|
||||||
var c2 = protoChild2.instantiate();
|
var c2 = protoChild2.instantiate({view: null});
|
||||||
|
|
||||||
expect(humanize(p, [
|
expect(humanize(p, [
|
||||||
[p, 'parent'],
|
[p, 'parent'],
|
||||||
@ -93,29 +128,6 @@ export function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("instantiateDirectives", function () {
|
describe("instantiateDirectives", function () {
|
||||||
function injector(bindings, appInjector = null) {
|
|
||||||
var proto = new ProtoElementInjector(null, bindings, []);
|
|
||||||
var inj = proto.instantiate();
|
|
||||||
|
|
||||||
if (isBlank(appInjector)) appInjector = new Injector([]);
|
|
||||||
inj.instantiateDirectives(appInjector);
|
|
||||||
return inj;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parentChildInjectors(parentBindings, childBindings) {
|
|
||||||
var inj = new Injector([]);
|
|
||||||
|
|
||||||
var protoParent = new ProtoElementInjector(null, parentBindings, []);
|
|
||||||
var parent = protoParent.instantiate();
|
|
||||||
parent.instantiateDirectives(inj);
|
|
||||||
|
|
||||||
var protoChild = new ProtoElementInjector(protoParent, childBindings, []);
|
|
||||||
var child = protoChild.instantiate();
|
|
||||||
child.instantiateDirectives(inj);
|
|
||||||
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
|
|
||||||
it("should instantiate directives that have no dependencies", function () {
|
it("should instantiate directives that have no dependencies", function () {
|
||||||
var inj = injector([Directive]);
|
var inj = injector([Directive]);
|
||||||
expect(inj.get(Directive)).toBeAnInstanceOf(Directive);
|
expect(inj.get(Directive)).toBeAnInstanceOf(Directive);
|
||||||
@ -141,6 +153,13 @@ export function main() {
|
|||||||
expect(d.service).toEqual("service");
|
expect(d.service).toEqual("service");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should instantiate directives that depend on speical objects", function () {
|
||||||
|
var view = new DummyView();
|
||||||
|
var inj = injector([NeedsView], null, {"view" : view});
|
||||||
|
|
||||||
|
expect(inj.get(NeedsView).view).toBe(view);
|
||||||
|
});
|
||||||
|
|
||||||
it("should return app services", function () {
|
it("should return app services", function () {
|
||||||
var appInjector = new Injector([
|
var appInjector = new Injector([
|
||||||
bind("service").toValue("service")
|
bind("service").toValue("service")
|
||||||
@ -173,5 +192,14 @@ export function main() {
|
|||||||
toThrowError('No provider for Directive! (NeedDirectiveFromParent -> Directive)');
|
toThrowError('No provider for Directive! (NeedDirectiveFromParent -> Directive)');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("special objects", function () {
|
||||||
|
it("should return view", function () {
|
||||||
|
var view = new DummyView();
|
||||||
|
var inj = injector([], null, {"view" : view});
|
||||||
|
|
||||||
|
expect(inj.get(View)).toEqual(view);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user