feat(compiler): add BindingPropagationConfig to the list of pre-built objects

This commit is contained in:
vsavkin 2015-02-02 17:31:12 -08:00 committed by Alex Eagle
parent 23a080026a
commit fc6e421e7e
6 changed files with 60 additions and 102 deletions

View File

@ -4,7 +4,8 @@ export {Parser} from './src/parser/parser';
export {ContextWithVariableBindings} from './src/parser/context_with_variable_bindings';
export {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError} from './src/exceptions';
export {ChangeRecord, ChangeDispatcher, ChangeDetector} from './src/interfaces';
export {ChangeRecord, ChangeDispatcher, ChangeDetector,
CHECK_ONCE, CHECK_ALWAYS, DETACHED, CHECKED} from './src/interfaces';
export {ProtoChangeDetector, DynamicProtoChangeDetector, JitProtoChangeDetector} from './src/proto_change_detector';
export {DynamicChangeDetector} from './src/dynamic_change_detector';

View File

@ -1,88 +0,0 @@
import {isPresent} from 'facade/lang';
import {List, ListWrapper, Map, MapWrapper} from 'facade/collection';
import {RECORD_TYPE_SELF, ProtoRecord} from './proto_change_detector';
/**
* Removes "duplicate" records. It assuming that record evaluation does not
* have side-effects.
*
* Records that are not last in bindings are removed and all the indices
* of the records that depend on them are updated.
*
* Records that are last in bindings CANNOT be removed, and instead are
* replaced with very cheap SELF records.
*/
export function coalesce(records:List<ProtoRecord>):List<ProtoRecord> {
var res = ListWrapper.create();
var indexMap = MapWrapper.create();
for (var i = 0; i < records.length; ++i) {
var r = records[i];
var record = _replaceIndices(r, res.length + 1, indexMap);
var matchingRecord = _findMatching(record, res);
if (isPresent(matchingRecord) && record.lastInBinding) {
ListWrapper.push(res, _selfRecord(record, matchingRecord.selfIndex, res.length + 1));
MapWrapper.set(indexMap, r.selfIndex, matchingRecord.selfIndex);
} else if (isPresent(matchingRecord) && !record.lastInBinding) {
MapWrapper.set(indexMap, r.selfIndex, matchingRecord.selfIndex);
} else {
ListWrapper.push(res, record);
MapWrapper.set(indexMap, r.selfIndex, record.selfIndex);
}
}
return res;
}
function _selfRecord(r:ProtoRecord, contextIndex:number, selfIndex:number):ProtoRecord {
return new ProtoRecord(
RECORD_TYPE_SELF,
"self",
null,
[],
r.fixedArgs,
contextIndex,
selfIndex,
r.bindingMemento,
r.groupMemento,
r.expressionAsString,
r.lastInBinding,
r.lastInGroup
);
}
function _findMatching(r:ProtoRecord, rs:List<ProtoRecord>){
return ListWrapper.find(rs, (rr) =>
rr.mode === r.mode &&
rr.funcOrValue === r.funcOrValue &&
rr.contextIndex === r.contextIndex &&
ListWrapper.equals(rr.args, r.args)
);
}
function _replaceIndices(r:ProtoRecord, selfIndex:number, indexMap:Map) {
var args = ListWrapper.map(r.args, (a) => _map(indexMap, a));
var contextIndex = _map(indexMap, r.contextIndex);
return new ProtoRecord(
r.mode,
r.name,
r.funcOrValue,
args,
r.fixedArgs,
contextIndex,
selfIndex,
r.bindingMemento,
r.groupMemento,
r.expressionAsString,
r.lastInBinding,
r.lastInGroup
);
}
function _map(indexMap:Map, value:number) {
var r = MapWrapper.get(indexMap, value)
return isPresent(r) ? r : value;
}

View File

@ -0,0 +1,25 @@
import {ChangeDetector, CHECK_ONCE, DETACHED, CHECK_ALWAYS} from 'change_detection/change_detection';
export class BindingPropagationConfig {
_cd:ChangeDetector;
constructor(cd:ChangeDetector) {
this._cd = cd;
}
shouldBePropagated() {
this._cd.mode = CHECK_ONCE;
}
shouldBePropagatedFromRoot() {
this._cd.markAsCheckOnce();
}
shouldNotPropagate() {
this._cd.mode = DETACHED;
}
shouldAlwaysPropagate() {
this._cd.mode = CHECK_ALWAYS;
}
}

View File

@ -10,6 +10,7 @@ import {LightDom, SourceLightDom, DestinationLightDom} from 'core/src/compiler/s
import {ViewPort} from 'core/src/compiler/viewport';
import {NgElement} from 'core/src/dom/element';
import {Directive} from 'core/src/annotations/annotations'
import {BindingPropagationConfig} from 'core/src/compiler/binding_propagation_config'
var _MAX_DIRECTIVE_CONSTRUCTION_COUNTER = 10;
@ -20,11 +21,12 @@ var _undefined = new Object();
var _staticKeys;
class StaticKeys {
viewId:int;
ngElementId:int;
viewPortId:int;
destinationLightDomId:int;
sourceLightDomId:int;
viewId:number;
ngElementId:number;
viewPortId:number;
destinationLightDomId:number;
sourceLightDomId:number;
bindingPropagationConfigId:number;
constructor() {
//TODO: vsavkin Key.annotate(Key.get(View), 'static')
@ -33,6 +35,7 @@ class StaticKeys {
this.viewPortId = Key.get(ViewPort).id;
this.destinationLightDomId = Key.get(DestinationLightDom).id;
this.sourceLightDomId = Key.get(SourceLightDom).id;
this.bindingPropagationConfigId = Key.get(BindingPropagationConfig).id;
}
static instance() {
@ -151,11 +154,14 @@ export class PreBuiltObjects {
element:NgElement;
viewPort:ViewPort;
lightDom:LightDom;
constructor(view, element:NgElement, viewPort:ViewPort, lightDom:LightDom) {
bindingPropagationConfig:BindingPropagationConfig;
constructor(view, element:NgElement, viewPort:ViewPort, lightDom:LightDom,
bindingPropagationConfig:BindingPropagationConfig) {
this.view = view;
this.element = element;
this.viewPort = viewPort;
this.lightDom = lightDom;
this.bindingPropagationConfig = bindingPropagationConfig;
}
}
@ -532,6 +538,7 @@ export class ElementInjector extends TreeNode {
if (keyId === staticKeys.viewId) return this._preBuiltObjects.view;
if (keyId === staticKeys.ngElementId) return this._preBuiltObjects.element;
if (keyId === staticKeys.viewPortId) return this._preBuiltObjects.viewPort;
if (keyId === staticKeys.bindingPropagationConfigId) return this._preBuiltObjects.bindingPropagationConfig;
if (keyId === staticKeys.destinationLightDomId) {
var p:ElementInjector = this.directParent();
return isPresent(p) ? p._preBuiltObjects.lightDom : null;

View File

@ -4,6 +4,7 @@ import {AST, ContextWithVariableBindings, ChangeDispatcher, ProtoChangeDetector,
from 'change_detection/change_detection';
import {ProtoElementInjector, ElementInjector, PreBuiltObjects} from './element_injector';
import {BindingPropagationConfig} from './binding_propagation_config';
import {ElementBinder} from './element_binder';
import {DirectiveMetadata} from './directive_metadata';
import {SetterFn} from 'reflection/src/types';
@ -350,6 +351,7 @@ export class ProtoView {
// componentChildViews
var lightDom = null;
var bindingPropagationConfig = null;
if (isPresent(binder.componentDirective)) {
var childView = binder.nestedProtoView.instantiate(elementInjector);
view.changeDetector.addChild(childView.changeDetector);
@ -357,6 +359,8 @@ export class ProtoView {
lightDom = binder.componentDirective.shadowDomStrategy.constructLightDom(view, childView, element);
binder.componentDirective.shadowDomStrategy.attachTemplate(element, childView);
bindingPropagationConfig = new BindingPropagationConfig(view.changeDetector);
ListWrapper.push(componentChildViews, childView);
}
@ -370,7 +374,8 @@ export class ProtoView {
// preBuiltObjects
if (isPresent(elementInjector)) {
preBuiltObjects[i] = new PreBuiltObjects(view, new NgElement(element), viewPort, lightDom);
preBuiltObjects[i] = new PreBuiltObjects(view, new NgElement(element), viewPort,
lightDom, bindingPropagationConfig);
}
// events

View File

@ -12,6 +12,7 @@ import {ViewPort} from 'core/src/compiler/viewport';
import {NgElement} from 'core/src/dom/element';
import {LightDom, SourceLightDom, DestinationLightDom} from 'core/src/compiler/shadow_dom_emulation/light_dom';
import {Directive} from 'core/src/annotations/annotations';
import {BindingPropagationConfig} from 'core/src/compiler/binding_propagation_config';
@proxy
@IMPLEMENTS(View)
@ -95,7 +96,7 @@ class DirectiveWithDestroy {
}
export function main() {
var defaultPreBuiltObjects = new PreBuiltObjects(null, null, null, null);
var defaultPreBuiltObjects = new PreBuiltObjects(null, null, null, null, null);
function humanize(tree, names:List) {
var lookupName = (item) =>
@ -245,7 +246,7 @@ export function main() {
it("should instantiate directives that depend on pre built objects", function () {
var view = new DummyView();
var inj = injector([NeedsView], null, null, new PreBuiltObjects(view, null, null, null));
var inj = injector([NeedsView], null, null, new PreBuiltObjects(view, null, null, null, null));
expect(inj.get(NeedsView).view).toBe(view);
});
@ -365,31 +366,38 @@ export function main() {
describe("pre built objects", function () {
it("should return view", function () {
var view = new DummyView();
var inj = injector([], null, null, new PreBuiltObjects(view, null, null, null));
var inj = injector([], null, null, new PreBuiltObjects(view, null, null, null, null));
expect(inj.get(View)).toEqual(view);
});
it("should return element", function () {
var element = new NgElement(null);
var inj = injector([], null, null, new PreBuiltObjects(null, element, null, null));
var inj = injector([], null, null, new PreBuiltObjects(null, element, null, null, null));
expect(inj.get(NgElement)).toEqual(element);
});
it('should return viewPort', function () {
var viewPort = new ViewPort(null, null, null, null);
var inj = injector([], null, null, new PreBuiltObjects(null, null, viewPort, null));
var inj = injector([], null, null, new PreBuiltObjects(null, null, viewPort, null, null));
expect(inj.get(ViewPort)).toEqual(viewPort);
});
it('should return bindingPropagationConfig', function () {
var config = new BindingPropagationConfig(null);
var inj = injector([], null, null, new PreBuiltObjects(null, null, null, null, config));
expect(inj.get(BindingPropagationConfig)).toEqual(config);
});
describe("light DOM", () => {
var lightDom, parentPreBuiltObjects;
beforeEach(() => {
lightDom = new DummyLightDom();
parentPreBuiltObjects = new PreBuiltObjects(null, null, null, lightDom);
parentPreBuiltObjects = new PreBuiltObjects(null, null, null, lightDom, null);
});
it("should return destination light DOM from the parent's injector", function () {