refactor(view): change view to pass all bindings to proto change detector at once

This commit is contained in:
vsavkin 2015-03-11 21:43:22 -07:00
parent 1adb23d222
commit 3273adade5
7 changed files with 93 additions and 83 deletions

View File

@ -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';

View File

@ -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<ProtoRecord>;
_recordBuilder:ProtoRecordBuilder;
_pipeRegistry:PipeRegistry;
_records:List<ProtoRecord>;
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();
}

View File

@ -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<Promise>;
// List<Map<eventName, handler>>, 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 <rootcmp></rootcmp>,

View File

@ -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;

View File

@ -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();

View File

@ -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;
}

View File

@ -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);
}