diff --git a/modules/angular2/change_detection.js b/modules/angular2/change_detection.js index f1b8cebc0b..3da163a679 100644 --- a/modules/angular2/change_detection.js +++ b/modules/angular2/change_detection.js @@ -7,7 +7,7 @@ export {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError} from './src/change_detection/exceptions'; export {ChangeRecord, ChangeDispatcher, ChangeDetector, CHECK_ONCE, CHECK_ALWAYS, DETACHED, CHECKED} from './src/change_detection/interfaces'; -export {ProtoChangeDetector, DynamicProtoChangeDetector, JitProtoChangeDetector} +export {ProtoChangeDetector, DynamicProtoChangeDetector, JitProtoChangeDetector, BindingRecord} from './src/change_detection/proto_change_detector'; export {DynamicChangeDetector} from './src/change_detection/dynamic_change_detector'; diff --git a/modules/angular2/src/change_detection/proto_change_detector.js b/modules/angular2/src/change_detection/proto_change_detector.js index dee42b9f22..52e7522c46 100644 --- a/modules/angular2/src/change_detection/proto_change_detector.js +++ b/modules/angular2/src/change_detection/proto_change_detector.js @@ -45,36 +45,44 @@ import { export class ProtoChangeDetector { addAst(ast:AST, bindingMemento:any, directiveMemento:any = null){} - instantiate(dispatcher:any):ChangeDetector{ + instantiate(dispatcher:any, bindingRecords:List):ChangeDetector{ return null; } } +export class BindingRecord { + ast:AST; + bindingMemento:any; + directiveMemento:any; + + constructor(ast:AST, bindingMemento:any, directiveMemento:any) { + this.ast = ast; + this.bindingMemento = bindingMemento; + this.directiveMemento = directiveMemento; + } +} + export class DynamicProtoChangeDetector extends ProtoChangeDetector { - _records:List; - _recordBuilder:ProtoRecordBuilder; _pipeRegistry:PipeRegistry; + _records:List; constructor(pipeRegistry:PipeRegistry) { super(); this._pipeRegistry = pipeRegistry; - this._records = null; - this._recordBuilder = new ProtoRecordBuilder(); } - addAst(ast:AST, bindingMemento:any, directiveMemento:any = null) { - this._recordBuilder.addAst(ast, bindingMemento, directiveMemento); - } - - instantiate(dispatcher:any) { - this._createRecordsIfNecessary(); + instantiate(dispatcher:any, bindingRecords:List) { + this._createRecordsIfNecessary(bindingRecords); return new DynamicChangeDetector(dispatcher, this._pipeRegistry, this._records); } - _createRecordsIfNecessary() { + _createRecordsIfNecessary(bindingRecords:List) { if (isBlank(this._records)) { - var records = this._recordBuilder.records; - this._records = coalesce(records); + var recordBuilder = new ProtoRecordBuilder(); + ListWrapper.forEach(bindingRecords, (r) => { + recordBuilder.addAst(r.ast, r.bindingMemento, r.directiveMemento); + }); + this._records = coalesce(recordBuilder.records); } } } @@ -82,29 +90,27 @@ export class DynamicProtoChangeDetector extends ProtoChangeDetector { var _jitProtoChangeDetectorClassCounter:number = 0; export class JitProtoChangeDetector extends ProtoChangeDetector { _factory:Function; - _recordBuilder:ProtoRecordBuilder; _pipeRegistry; constructor(pipeRegistry) { super(); this._pipeRegistry = pipeRegistry; this._factory = null; - this._recordBuilder = new ProtoRecordBuilder(); } - addAst(ast:AST, bindingMemento:any, directiveMemento:any = null) { - this._recordBuilder.addAst(ast, bindingMemento, directiveMemento); - } - - instantiate(dispatcher:any) { - this._createFactoryIfNecessary(); + instantiate(dispatcher:any, bindingRecords:List) { + this._createFactoryIfNecessary(bindingRecords); return this._factory(dispatcher, this._pipeRegistry); } - _createFactoryIfNecessary() { + _createFactoryIfNecessary(bindingRecords:List) { if (isBlank(this._factory)) { + var recordBuilder = new ProtoRecordBuilder(); + ListWrapper.forEach(bindingRecords, (r) => { + recordBuilder.addAst(r.ast, r.bindingMemento, r.directiveMemento); + }); var c = _jitProtoChangeDetectorClassCounter++; - var records = coalesce(this._recordBuilder.records); + var records = coalesce(recordBuilder.records); var typeName = `ChangeDetector${c}`; this._factory = new ChangeDetectorJITGenerator(typeName, records).generate(); } diff --git a/modules/angular2/src/core/compiler/view.js b/modules/angular2/src/core/compiler/view.js index 6b70480442..b58ce9d84c 100644 --- a/modules/angular2/src/core/compiler/view.js +++ b/modules/angular2/src/core/compiler/view.js @@ -1,8 +1,8 @@ import {DOM} from 'angular2/src/dom/dom_adapter'; import {Promise} from 'angular2/src/facade/async'; import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection'; -import {AST, ContextWithVariableBindings, ChangeDispatcher, ProtoChangeDetector, ChangeDetector, ChangeRecord} - from 'angular2/change_detection'; +import {AST, ContextWithVariableBindings, ChangeDispatcher, ProtoChangeDetector, ChangeDetector, + ChangeRecord, BindingRecord} from 'angular2/change_detection'; import {ProtoElementInjector, ElementInjector, PreBuiltObjects} from './element_injector'; import {BindingPropagationConfig} from './binding_propagation_config'; @@ -46,10 +46,10 @@ export class View { context: any; contextWithLocals:ContextWithVariableBindings; - constructor(proto:ProtoView, nodes:List, protoChangeDetector:ProtoChangeDetector, protoContextLocals:Map) { + constructor(proto:ProtoView, nodes:List, protoContextLocals:Map) { this.proto = proto; this.nodes = nodes; - this.changeDetector = protoChangeDetector.instantiate(this); + this.changeDetector = null; this.elementInjectors = null; this.rootElementInjectors = null; this.textNodes = null; @@ -63,8 +63,9 @@ export class View { : null; } - init(elementInjectors:List, rootElementInjectors:List, textNodes: List, bindElements:List, + init(changeDetector:ChangeDetector, elementInjectors:List, rootElementInjectors:List, textNodes: List, bindElements:List, viewContainers:List, preBuiltObjects:List, componentChildViews:List) { + this.changeDetector = changeDetector; this.elementInjectors = elementInjectors; this.rootElementInjectors = rootElementInjectors; this.textNodes = textNodes; @@ -294,12 +295,13 @@ export class ProtoView { _viewPool: ViewPool; stylePromises: List; // List>, indexed by binder index - eventHandlers: List; + eventHandlers:List; + bindingRecords:List; constructor( template, protoChangeDetector:ProtoChangeDetector, - shadowDomStrategy: ShadowDomStrategy) { + shadowDomStrategy:ShadowDomStrategy) { this.element = template; this.elementBinders = []; this.variableBindings = MapWrapper.create(); @@ -315,6 +317,7 @@ export class ProtoView { this._viewPool = new ViewPool(VIEW_POOL_CAPACITY); this.stylePromises = []; this.eventHandlers = []; + this.bindingRecords = []; } // TODO(rado): hostElementInjector should be moved to hydrate phase. @@ -357,7 +360,9 @@ export class ProtoView { viewNodes = [rootElementClone]; } - var view = new View(this, viewNodes, this.protoChangeDetector, this.protoContextLocals); + var view = new View(this, viewNodes, this.protoContextLocals); + var changeDetector = this.protoChangeDetector.instantiate(view, this.bindingRecords); + var binders = this.elementBinders; var elementInjectors = ListWrapper.createFixedSize(binders.length); var eventHandlers = ListWrapper.createFixedSize(binders.length); @@ -413,12 +418,12 @@ export class ProtoView { if (isPresent(binder.componentDirective)) { var strategy = this.shadowDomStrategy; var childView = binder.nestedProtoView.instantiate(elementInjector, eventManager); - view.changeDetector.addChild(childView.changeDetector); + changeDetector.addChild(childView.changeDetector); lightDom = strategy.constructLightDom(view, childView, element); strategy.attachTemplate(element, childView); - bindingPropagationConfig = new BindingPropagationConfig(view.changeDetector); + bindingPropagationConfig = new BindingPropagationConfig(changeDetector); ListWrapper.push(componentChildViews, childView); } @@ -454,7 +459,7 @@ export class ProtoView { this.eventHandlers = eventHandlers; - view.init(elementInjectors, rootElementInjectors, textNodes, elementsWithPropertyBindings, + view.init(changeDetector, elementInjectors, rootElementInjectors, textNodes, elementsWithPropertyBindings, viewContainers, preBuiltObjects, componentChildViews); return view; @@ -519,7 +524,7 @@ export class ProtoView { } ListWrapper.push(elBinder.textNodeIndices, indexInParent); var memento = this.textNodesWithBindingCount++; - this.protoChangeDetector.addAst(expression, memento); + ListWrapper.push(this.bindingRecords, new BindingRecord(expression, memento, null)); } /** @@ -532,7 +537,7 @@ export class ProtoView { this.elementsWithBindingCount++; } var memento = new ElementBindingMemento(this.elementsWithBindingCount-1, setterName, setter); - this.protoChangeDetector.addAst(expression, memento); + ListWrapper.push(this.bindingRecords, new BindingRecord(expression, memento, null)); } /** @@ -579,7 +584,7 @@ export class ProtoView { setter ); var directiveMemento = DirectiveMemento.get(bindingMemento); - this.protoChangeDetector.addAst(expression, bindingMemento, directiveMemento); + ListWrapper.push(this.bindingRecords, new BindingRecord(expression, bindingMemento, directiveMemento)); } // Create a rootView as if the compiler encountered , diff --git a/modules/angular2/test/change_detection/change_detection_spec.js b/modules/angular2/test/change_detection/change_detection_spec.js index 124cd56664..491b222fd4 100644 --- a/modules/angular2/test/change_detection/change_detection_spec.js +++ b/modules/angular2/test/change_detection/change_detection_spec.js @@ -6,7 +6,7 @@ import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/faca import {Parser} from 'angular2/src/change_detection/parser/parser'; import {Lexer} from 'angular2/src/change_detection/parser/lexer'; -import {ChangeDispatcher, DynamicChangeDetector, ChangeDetectionError, ContextWithVariableBindings, +import {ChangeDispatcher, DynamicChangeDetector, ChangeDetectionError, ContextWithVariableBindings, BindingRecord, PipeRegistry, Pipe, NO_CHANGE, CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from 'angular2/change_detection'; import {ChangeDetectionUtil} from 'angular2/src/change_detection/change_detection_util'; @@ -30,9 +30,8 @@ export function main() { function createChangeDetector(memo:string, exp:string, context = null, registry = null) { var pcd = createProtoChangeDetector(registry); - pcd.addAst(ast(exp), memo, memo); var dispatcher = new TestDispatcher(); - var cd = pcd.instantiate(dispatcher); + var cd = pcd.instantiate(dispatcher, [new BindingRecord(ast(exp), memo, memo)]); cd.hydrate(context); return {"changeDetector" : cd, "dispatcher" : dispatcher}; @@ -179,10 +178,9 @@ export function main() { var parser = new Parser(new Lexer()); var pcd = createProtoChangeDetector(); var ast = parser.parseInterpolation("B{{a}}A", "location"); - pcd.addAst(ast, "memo", "memo"); var dispatcher = new TestDispatcher(); - var cd = pcd.instantiate(dispatcher); + var cd = pcd.instantiate(dispatcher, [new BindingRecord(ast, "memo", "memo")]); cd.hydrate(new TestData("value")); cd.detectChanges(); @@ -230,12 +228,13 @@ export function main() { describe("group changes", () => { it("should notify the dispatcher when a group of records changes", () => { var pcd = createProtoChangeDetector(); - pcd.addAst(ast("1 + 2"), "memo", "1"); - pcd.addAst(ast("10 + 20"), "memo", "1"); - pcd.addAst(ast("100 + 200"), "memo2", "2"); var dispatcher = new TestDispatcher(); - var cd = pcd.instantiate(dispatcher); + var cd = pcd.instantiate(dispatcher, [ + new BindingRecord(ast("1 + 2"), "memo", "1"), + new BindingRecord(ast("10 + 20"), "memo", "1"), + new BindingRecord(ast("100 + 200"), "memo", "2") + ]); cd.detectChanges(); @@ -244,12 +243,12 @@ export function main() { it("should notify the dispatcher before switching to the next group", () => { var pcd = createProtoChangeDetector(); - pcd.addAst(ast("a()"), "a", "1"); - pcd.addAst(ast("b()"), "b", "2"); - pcd.addAst(ast("c()"), "c", "2"); - var dispatcher = new TestDispatcher(); - var cd = pcd.instantiate(dispatcher); + var cd = pcd.instantiate(dispatcher, [ + new BindingRecord(ast("a()"), "a", "1"), + new BindingRecord(ast("b()"), "b", "2"), + new BindingRecord(ast("c()"), "c", "2") + ]); var tr = new TestRecord(); tr.a = () => { @@ -279,7 +278,9 @@ export function main() { pcd.addAst(ast("a"), "a", 1); var dispatcher = new TestDispatcher(); - var cd = pcd.instantiate(dispatcher); + var cd = pcd.instantiate(dispatcher, [ + new BindingRecord(ast("a"), "a", 1) + ]); cd.hydrate(new TestData('value')); expect(() => { @@ -292,9 +293,9 @@ export function main() { describe("error handling", () => { xit("should wrap exceptions into ChangeDetectionError", () => { var pcd = createProtoChangeDetector(); - pcd.addAst(ast('invalidProp', 'someComponent'), "a", 1); - - var cd = pcd.instantiate(new TestDispatcher()); + var cd = pcd.instantiate(new TestDispatcher(), [ + new BindingRecord(ast("invalidProp", "someComponent"), "a", 1) + ]); cd.hydrate(null); try { @@ -349,10 +350,10 @@ export function main() { beforeEach(() => { var protoParent = createProtoChangeDetector(); - parent = protoParent.instantiate(null); + parent = protoParent.instantiate(null, []); var protoChild = createProtoChangeDetector(); - child = protoChild.instantiate(null); + child = protoChild.instantiate(null, []); }); it("should add children", () => { @@ -395,7 +396,7 @@ export function main() { }); it("should change CHECK_ONCE to CHECKED", () => { - var cd = createProtoChangeDetector().instantiate(null); + var cd = createProtoChangeDetector().instantiate(null, []); cd.mode = CHECK_ONCE; cd.detectChanges(); @@ -404,7 +405,7 @@ export function main() { }); it("should not change the CHECK_ALWAYS", () => { - var cd = createProtoChangeDetector().instantiate(null); + var cd = createProtoChangeDetector().instantiate(null, []); cd.mode = CHECK_ALWAYS; cd.detectChanges(); @@ -415,7 +416,7 @@ export function main() { describe("markPathToRootAsCheckOnce", () => { function changeDetector(mode, parent) { - var cd = createProtoChangeDetector().instantiate(null); + var cd = createProtoChangeDetector().instantiate(null, []); cd.mode = mode; if (isPresent(parent)) parent.addChild(cd); return cd; diff --git a/modules/angular2/test/core/compiler/element_injector_spec.js b/modules/angular2/test/core/compiler/element_injector_spec.js index b425e68a11..2127b16629 100644 --- a/modules/angular2/test/core/compiler/element_injector_spec.js +++ b/modules/angular2/test/core/compiler/element_injector_spec.js @@ -12,7 +12,6 @@ import {NgElement} from 'angular2/src/core/dom/element'; import {LightDom, DestinationLightDom} from 'angular2/src/core/compiler/shadow_dom_emulation/light_dom'; import {Directive} from 'angular2/src/core/annotations/annotations'; import {BindingPropagationConfig} from 'angular2/src/core/compiler/binding_propagation_config'; -import {DynamicProtoChangeDetector} from 'angular2/change_detection'; @proxy @IMPLEMENTS(View) @@ -476,7 +475,7 @@ export function main() { StringMapWrapper.set(handlers, 'click', (e, view) => { called = true;}); var pv = new ProtoView(null, null, null); pv.eventHandlers = [handlers]; - var view = new View(pv, null, new DynamicProtoChangeDetector(null), MapWrapper.create()); + var view = new View(pv, null, MapWrapper.create()); var preBuildObject = new PreBuiltObjects(view, null, null, null, null); var inj = injector([NeedsEventEmitter], null, null, preBuildObject); inj.get(NeedsEventEmitter).click(); diff --git a/modules/angular2/test/core/compiler/view_container_spec.js b/modules/angular2/test/core/compiler/view_container_spec.js index 16462802ab..0a4d5879fc 100644 --- a/modules/angular2/test/core/compiler/view_container_spec.js +++ b/modules/angular2/test/core/compiler/view_container_spec.js @@ -10,8 +10,9 @@ import {NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_str import {DynamicProtoChangeDetector, ChangeDetector, Lexer, Parser} from 'angular2/change_detection'; function createView(nodes) { - var view = new View(null, nodes, new DynamicProtoChangeDetector(null), MapWrapper.create()); - view.init([], [], [], [], [], [], []); + var view = new View(null, nodes, MapWrapper.create()); + var cd = new DynamicProtoChangeDetector(null).instantiate(view, []); + view.init(cd, [], [], [], [], [], [], []); return view; } diff --git a/modules/benchmarks/src/change_detection/change_detection_benchmark.js b/modules/benchmarks/src/change_detection/change_detection_benchmark.js index ff1830425f..6c3984d7f0 100644 --- a/modules/benchmarks/src/change_detection/change_detection_benchmark.js +++ b/modules/benchmarks/src/change_detection/change_detection_benchmark.js @@ -10,7 +10,8 @@ import { ChangeDispatcher, ChangeDetection, dynamicChangeDetection, - jitChangeDetection + jitChangeDetection, + BindingRecord } from 'angular2/change_detection'; @@ -104,31 +105,28 @@ function setUpChangeDetection(changeDetection:ChangeDetection, iterations) { var parser = new Parser(new Lexer()); var parentProto = changeDetection.createProtoChangeDetector('parent'); - var parentCd = parentProto.instantiate(dispatcher); + var parentCd = parentProto.instantiate(dispatcher, []); var proto = changeDetection.createProtoChangeDetector("proto"); - var astWithSource = [ - parser.parseBinding('field0', null), - parser.parseBinding('field1', null), - parser.parseBinding('field2', null), - parser.parseBinding('field3', null), - parser.parseBinding('field4', null), - parser.parseBinding('field5', null), - parser.parseBinding('field6', null), - parser.parseBinding('field7', null), - parser.parseBinding('field8', null), - parser.parseBinding('field9', null) + var bindingRecords = [ + new BindingRecord(parser.parseBinding('field0', null), "memo", 0), + new BindingRecord(parser.parseBinding('field1', null), "memo", 1), + new BindingRecord(parser.parseBinding('field2', null), "memo", 2), + new BindingRecord(parser.parseBinding('field3', null), "memo", 3), + new BindingRecord(parser.parseBinding('field4', null), "memo", 4), + new BindingRecord(parser.parseBinding('field5', null), "memo", 5), + new BindingRecord(parser.parseBinding('field6', null), "memo", 6), + new BindingRecord(parser.parseBinding('field7', null), "memo", 7), + new BindingRecord(parser.parseBinding('field8', null), "memo", 8), + new BindingRecord(parser.parseBinding('field9', null), "memo", 9) ]; - for (var j = 0; j < 10; ++j) { - proto.addAst(astWithSource[j].ast, "memo", j); - } for (var i = 0; i < iterations; ++i) { var obj = new Obj(); for (var j = 0; j < 10; ++j) { obj.setField(j, i); } - var cd = proto.instantiate(dispatcher); + var cd = proto.instantiate(dispatcher, bindingRecords); cd.hydrate(obj); parentCd.addChild(cd); }