From fc6e421e7e71d737c8cd184c0f966d894bb9ee32 Mon Sep 17 00:00:00 2001 From: vsavkin Date: Mon, 2 Feb 2015 17:31:12 -0800 Subject: [PATCH] feat(compiler): add BindingPropagationConfig to the list of pre-built objects --- modules/change_detection/change_detection.js | 3 +- .../src/binding_propagation_config.js | 88 ------------------- .../compiler/binding_propagation_config.js | 25 ++++++ modules/core/src/compiler/element_injector.js | 19 ++-- modules/core/src/compiler/view.js | 7 +- .../test/compiler/element_injector_spec.js | 20 +++-- 6 files changed, 60 insertions(+), 102 deletions(-) delete mode 100644 modules/change_detection/src/binding_propagation_config.js create mode 100644 modules/core/src/compiler/binding_propagation_config.js diff --git a/modules/change_detection/change_detection.js b/modules/change_detection/change_detection.js index 2f5a8e8084..7c9b32afa9 100644 --- a/modules/change_detection/change_detection.js +++ b/modules/change_detection/change_detection.js @@ -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'; diff --git a/modules/change_detection/src/binding_propagation_config.js b/modules/change_detection/src/binding_propagation_config.js deleted file mode 100644 index 2b2f087e37..0000000000 --- a/modules/change_detection/src/binding_propagation_config.js +++ /dev/null @@ -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):List { - 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){ - 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; -} \ No newline at end of file diff --git a/modules/core/src/compiler/binding_propagation_config.js b/modules/core/src/compiler/binding_propagation_config.js new file mode 100644 index 0000000000..0d23249614 --- /dev/null +++ b/modules/core/src/compiler/binding_propagation_config.js @@ -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; + } +} \ No newline at end of file diff --git a/modules/core/src/compiler/element_injector.js b/modules/core/src/compiler/element_injector.js index 0c2b458d63..58f10372c6 100644 --- a/modules/core/src/compiler/element_injector.js +++ b/modules/core/src/compiler/element_injector.js @@ -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; diff --git a/modules/core/src/compiler/view.js b/modules/core/src/compiler/view.js index e2d41a609e..2935718247 100644 --- a/modules/core/src/compiler/view.js +++ b/modules/core/src/compiler/view.js @@ -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 diff --git a/modules/core/test/compiler/element_injector_spec.js b/modules/core/test/compiler/element_injector_spec.js index 1ddf6a3f22..b4f051f7a8 100644 --- a/modules/core/test/compiler/element_injector_spec.js +++ b/modules/core/test/compiler/element_injector_spec.js @@ -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 () {