refactor(change_detection): removed directive and binding mementos

This commit is contained in:
vsavkin 2015-04-09 07:57:33 -07:00
parent 5408a9a72d
commit 61cb99ea42
15 changed files with 291 additions and 306 deletions

View File

@ -9,8 +9,12 @@ export {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError}
from './src/change_detection/exceptions'; from './src/change_detection/exceptions';
export {ProtoChangeDetector, ChangeDispatcher, ChangeDetector, ChangeDetection} from './src/change_detection/interfaces'; export {ProtoChangeDetector, ChangeDispatcher, ChangeDetector, ChangeDetection} from './src/change_detection/interfaces';
export {CHECK_ONCE, CHECK_ALWAYS, DETACHED, CHECKED, ON_PUSH, DEFAULT} from './src/change_detection/constants'; export {CHECK_ONCE, CHECK_ALWAYS, DETACHED, CHECKED, ON_PUSH, DEFAULT} from './src/change_detection/constants';
export {DynamicProtoChangeDetector, JitProtoChangeDetector, BindingRecord} export {DynamicProtoChangeDetector, JitProtoChangeDetector}
from './src/change_detection/proto_change_detector'; from './src/change_detection/proto_change_detector';
export {BindingRecord}
from './src/change_detection/binding_record';
export {DirectiveRecord}
from './src/change_detection/directive_record';
export {DynamicChangeDetector} export {DynamicChangeDetector}
from './src/change_detection/dynamic_change_detector'; from './src/change_detection/dynamic_change_detector';
export {BindingPropagationConfig} export {BindingPropagationConfig}

View File

@ -0,0 +1,58 @@
import {isPresent, isBlank} from 'angular2/src/facade/lang';
import {SetterFn} from 'angular2/src/reflection/types';
import {AST} from './parser/ast';
import {DirectiveRecord} from './directive_record';
const DIRECTIVE="directive";
const ELEMENT="element";
const TEXT_NODE="textNode";
export class BindingRecord {
mode:string;
ast:AST;
elementIndex:number;
propertyName:string;
setter:SetterFn;
directiveRecord:DirectiveRecord;
constructor(mode:string, ast:AST, elementIndex:number, propertyName:string, setter:SetterFn, directiveRecord:DirectiveRecord) {
this.mode = mode;
this.ast = ast;
this.elementIndex = elementIndex;
this.propertyName = propertyName;
this.setter = setter;
this.directiveRecord = directiveRecord;
}
callOnChange() {
return isPresent(this.directiveRecord) && this.directiveRecord.callOnChange;
}
isDirective() {
return this.mode === DIRECTIVE;
}
isElement() {
return this.mode === ELEMENT;
}
isTextNode() {
return this.mode === TEXT_NODE;
}
static createForDirective(ast:AST, propertyName:string, setter:SetterFn, directiveRecord:DirectiveRecord) {
return new BindingRecord(DIRECTIVE, ast, 0, propertyName, setter, directiveRecord);
}
static createForElement(ast:AST, elementIndex:number, propertyName:string) {
return new BindingRecord(ELEMENT, ast, elementIndex, propertyName, null, null);
}
static createForTextNode(ast:AST, elementIndex:number) {
return new BindingRecord(TEXT_NODE, ast, elementIndex, null, null, null);
}
}

View File

@ -3,6 +3,7 @@ import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/faca
import {AbstractChangeDetector} from './abstract_change_detector'; import {AbstractChangeDetector} from './abstract_change_detector';
import {ChangeDetectionUtil} from './change_detection_util'; import {ChangeDetectionUtil} from './change_detection_util';
import {DirectiveRecord} from './directive_record';
import { import {
ProtoRecord, ProtoRecord,
@ -33,7 +34,7 @@ var UTIL = "ChangeDetectionUtil";
var DISPATCHER_ACCESSOR = "this.dispatcher"; var DISPATCHER_ACCESSOR = "this.dispatcher";
var PIPE_REGISTRY_ACCESSOR = "this.pipeRegistry"; var PIPE_REGISTRY_ACCESSOR = "this.pipeRegistry";
var PROTOS_ACCESSOR = "this.protos"; var PROTOS_ACCESSOR = "this.protos";
var MEMENTOS_ACCESSOR = "this.directiveMementos"; var DIRECTIVES_ACCESSOR = "this.directiveRecords";
var CONTEXT_ACCESSOR = "this.context"; var CONTEXT_ACCESSOR = "this.context";
var CHANGE_LOCAL = "change"; var CHANGE_LOCAL = "change";
var CHANGES_LOCAL = "changes"; var CHANGES_LOCAL = "changes";
@ -51,19 +52,19 @@ ${notifyOnAllChangesDone}
${setContext}; ${setContext};
return function(dispatcher, pipeRegistry) { return function(dispatcher, pipeRegistry) {
return new ${type}(dispatcher, pipeRegistry, protos, directiveMementos); return new ${type}(dispatcher, pipeRegistry, protos, directiveRecords);
} }
`; `;
} }
function constructorTemplate(type:string, fieldsDefinitions:string):string { function constructorTemplate(type:string, fieldsDefinitions:string):string {
return ` return `
var ${type} = function ${type}(dispatcher, pipeRegistry, protos, directiveMementos) { var ${type} = function ${type}(dispatcher, pipeRegistry, protos, directiveRecords) {
${ABSTRACT_CHANGE_DETECTOR}.call(this); ${ABSTRACT_CHANGE_DETECTOR}.call(this);
${DISPATCHER_ACCESSOR} = dispatcher; ${DISPATCHER_ACCESSOR} = dispatcher;
${PIPE_REGISTRY_ACCESSOR} = pipeRegistry; ${PIPE_REGISTRY_ACCESSOR} = pipeRegistry;
${PROTOS_ACCESSOR} = protos; ${PROTOS_ACCESSOR} = protos;
${MEMENTOS_ACCESSOR} = directiveMementos; ${DIRECTIVES_ACCESSOR} = directiveRecords;
${LOCALS_ACCESSOR} = null; ${LOCALS_ACCESSOR} = null;
${fieldsDefinitions} ${fieldsDefinitions}
} }
@ -80,7 +81,7 @@ function hydrateTemplate(type:string, mode:string, fieldDefinitions:string, pipe
directiveFieldNames:List<String>):string { directiveFieldNames:List<String>):string {
var directiveInit = ""; var directiveInit = "";
for(var i = 0; i < directiveFieldNames.length; ++i) { for(var i = 0; i < directiveFieldNames.length; ++i) {
directiveInit += `${directiveFieldNames[i]} = this.directiveMementos[${i}].directive(directives);\n`; directiveInit += `${directiveFieldNames[i]} = directives.directive(this.directiveRecords[${i}]);\n`;
} }
return ` return `
@ -137,7 +138,7 @@ ${records}
} }
function pipeCheckTemplate(protoIndex:number, context:string, bindingPropagationConfig:string, pipe:string, pipeType:string, function pipeCheckTemplate(protoIndex:number, context:string, bindingPropagationConfig:string, pipe:string, pipeType:string,
oldValue:string, newValue:string, change:string, invokeMemento:string, oldValue:string, newValue:string, change:string, update:string,
addToChanges, lastInDirective:string):string{ addToChanges, lastInDirective:string):string{
return ` return `
${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}]; ${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}];
@ -151,7 +152,7 @@ if (${pipe} === ${UTIL}.unitialized()) {
${newValue} = ${pipe}.transform(${context}); ${newValue} = ${pipe}.transform(${context});
if (! ${UTIL}.noChangeMarker(${newValue})) { if (! ${UTIL}.noChangeMarker(${newValue})) {
${change} = true; ${change} = true;
${invokeMemento} ${update}
${addToChanges} ${addToChanges}
${oldValue} = ${newValue}; ${oldValue} = ${newValue};
} }
@ -160,13 +161,13 @@ ${lastInDirective}
} }
function referenceCheckTemplate(protoIndex:number, assignment:string, oldValue:string, newValue:string, change:string, function referenceCheckTemplate(protoIndex:number, assignment:string, oldValue:string, newValue:string, change:string,
invokeMemento:string, addToChanges:string, lastInDirective:string):string { update:string, addToChanges:string, lastInDirective:string):string {
return ` return `
${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}]; ${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}];
${assignment} ${assignment}
if (${newValue} !== ${oldValue} || (${newValue} !== ${newValue}) && (${oldValue} !== ${oldValue})) { if (${newValue} !== ${oldValue} || (${newValue} !== ${newValue}) && (${oldValue} !== ${oldValue})) {
${change} = true; ${change} = true;
${invokeMemento} ${update}
${addToChanges} ${addToChanges}
${oldValue} = ${newValue}; ${oldValue} = ${newValue};
} }
@ -200,7 +201,7 @@ if (${cond}) {
} }
function addToChangesTemplate(oldValue:string, newValue:string):string { function addToChangesTemplate(oldValue:string, newValue:string):string {
return `${CHANGES_LOCAL} = ${UTIL}.addChange(${CHANGES_LOCAL}, ${CURRENT_PROTO}.bindingMemento, ${UTIL}.simpleChange(${oldValue}, ${newValue}));`; return `${CHANGES_LOCAL} = ${UTIL}.addChange(${CHANGES_LOCAL}, ${CURRENT_PROTO}.bindingRecord.propertyName, ${UTIL}.simpleChange(${oldValue}, ${newValue}));`;
} }
function updateDirectiveTemplate(oldValue:string, newValue:string, directiveProperty:string):string { function updateDirectiveTemplate(oldValue:string, newValue:string, directiveProperty:string):string {
@ -213,7 +214,7 @@ ${directiveProperty} = ${newValue};
function updateElementTemplate(oldValue:string, newValue:string):string { function updateElementTemplate(oldValue:string, newValue:string):string {
return ` return `
if(throwOnChange) ${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue})); if(throwOnChange) ${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue}));
${DISPATCHER_ACCESSOR}.invokeMementoFor(${CURRENT_PROTO}.bindingMemento, ${newValue}); ${DISPATCHER_ACCESSOR}.notifyOnBinding(${CURRENT_PROTO}.bindingRecord, ${newValue});
`; `;
} }
@ -230,18 +231,18 @@ if(${CHANGES_LOCAL}) {
export class ChangeDetectorJITGenerator { export class ChangeDetectorJITGenerator {
typeName:string; typeName:string;
records:List<ProtoRecord>; records:List<ProtoRecord>;
directiveMementos:List; directiveRecords:List;
localNames:List<string>; localNames:List<string>;
changeNames:List<string>; changeNames:List<string>;
fieldNames:List<string>; fieldNames:List<string>;
pipeNames:List<string>; pipeNames:List<string>;
changeDetectionStrategy:stirng; changeDetectionStrategy:stirng;
constructor(typeName:string, changeDetectionStrategy:string, records:List<ProtoRecord>, directiveMementos:List) { constructor(typeName:string, changeDetectionStrategy:string, records:List<ProtoRecord>, directiveRecords:List) {
this.typeName = typeName; this.typeName = typeName;
this.changeDetectionStrategy = changeDetectionStrategy; this.changeDetectionStrategy = changeDetectionStrategy;
this.records = records; this.records = records;
this.directiveMementos = directiveMementos; this.directiveRecords = directiveRecords;
this.localNames = this.getLocalNames(records); this.localNames = this.getLocalNames(records);
this.changeNames = this.getChangeNames(this.localNames); this.changeNames = this.getChangeNames(this.localNames);
@ -273,8 +274,8 @@ export class ChangeDetectorJITGenerator {
generate():Function { generate():Function {
var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(), var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(),
this.genCallOnAllChangesDone(), this.genHydrate()); this.genCallOnAllChangesDone(), this.genHydrate());
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos', 'directiveMementos', text) return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos', 'directiveRecords', text)
(AbstractChangeDetector, ChangeDetectionUtil, this.records, this.directiveMementos); (AbstractChangeDetector, ChangeDetectionUtil, this.records, this.directiveRecords);
} }
genConstructor():string { genConstructor():string {
@ -288,11 +289,11 @@ export class ChangeDetectorJITGenerator {
} }
getDirectiveFieldNames():List<string> { getDirectiveFieldNames():List<string> {
return this.directiveMementos.map((d) => this.getDirective(d)); return this.directiveRecords.map((d) => this.getDirective(d));
} }
getDirective(memento) { getDirective(d:DirectiveRecord) {
return `this.directive_${memento.name}`; return `this.directive_${d.name}`;
} }
genFieldDefinitions() { genFieldDefinitions() {
@ -320,12 +321,12 @@ export class ChangeDetectorJITGenerator {
genCallOnAllChangesDone():string { genCallOnAllChangesDone():string {
var notifications = []; var notifications = [];
var mementos = this.directiveMementos; var dirs = this.directiveRecords;
for (var i = mementos.length - 1; i >= 0; --i) { for (var i = dirs.length - 1; i >= 0; --i) {
var memento = mementos[i]; var dir = dirs[i];
if (memento.callOnAllChangesDone) { if (dir.callOnAllChangesDone) {
var directive = `this.directive_${memento.name}`; var directive = `this.directive_${dir.name}`;
notifications.push(onAllChangesDoneTemplate(directive)); notifications.push(onAllChangesDoneTemplate(directive));
} }
} }
@ -363,12 +364,12 @@ export class ChangeDetectorJITGenerator {
var pipe = this.pipeNames[r.selfIndex]; var pipe = this.pipeNames[r.selfIndex];
var bpc = r.mode === RECORD_TYPE_BINDING_PIPE ? "this.bindingPropagationConfig" : "null"; var bpc = r.mode === RECORD_TYPE_BINDING_PIPE ? "this.bindingPropagationConfig" : "null";
var invokeMemento = this.genUpdateDirectiveOrElement(r); var update = this.genUpdateDirectiveOrElement(r);
var addToChanges = this.genAddToChanges(r); var addToChanges = this.genAddToChanges(r);
var lastInDirective = this.genNotifyOnChanges(r); var lastInDirective = this.genNotifyOnChanges(r);
return pipeCheckTemplate(r.selfIndex - 1, context, bpc, pipe, r.name, oldValue, newValue, change, return pipeCheckTemplate(r.selfIndex - 1, context, bpc, pipe, r.name, oldValue, newValue, change,
invokeMemento, addToChanges, lastInDirective); update, addToChanges, lastInDirective);
} }
genReferenceCheck(r:ProtoRecord):string { genReferenceCheck(r:ProtoRecord):string {
@ -377,12 +378,12 @@ export class ChangeDetectorJITGenerator {
var change = this.changeNames[r.selfIndex]; var change = this.changeNames[r.selfIndex];
var assignment = this.genUpdateCurrentValue(r); var assignment = this.genUpdateCurrentValue(r);
var invokeMemento = this.genUpdateDirectiveOrElement(r); var update = this.genUpdateDirectiveOrElement(r);
var addToChanges = this.genAddToChanges(r); var addToChanges = this.genAddToChanges(r);
var lastInDirective = this.genNotifyOnChanges(r); var lastInDirective = this.genNotifyOnChanges(r);
var check = referenceCheckTemplate(r.selfIndex - 1, assignment, oldValue, newValue, change, var check = referenceCheckTemplate(r.selfIndex - 1, assignment, oldValue, newValue, change,
invokeMemento, addToChanges, lastInDirective); update, addToChanges, lastInDirective);
if (r.isPureFunction()) { if (r.isPureFunction()) {
return this.ifChangedGuard(r, check); return this.ifChangedGuard(r, check);
} else { } else {
@ -455,8 +456,9 @@ export class ChangeDetectorJITGenerator {
var newValue = this.localNames[r.selfIndex]; var newValue = this.localNames[r.selfIndex];
var oldValue = this.fieldNames[r.selfIndex]; var oldValue = this.fieldNames[r.selfIndex];
if (isPresent(r.directiveMemento)) { var br = r.bindingRecord;
var directiveProperty = `${this.getDirective(r.directiveMemento)}.${r.bindingMemento.propertyName}`; if (br.isDirective()) {
var directiveProperty = `${this.getDirective(br.directiveRecord)}.${br.propertyName}`;
return updateDirectiveTemplate(oldValue, newValue, directiveProperty); return updateDirectiveTemplate(oldValue, newValue, directiveProperty);
} else { } else {
return updateElementTemplate(oldValue, newValue); return updateElementTemplate(oldValue, newValue);
@ -466,14 +468,13 @@ export class ChangeDetectorJITGenerator {
genAddToChanges(r:ProtoRecord):string { genAddToChanges(r:ProtoRecord):string {
var newValue = this.localNames[r.selfIndex]; var newValue = this.localNames[r.selfIndex];
var oldValue = this.fieldNames[r.selfIndex]; var oldValue = this.fieldNames[r.selfIndex];
var callOnChange = r.directiveMemento && r.directiveMemento.callOnChange; return r.bindingRecord.callOnChange() ? addToChangesTemplate(oldValue, newValue) : "";
return callOnChange ? addToChangesTemplate(oldValue, newValue) : "";
} }
genNotifyOnChanges(r:ProtoRecord):string{ genNotifyOnChanges(r:ProtoRecord):string{
var callOnChange = r.directiveMemento && r.directiveMemento.callOnChange; var br = r.bindingRecord;
if (r.lastInDirective && callOnChange) { if (r.lastInDirective && br.callOnChange()) {
return notifyOnChangesTemplate(this.getDirective(r.directiveMemento)); return notifyOnChangesTemplate(this.getDirective(br.directiveRecord));
} else { } else {
return ""; return "";
} }

View File

@ -125,11 +125,11 @@ export class ChangeDetectionUtil {
return _simpleChange(previousValue, currentValue); return _simpleChange(previousValue, currentValue);
} }
static addChange(changes, bindingMemento, change){ static addChange(changes, propertyName:string, change){
if (isBlank(changes)) { if (isBlank(changes)) {
changes = {}; changes = {};
} }
changes[bindingMemento.propertyName] = change; changes[propertyName] = change;
return changes; return changes;
} }
} }

View File

@ -46,8 +46,7 @@ function _selfRecord(r:ProtoRecord, contextIndex:number, selfIndex:number):Proto
r.fixedArgs, r.fixedArgs,
contextIndex, contextIndex,
selfIndex, selfIndex,
r.bindingMemento, r.bindingRecord,
r.directiveMemento,
r.expressionAsString, r.expressionAsString,
r.lastInBinding, r.lastInBinding,
r.lastInDirective r.lastInDirective
@ -74,8 +73,7 @@ function _replaceIndices(r:ProtoRecord, selfIndex:number, indexMap:Map) {
r.fixedArgs, r.fixedArgs,
contextIndex, contextIndex,
selfIndex, selfIndex,
r.bindingMemento, r.bindingRecord,
r.directiveMemento,
r.expressionAsString, r.expressionAsString,
r.lastInBinding, r.lastInBinding,
r.lastInDirective r.lastInDirective

View File

@ -0,0 +1,19 @@
export class DirectiveRecord {
elementIndex:number;
directiveIndex:number;
callOnAllChangesDone:boolean;
callOnChange:boolean;
constructor(elementIndex:number, directiveIndex:number,
callOnAllChangesDone:boolean,
callOnChange:boolean) {
this.elementIndex = elementIndex;
this.directiveIndex = directiveIndex;
this.callOnAllChangesDone = callOnAllChangesDone;
this.callOnChange = callOnChange;
}
get name() {
return `${this.elementIndex}_${this.directiveIndex}`;
}
}

View File

@ -2,6 +2,8 @@ import {isPresent, isBlank, BaseException, FunctionWrapper} from 'angular2/src/f
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection'; import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {AbstractChangeDetector} from './abstract_change_detector'; import {AbstractChangeDetector} from './abstract_change_detector';
import {BindingRecord} from './binding_record';
import {DirectiveRecord} from './directive_record';
import {PipeRegistry} from './pipes/pipe_registry'; import {PipeRegistry} from './pipes/pipe_registry';
import {ChangeDetectionUtil, uninitialized} from './change_detection_util'; import {ChangeDetectionUtil, uninitialized} from './change_detection_util';
@ -35,11 +37,11 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
protos:List<ProtoRecord>; protos:List<ProtoRecord>;
directives:any; directives:any;
directiveMementos:List; directiveRecords:List;
changeControlStrategy:string; changeControlStrategy:string;
constructor(changeControlStrategy:string, dispatcher:any, pipeRegistry:PipeRegistry, constructor(changeControlStrategy:string, dispatcher:any, pipeRegistry:PipeRegistry,
protoRecords:List<ProtoRecord>, directiveMementos:List) { protoRecords:List<ProtoRecord>, directiveRecords:List) {
super(); super();
this.dispatcher = dispatcher; this.dispatcher = dispatcher;
this.pipeRegistry = pipeRegistry; this.pipeRegistry = pipeRegistry;
@ -57,7 +59,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
this.directives = null; this.directives = null;
this.protos = protoRecords; this.protos = protoRecords;
this.directiveMementos = directiveMementos; this.directiveRecords = directiveRecords;
this.changeControlStrategy = changeControlStrategy; this.changeControlStrategy = changeControlStrategy;
} }
@ -99,45 +101,45 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
var change = this._check(proto); var change = this._check(proto);
if (isPresent(change)) { if (isPresent(change)) {
if (throwOnChange) ChangeDetectionUtil.throwOnChange(proto, change); if (throwOnChange) ChangeDetectionUtil.throwOnChange(proto, change);
this._updateDirectiveOrElement(change, proto.directiveMemento, proto.bindingMemento); this._updateDirectiveOrElement(change, proto.bindingRecord);
changes = this._addChange(proto.directiveMemento, proto.bindingMemento, change, changes); changes = this._addChange(proto.bindingRecord, change, changes);
} }
if (proto.lastInDirective && isPresent(changes)) { if (proto.lastInDirective && isPresent(changes)) {
this._directive(proto.directiveMemento).onChange(changes); this._directive(proto.bindingRecord.directiveRecord).onChange(changes);
changes = null; changes = null;
} }
} }
} }
callOnAllChangesDone() { callOnAllChangesDone() {
var mementos = this.directiveMementos; var dirs = this.directiveRecords;
for (var i = mementos.length - 1; i >= 0; --i) { for (var i = dirs.length - 1; i >= 0; --i) {
var memento = mementos[i]; var dir = dirs[i];
if (memento.callOnAllChangesDone) { if (dir.callOnAllChangesDone) {
this._directive(memento).onAllChangesDone(); this._directive(dir).onAllChangesDone();
} }
} }
} }
_updateDirectiveOrElement(change, directiveMemento, bindingMemento) { _updateDirectiveOrElement(change, bindingRecord) {
if (isBlank(directiveMemento)) { if (isBlank(bindingRecord.directiveRecord)) {
this.dispatcher.invokeMementoFor(bindingMemento, change.currentValue); this.dispatcher.notifyOnBinding(bindingRecord, change.currentValue);
} else { } else {
bindingMemento.setter(this._directive(directiveMemento), change.currentValue); bindingRecord.setter(this._directive(bindingRecord.directiveRecord), change.currentValue);
} }
} }
_addChange(directiveMemento, bindingMemento, change, changes) { _addChange(bindingRecord:BindingRecord, change, changes) {
if (isPresent(directiveMemento) && directiveMemento.callOnChange) { if (bindingRecord.callOnChange()) {
return ChangeDetectionUtil.addChange(changes, bindingMemento, change); return ChangeDetectionUtil.addChange(changes, bindingRecord.propertyName, change);
} else { } else {
return changes; return changes;
} }
} }
_directive(memento) { _directive(directive:DirectiveRecord) {
return memento.directive(this.directives); return this.directives.directive(directive);
} }
_check(proto:ProtoRecord) { _check(proto:ProtoRecord) {

View File

@ -1,11 +1,10 @@
import {List} from 'angular2/src/facade/collection'; import {List} from 'angular2/src/facade/collection';
import {Locals} from './parser/locals'; import {Locals} from './parser/locals';
import {AST} from './parser/ast';
import {DEFAULT} from './constants'; import {DEFAULT} from './constants';
import {BindingRecord} from './binding_record';
export class ProtoChangeDetector { export class ProtoChangeDetector {
addAst(ast:AST, bindingMemento:any, directiveMemento:any = null){} instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveRecords:List):ChangeDetector{
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveMementos:List):ChangeDetector{
return null; return null;
} }
} }
@ -17,7 +16,7 @@ export class ChangeDetection {
} }
export class ChangeDispatcher { export class ChangeDispatcher {
invokeMementoFor(memento:any, value) {} notifyOnBinding(bindingRecord:BindingRecord, value:any) {}
} }
export class ChangeDetector { export class ChangeDetector {

View File

@ -27,6 +27,7 @@ import {ChangeDetectionUtil} from './change_detection_util';
import {DynamicChangeDetector} from './dynamic_change_detector'; import {DynamicChangeDetector} from './dynamic_change_detector';
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator'; import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
import {PipeRegistry} from './pipes/pipe_registry'; import {PipeRegistry} from './pipes/pipe_registry';
import {BindingRecord} from './binding_record';
import {coalesce} from './coalesce'; import {coalesce} from './coalesce';
@ -45,18 +46,6 @@ import {
RECORD_TYPE_INTERPOLATE RECORD_TYPE_INTERPOLATE
} from './proto_record'; } from './proto_record';
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 { export class DynamicProtoChangeDetector extends ProtoChangeDetector {
_pipeRegistry:PipeRegistry; _pipeRegistry:PipeRegistry;
_records:List<ProtoRecord>; _records:List<ProtoRecord>;
@ -68,17 +57,17 @@ export class DynamicProtoChangeDetector extends ProtoChangeDetector {
this._changeControlStrategy = changeControlStrategy; this._changeControlStrategy = changeControlStrategy;
} }
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveMementos:List) { instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveRecords:List) {
this._createRecordsIfNecessary(bindingRecords, variableBindings); this._createRecordsIfNecessary(bindingRecords, variableBindings);
return new DynamicChangeDetector(this._changeControlStrategy, dispatcher, return new DynamicChangeDetector(this._changeControlStrategy, dispatcher,
this._pipeRegistry, this._records, directiveMementos); this._pipeRegistry, this._records, directiveRecords);
} }
_createRecordsIfNecessary(bindingRecords:List, variableBindings:List) { _createRecordsIfNecessary(bindingRecords:List, variableBindings:List) {
if (isBlank(this._records)) { if (isBlank(this._records)) {
var recordBuilder = new ProtoRecordBuilder(); var recordBuilder = new ProtoRecordBuilder();
ListWrapper.forEach(bindingRecords, (r) => { ListWrapper.forEach(bindingRecords, (b) => {
recordBuilder.addAst(r.ast, r.bindingMemento, r.directiveMemento, variableBindings); recordBuilder.addAst(b, variableBindings);
}); });
this._records = coalesce(recordBuilder.records); this._records = coalesce(recordBuilder.records);
} }
@ -98,22 +87,22 @@ export class JitProtoChangeDetector extends ProtoChangeDetector {
this._changeControlStrategy = changeControlStrategy; this._changeControlStrategy = changeControlStrategy;
} }
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveMementos:List) { instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveRecords:List) {
this._createFactoryIfNecessary(bindingRecords, variableBindings, directiveMementos); this._createFactoryIfNecessary(bindingRecords, variableBindings, directiveRecords);
return this._factory(dispatcher, this._pipeRegistry); return this._factory(dispatcher, this._pipeRegistry);
} }
_createFactoryIfNecessary(bindingRecords:List, variableBindings:List, directiveMementos:List) { _createFactoryIfNecessary(bindingRecords:List, variableBindings:List, directiveRecords:List) {
if (isBlank(this._factory)) { if (isBlank(this._factory)) {
var recordBuilder = new ProtoRecordBuilder(); var recordBuilder = new ProtoRecordBuilder();
ListWrapper.forEach(bindingRecords, (r) => { ListWrapper.forEach(bindingRecords, (b) => {
recordBuilder.addAst(r.ast, r.bindingMemento, r.directiveMemento, variableBindings); recordBuilder.addAst(b, variableBindings);
}); });
var c = _jitProtoChangeDetectorClassCounter++; var c = _jitProtoChangeDetectorClassCounter++;
var records = coalesce(recordBuilder.records); var records = coalesce(recordBuilder.records);
var typeName = `ChangeDetector${c}`; var typeName = `ChangeDetector${c}`;
this._factory = new ChangeDetectorJITGenerator(typeName, this._changeControlStrategy, records, this._factory = new ChangeDetectorJITGenerator(typeName, this._changeControlStrategy, records,
directiveMementos).generate(); directiveRecords).generate();
} }
} }
} }
@ -125,13 +114,13 @@ class ProtoRecordBuilder {
this.records = []; this.records = [];
} }
addAst(ast:AST, bindingMemento:any, directiveMemento:any = null, variableBindings:List = null) { addAst(b:BindingRecord, variableBindings:List = null) {
var last = ListWrapper.last(this.records); var last = ListWrapper.last(this.records);
if (isPresent(last) && last.directiveMemento == directiveMemento) { if (isPresent(last) && last.bindingRecord.directiveRecord == b.directiveRecord) {
last.lastInDirective = false; last.lastInDirective = false;
} }
var pr = _ConvertAstIntoProtoRecords.convert(ast, bindingMemento, directiveMemento, this.records.length, variableBindings); var pr = _ConvertAstIntoProtoRecords.convert(b, this.records.length, variableBindings);
if (! ListWrapper.isEmpty(pr)) { if (! ListWrapper.isEmpty(pr)) {
var last = ListWrapper.last(pr); var last = ListWrapper.last(pr);
last.lastInBinding = true; last.lastInBinding = true;
@ -144,24 +133,22 @@ class ProtoRecordBuilder {
class _ConvertAstIntoProtoRecords { class _ConvertAstIntoProtoRecords {
protoRecords:List; protoRecords:List;
bindingMemento:any; bindingRecord:BindingRecord;
directiveMemento:any;
variableBindings:List; variableBindings:List;
contextIndex:number; contextIndex:number;
expressionAsString:string; expressionAsString:string;
constructor(bindingMemento:any, directiveMemento:any, contextIndex:number, expressionAsString:string, variableBindings:List) { constructor(bindingRecord:BindingRecord, contextIndex:number, expressionAsString:string, variableBindings:List) {
this.protoRecords = []; this.protoRecords = [];
this.bindingMemento = bindingMemento; this.bindingRecord = bindingRecord;
this.directiveMemento = directiveMemento;
this.contextIndex = contextIndex; this.contextIndex = contextIndex;
this.expressionAsString = expressionAsString; this.expressionAsString = expressionAsString;
this.variableBindings = variableBindings; this.variableBindings = variableBindings;
} }
static convert(ast:AST, bindingMemento:any, directiveMemento:any, contextIndex:number, variableBindings:List) { static convert(b:BindingRecord, contextIndex:number, variableBindings:List) {
var c = new _ConvertAstIntoProtoRecords(bindingMemento, directiveMemento, contextIndex, ast.toString(), variableBindings); var c = new _ConvertAstIntoProtoRecords(b, contextIndex, b.ast.toString(), variableBindings);
ast.visit(c); b.ast.visit(c);
return c.protoRecords; return c.protoRecords;
} }
@ -262,7 +249,7 @@ class _ConvertAstIntoProtoRecords {
var selfIndex = ++ this.contextIndex; var selfIndex = ++ this.contextIndex;
ListWrapper.push(this.protoRecords, ListWrapper.push(this.protoRecords,
new ProtoRecord(type, name, funcOrValue, args, fixedArgs, context, selfIndex, new ProtoRecord(type, name, funcOrValue, args, fixedArgs, context, selfIndex,
this.bindingMemento, this.directiveMemento, this.expressionAsString, false, false)); this.bindingRecord, this.expressionAsString, false, false));
return selfIndex; return selfIndex;
} }
} }

View File

@ -1,4 +1,5 @@
import {List} from 'angular2/src/facade/collection'; import {List} from 'angular2/src/facade/collection';
import {BindingRecord} from './binding_record';
export const RECORD_TYPE_SELF = 0; export const RECORD_TYPE_SELF = 0;
export const RECORD_TYPE_CONST = 1; export const RECORD_TYPE_CONST = 1;
@ -20,8 +21,7 @@ export class ProtoRecord {
fixedArgs:List; fixedArgs:List;
contextIndex:number; contextIndex:number;
selfIndex:number; selfIndex:number;
bindingMemento:any; bindingRecord:BindingRecord;
directiveMemento:any;
lastInBinding:boolean; lastInBinding:boolean;
lastInDirective:boolean; lastInDirective:boolean;
expressionAsString:string; expressionAsString:string;
@ -33,8 +33,7 @@ export class ProtoRecord {
fixedArgs:List, fixedArgs:List,
contextIndex:number, contextIndex:number,
selfIndex:number, selfIndex:number,
bindingMemento:any, bindingRecord:BindingRecord,
directiveMemento:any,
expressionAsString:string, expressionAsString:string,
lastInBinding:boolean, lastInBinding:boolean,
lastInDirective:boolean) { lastInDirective:boolean) {
@ -46,8 +45,7 @@ export class ProtoRecord {
this.fixedArgs = fixedArgs; this.fixedArgs = fixedArgs;
this.contextIndex = contextIndex; this.contextIndex = contextIndex;
this.selfIndex = selfIndex; this.selfIndex = selfIndex;
this.bindingMemento = bindingMemento; this.bindingRecord = bindingRecord;
this.directiveMemento = directiveMemento;
this.lastInBinding = lastInBinding; this.lastInBinding = lastInBinding;
this.lastInDirective = lastInDirective; this.lastInDirective = lastInDirective;
this.expressionAsString = expressionAsString; this.expressionAsString = expressionAsString;

View File

@ -1,6 +1,6 @@
import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection'; import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection';
import {AST, Locals, ChangeDispatcher, ProtoChangeDetector, ChangeDetector, import {AST, Locals, ChangeDispatcher, ProtoChangeDetector, ChangeDetector,
BindingRecord, BindingPropagationConfig, uninitialized} from 'angular2/change_detection'; ChangeRecord, BindingRecord, DirectiveRecord, BindingPropagationConfig} from 'angular2/change_detection';
import {ProtoElementInjector, ElementInjector, PreBuiltObjects, DirectiveBinding} from './element_injector'; import {ProtoElementInjector, ElementInjector, PreBuiltObjects, DirectiveBinding} from './element_injector';
import {ElementBinder} from './element_binder'; import {ElementBinder} from './element_binder';
@ -70,11 +70,10 @@ export class View {
_setContextAndLocals(newContext, locals) { _setContextAndLocals(newContext, locals) {
this.context = newContext; this.context = newContext;
this.locals.parent = locals; this.locals.parent = locals;
this.changeDetector.hydrate(this.context, this.locals, this.elementInjectors);
} }
_hydrateChangeDetector() { _hydrateChangeDetector() {
this.changeDetector.hydrate(this.context, this.locals, this.elementInjectors); this.changeDetector.hydrate(this.context, this.locals, this);
} }
_dehydrateContext() { _dehydrateContext() {
@ -229,18 +228,21 @@ export class View {
} }
// dispatch to element injector or text nodes based on context // dispatch to element injector or text nodes based on context
invokeMementoFor(memento:any, currentValue:any) { notifyOnBinding(b:BindingRecord, currentValue:any) {
if (memento instanceof ElementBindingMemento) { if (b.isElement()) {
var elementMemento:ElementBindingMemento = memento;
this.proto.renderer.setElementProperty( this.proto.renderer.setElementProperty(
this.render, elementMemento.elementIndex, elementMemento.propertyName, currentValue this.render, b.elementIndex, b.propertyName, currentValue
); );
} else { } else {
// we know it refers to _textNodes. // we know it refers to _textNodes.
var textNodeIndex:number = memento; this.proto.renderer.setText(this.render, b.elementIndex, currentValue);
this.proto.renderer.setText(this.render, textNodeIndex, currentValue);
} }
} }
directive(directive:DirectiveRecord) {
var elementInjector:ElementInjector = this.elementInjectors[directive.elementIndex];
return elementInjector.getDirectiveAtIndex(directive.directiveIndex);
}
// implementation of EventDispatcher#dispatchEvent // implementation of EventDispatcher#dispatchEvent
dispatchEvent( dispatchEvent(
@ -277,12 +279,12 @@ export class ProtoView {
variableBindings: Map; variableBindings: Map;
protoLocals:Map; protoLocals:Map;
textNodesWithBindingCount:int; textNodesWithBindingCount:int;
bindingRecords:List; bindings:List;
parentProtoView:ProtoView; parentProtoView:ProtoView;
_variableBindings:List; _variableBindings:List;
_directiveMementosMap:Map; _directiveRecordsMap:Map;
_directiveMementos:List; _directiveRecords:List;
render:renderApi.ProtoViewRef; render:renderApi.ProtoViewRef;
renderer:renderApi.Renderer; renderer:renderApi.Renderer;
@ -298,10 +300,10 @@ export class ProtoView {
this.protoChangeDetector = protoChangeDetector; this.protoChangeDetector = protoChangeDetector;
this.parentProtoView = null; this.parentProtoView = null;
this.textNodesWithBindingCount = 0; this.textNodesWithBindingCount = 0;
this.bindingRecords = []; this.bindings = [];
this._directiveMementosMap = MapWrapper.create(); this._directiveRecordsMap = MapWrapper.create();
this._variableBindings = null; this._variableBindings = null;
this._directiveMementos = null; this._directiveRecords = null;
} }
//TODO: Tobias or Victor. Moving it into the constructor. //TODO: Tobias or Victor. Moving it into the constructor.
@ -325,23 +327,23 @@ export class ProtoView {
//TODO: Tobias or Victor. Moving it into the constructor. //TODO: Tobias or Victor. Moving it into the constructor.
// this work should be done the constructor of ProtoView once we separate // this work should be done the constructor of ProtoView once we separate
// ProtoView and ProtoViewBuilder // ProtoView and ProtoViewBuilder
getDirectiveMementos() { getdirectiveRecords() {
if (isPresent(this._directiveMementos)) { if (isPresent(this._directiveRecords)) {
return this._directiveMementos; return this._directiveRecords;
} }
this._directiveMementos = []; this._directiveRecords = [];
for (var injectorIndex = 0; injectorIndex < this.elementBinders.length; ++injectorIndex) { for (var injectorIndex = 0; injectorIndex < this.elementBinders.length; ++injectorIndex) {
var pei = this.elementBinders[injectorIndex].protoElementInjector; var pei = this.elementBinders[injectorIndex].protoElementInjector;
if (isPresent(pei)) { if (isPresent(pei)) {
for (var directiveIndex = 0; directiveIndex < pei.numberOfDirectives; ++directiveIndex) { for (var directiveIndex = 0; directiveIndex < pei.numberOfDirectives; ++directiveIndex) {
ListWrapper.push(this._directiveMementos, this._getDirectiveMemento(injectorIndex, directiveIndex)); ListWrapper.push(this._directiveRecords, this._getDirectiveRecord(injectorIndex, directiveIndex));
} }
} }
} }
return this._directiveMementos; return this._directiveRecords;
} }
bindVariable(contextName:string, templateName:string) { bindVariable(contextName:string, templateName:string) {
@ -361,16 +363,18 @@ export class ProtoView {
* Adds a text node binding for the last created ElementBinder via bindElement * Adds a text node binding for the last created ElementBinder via bindElement
*/ */
bindTextNode(expression:AST) { bindTextNode(expression:AST) {
var memento = this.textNodesWithBindingCount++; var textNodeIndex = this.textNodesWithBindingCount++;
ListWrapper.push(this.bindingRecords, new BindingRecord(expression, memento, null)); var b = BindingRecord.createForTextNode(expression, textNodeIndex);
ListWrapper.push(this.bindings, b);
} }
/** /**
* Adds an element property binding for the last created ElementBinder via bindElement * Adds an element property binding for the last created ElementBinder via bindElement
*/ */
bindElementProperty(expression:AST, setterName:string) { bindElementProperty(expression:AST, setterName:string) {
var memento = new ElementBindingMemento(this.elementBinders.length-1, setterName); var elementIndex = this.elementBinders.length-1;
ListWrapper.push(this.bindingRecords, new BindingRecord(expression, memento, null)); var b = BindingRecord.createForElement(expression, elementIndex, setterName);
ListWrapper.push(this.bindings, b);
} }
/** /**
@ -411,82 +415,22 @@ export class ProtoView {
setter:SetterFn) { setter:SetterFn) {
var elementIndex = this.elementBinders.length-1; var elementIndex = this.elementBinders.length-1;
var bindingMemento = new DirectiveBindingMemento( var directiveRecord = this._getDirectiveRecord(elementIndex, directiveIndex);
elementIndex, var b = BindingRecord.createForDirective(expression, setterName, setter, directiveRecord);
directiveIndex, ListWrapper.push(this.bindings, b);
setterName,
setter
);
var directiveMemento = this._getDirectiveMemento(elementIndex, directiveIndex);
ListWrapper.push(this.bindingRecords, new BindingRecord(expression, bindingMemento, directiveMemento));
} }
_getDirectiveMemento(elementInjectorIndex:number, directiveIndex:number) { _getDirectiveRecord(elementInjectorIndex:number, directiveIndex:number) {
var id = elementInjectorIndex * 100 + directiveIndex; var id = elementInjectorIndex * 100 + directiveIndex;
var protoElementInjector = this.elementBinders[elementInjectorIndex].protoElementInjector; var protoElementInjector = this.elementBinders[elementInjectorIndex].protoElementInjector;
if (!MapWrapper.contains(this._directiveMementosMap, id)) { if (!MapWrapper.contains(this._directiveRecordsMap, id)) {
var binding = protoElementInjector.getDirectiveBindingAtIndex(directiveIndex); var binding = protoElementInjector.getDirectiveBindingAtIndex(directiveIndex);
MapWrapper.set(this._directiveMementosMap, id, MapWrapper.set(this._directiveRecordsMap, id,
new DirectiveMemento(elementInjectorIndex, directiveIndex, new DirectiveRecord(elementInjectorIndex, directiveIndex,
binding.callOnAllChangesDone, binding.callOnChange)); binding.callOnAllChangesDone, binding.callOnChange));
} }
return MapWrapper.get(this._directiveMementosMap, id); return MapWrapper.get(this._directiveRecordsMap, id);
}
}
/**
*/
export class ElementBindingMemento {
elementIndex:int;
propertyName:string;
constructor(elementIndex:int, propertyName:string) {
this.elementIndex = elementIndex;
this.propertyName = propertyName;
}
}
/**
*/
export class DirectiveBindingMemento {
_elementInjectorIndex:int;
_directiveIndex:int;
propertyName:string;
setter:SetterFn;
constructor(
elementInjectorIndex:number,
directiveIndex:number,
propertyName:string,
setter:SetterFn) {
this._elementInjectorIndex = elementInjectorIndex;
this._directiveIndex = directiveIndex;
this.propertyName = propertyName;
this.setter = setter;
}
}
class DirectiveMemento {
_elementInjectorIndex:number;
_directiveIndex:number;
callOnAllChangesDone:boolean;
callOnChange:boolean;
get name() {
return `${this._elementInjectorIndex}_${this._directiveIndex}`;
}
constructor(elementInjectorIndex:number, directiveIndex:number, callOnAllChangesDone:boolean,
callOnChange:boolean) {
this._elementInjectorIndex = elementInjectorIndex;
this._directiveIndex = directiveIndex;
this.callOnAllChangesDone = callOnAllChangesDone;
this.callOnChange = callOnChange;
}
directive(elementInjectors:List<ElementInjector>) {
var elementInjector:ElementInjector = elementInjectors[this._elementInjectorIndex];
return elementInjector.getDirectiveAtIndex(this._directiveIndex);
} }
} }

View File

@ -48,8 +48,8 @@ export class ViewFactory {
_createView(protoView:viewModule.ProtoView): viewModule.View { _createView(protoView:viewModule.ProtoView): viewModule.View {
var view = new viewModule.View(protoView, protoView.protoLocals); var view = new viewModule.View(protoView, protoView.protoLocals);
var changeDetector = protoView.protoChangeDetector.instantiate(view, protoView.bindingRecords, var changeDetector = protoView.protoChangeDetector.instantiate(view, protoView.bindings,
protoView.getVariableBindings(), protoView.getDirectiveMementos()); protoView.getVariableBindings(), protoView.getdirectiveRecords());
var binders = protoView.elementBinders; var binders = protoView.elementBinders;
var elementInjectors = ListWrapper.createFixedSize(binders.length); var elementInjectors = ListWrapper.createFixedSize(binders.length);

View File

@ -7,7 +7,7 @@ import {Parser} from 'angular2/src/change_detection/parser/parser';
import {Lexer} from 'angular2/src/change_detection/parser/lexer'; import {Lexer} from 'angular2/src/change_detection/parser/lexer';
import {Locals} from 'angular2/src/change_detection/parser/locals'; import {Locals} from 'angular2/src/change_detection/parser/locals';
import {ChangeDispatcher, DynamicChangeDetector, ChangeDetectionError, BindingRecord, import {ChangeDispatcher, DynamicChangeDetector, ChangeDetectionError, BindingRecord, DirectiveRecord,
PipeRegistry, Pipe, NO_CHANGE, CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH, DEFAULT} from 'angular2/change_detection'; PipeRegistry, Pipe, NO_CHANGE, CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH, DEFAULT} from 'angular2/change_detection';
import {JitProtoChangeDetector, DynamicProtoChangeDetector} from 'angular2/src/change_detection/proto_change_detector'; import {JitProtoChangeDetector, DynamicProtoChangeDetector} from 'angular2/src/change_detection/proto_change_detector';
@ -27,6 +27,10 @@ export function main() {
return parser.parseBinding(exp, location); return parser.parseBinding(exp, location);
} }
function dirs(directives:List) {
return new FakeDirectives(directives);
}
function convertLocalsToVariableBindings(locals) { function convertLocalsToVariableBindings(locals) {
var variableBindings = []; var variableBindings = [];
var loc = locals; var loc = locals;
@ -37,13 +41,13 @@ export function main() {
return variableBindings; return variableBindings;
} }
function createChangeDetector(memo:string, exp:string, context = null, locals = null, registry = null) { function createChangeDetector(propName:string, exp:string, context = null, locals = null, registry = null) {
var pcd = createProtoChangeDetector(registry); var pcd = createProtoChangeDetector(registry);
var dispatcher = new TestDispatcher(); var dispatcher = new TestDispatcher();
var variableBindings = convertLocalsToVariableBindings(locals); var variableBindings = convertLocalsToVariableBindings(locals);
var records = [new BindingRecord(ast(exp), memo, null)]; var records = [BindingRecord.createForElement(ast(exp), 0, propName)];
var cd = pcd.instantiate(dispatcher, records, variableBindings, []); var cd = pcd.instantiate(dispatcher, records, variableBindings, []);
cd.hydrate(context, locals, null); cd.hydrate(context, locals, null);
@ -56,9 +60,9 @@ export function main() {
return res["dispatcher"].log; return res["dispatcher"].log;
} }
function instantiate(protoChangeDetector, dispatcher, bindings, directiveMementos = null) { function instantiate(protoChangeDetector, dispatcher, bindings, directiveRecords = null) {
if (isBlank(directiveMementos)) directiveMementos = []; if (isBlank(directiveRecords)) directiveRecords = [];
return protoChangeDetector.instantiate(dispatcher, bindings, null, directiveMementos); return protoChangeDetector.instantiate(dispatcher, bindings, null, directiveRecords);
} }
describe(`${name} change detection`, () => { describe(`${name} change detection`, () => {
@ -204,7 +208,7 @@ export function main() {
var pcd = createProtoChangeDetector(); var pcd = createProtoChangeDetector();
var ast = parser.parseInterpolation("B{{a}}A", "location"); var ast = parser.parseInterpolation("B{{a}}A", "location");
var cd = instantiate(pcd, dispatcher, [new BindingRecord(ast, "memo", null)]); var cd = instantiate(pcd, dispatcher, [BindingRecord.createForElement(ast, 0, "memo")]);
cd.hydrate(new TestData("value"), null, null); cd.hydrate(new TestData("value"), null, null);
cd.detectChanges(); cd.detectChanges();
@ -241,13 +245,18 @@ export function main() {
}); });
}); });
describe("updatingDirectives", () => { describe("updating directives", () => {
var dirMemento1 = new FakeDirectiveMemento(0, true, true); var dirRecord1 = new DirectiveRecord(0, 0, true, true);
var dirMemento2 = new FakeDirectiveMemento(1, true, true); var dirRecord2 = new DirectiveRecord(0, 1, true, true);
var dirMementoNoCallbacks = new FakeDirectiveMemento(0, false, false); var dirRecordNoCallbacks = new DirectiveRecord(0, 0, false, false);
var updateA = new FakeBindingMemento((o, v) => o.a = v, "a"); function updateA(exp:string, dirRecord) {
var updateB = new FakeBindingMemento((o, v) => o.b = v, "b"); return BindingRecord.createForDirective(ast(exp), "a", (o,v) => o.a = v, dirRecord);
}
function updateB(exp:string, dirRecord) {
return BindingRecord.createForDirective(ast(exp), "b", (o,v) => o.b = v, dirRecord);
}
var directive1; var directive1;
var directive2; var directive2;
@ -260,10 +269,10 @@ export function main() {
it("should happen directly, without invoking the dispatcher", () => { it("should happen directly, without invoking the dispatcher", () => {
var pcd = createProtoChangeDetector(); var pcd = createProtoChangeDetector();
var cd = instantiate(pcd, dispatcher, [new BindingRecord(ast("42"), updateA, dirMemento1)], var cd = instantiate(pcd, dispatcher, [updateA("42", dirRecord1)],
[dirMemento1]); [dirRecord1]);
cd.hydrate(null, null, [directive1]) cd.hydrate(null, null, dirs([directive1]));
cd.detectChanges(); cd.detectChanges();
@ -276,12 +285,12 @@ export function main() {
var pcd = createProtoChangeDetector(); var pcd = createProtoChangeDetector();
var cd = instantiate(pcd, dispatcher, [ var cd = instantiate(pcd, dispatcher, [
new BindingRecord(ast("1"), updateA, dirMemento1), updateA("1", dirRecord1),
new BindingRecord(ast("2"), updateB, dirMemento1), updateB("2", dirRecord1),
new BindingRecord(ast("3"), updateA, dirMemento2) updateA("3", dirRecord2)
], [dirMemento1, dirMemento2]); ], [dirRecord1, dirRecord2]);
cd.hydrate(null, null, [directive1, directive2]) cd.hydrate(null, null, dirs([directive1, directive2]));
cd.detectChanges(); cd.detectChanges();
@ -293,10 +302,10 @@ export function main() {
var pcd = createProtoChangeDetector(); var pcd = createProtoChangeDetector();
var cd = instantiate(pcd, dispatcher, [ var cd = instantiate(pcd, dispatcher, [
new BindingRecord(ast("1"), updateA, dirMementoNoCallbacks) updateA("1", dirRecordNoCallbacks)
], [dirMementoNoCallbacks]); ], [dirRecordNoCallbacks]);
cd.hydrate(null, null, [directive1]) cd.hydrate(null, null, dirs([directive1]));
cd.detectChanges(); cd.detectChanges();
@ -308,8 +317,8 @@ export function main() {
it("should be called after processing all the children", () => { it("should be called after processing all the children", () => {
var pcd = createProtoChangeDetector(); var pcd = createProtoChangeDetector();
var cd = instantiate(pcd, dispatcher, [], [dirMemento1, dirMemento2]); var cd = instantiate(pcd, dispatcher, [], [dirRecord1, dirRecord2]);
cd.hydrate(null, null, [directive1, directive2]); cd.hydrate(null, null, dirs([directive1, directive2]));
cd.detectChanges(); cd.detectChanges();
@ -322,10 +331,10 @@ export function main() {
var pcd = createProtoChangeDetector(); var pcd = createProtoChangeDetector();
var cd = instantiate(pcd, dispatcher, [ var cd = instantiate(pcd, dispatcher, [
new BindingRecord(ast("1"), updateA, dirMementoNoCallbacks) updateA("1", dirRecordNoCallbacks)
], [dirMementoNoCallbacks]); ], [dirRecordNoCallbacks]);
cd.hydrate(null, null, [directive1]) cd.hydrate(null, null, dirs([directive1]));
cd.detectChanges(); cd.detectChanges();
@ -334,14 +343,14 @@ export function main() {
it("should be called in reverse order so the child is always notified before the parent", () => { it("should be called in reverse order so the child is always notified before the parent", () => {
var pcd = createProtoChangeDetector(); var pcd = createProtoChangeDetector();
var cd = instantiate(pcd, dispatcher, [], [dirMemento1, dirMemento2]); var cd = instantiate(pcd, dispatcher, [], [dirRecord1, dirRecord2]);
var onChangesDoneCalls = []; var onChangesDoneCalls = [];
var td1; var td1;
td1 = new TestDirective(() => ListWrapper.push(onChangesDoneCalls, td1)); td1 = new TestDirective(() => ListWrapper.push(onChangesDoneCalls, td1));
var td2; var td2;
td2 = new TestDirective(() => ListWrapper.push(onChangesDoneCalls, td2)); td2 = new TestDirective(() => ListWrapper.push(onChangesDoneCalls, td2));
cd.hydrate(null, null, [td1, td2]); cd.hydrate(null, null, dirs([td1, td2]));
cd.detectChanges(); cd.detectChanges();
@ -352,19 +361,19 @@ export function main() {
var pcd = createProtoChangeDetector(); var pcd = createProtoChangeDetector();
var shadowDomChildPCD = createProtoChangeDetector(); var shadowDomChildPCD = createProtoChangeDetector();
var parent = pcd.instantiate(dispatcher, [], null, [dirMemento1]); var parent = pcd.instantiate(dispatcher, [], null, [dirRecord1]);
var child = shadowDomChildPCD.instantiate(dispatcher, [ var child = shadowDomChildPCD.instantiate(dispatcher,
new BindingRecord(ast("1"), updateA, dirMemento1)], null, [dirMemento1]); [updateA("1", dirRecord1)], null, [dirRecord1]);
parent.addShadowDomChild(child); parent.addShadowDomChild(child);
var directiveInShadowDOm = new TestDirective(); var directiveInShadowDom = new TestDirective();
var parentDirective = new TestDirective(() => { var parentDirective = new TestDirective(() => {
expect(directiveInShadowDOm.a).toBe(null); expect(directiveInShadowDom.a).toBe(null);
}); });
parent.hydrate(null, null, [parentDirective]); parent.hydrate(null, null, dirs([parentDirective]));
child.hydrate(null, null, [directiveInShadowDOm]); child.hydrate(null, null, dirs([directiveInShadowDom]));
parent.detectChanges(); parent.detectChanges();
}); });
@ -375,11 +384,10 @@ export function main() {
describe("enforce no new changes", () => { describe("enforce no new changes", () => {
it("should throw when a record gets changed after it has been checked", () => { it("should throw when a record gets changed after it has been checked", () => {
var pcd = createProtoChangeDetector(); var pcd = createProtoChangeDetector();
pcd.addAst(ast("a"), "a", 1);
var dispatcher = new TestDispatcher(); var dispatcher = new TestDispatcher();
var cd = instantiate(pcd, dispatcher, [ var cd = instantiate(pcd, dispatcher, [
new BindingRecord(ast("a"), "a", 1) BindingRecord.createForElement(ast("a"), 0, "a")
]); ]);
cd.hydrate(new TestData('value'), null, null); cd.hydrate(new TestData('value'), null, null);
@ -394,7 +402,7 @@ export function main() {
xit("should wrap exceptions into ChangeDetectionError", () => { xit("should wrap exceptions into ChangeDetectionError", () => {
var pcd = createProtoChangeDetector(); var pcd = createProtoChangeDetector();
var cd = pcd.instantiate(new TestDispatcher(), [ var cd = pcd.instantiate(new TestDispatcher(), [
new BindingRecord(ast("invalidProp", "someComponent"), "a", 1) BindingRecord.createForElement(ast("invalidProp"), 0, "a")
], null, []); ], null, []);
cd.hydrate(null, null); cd.hydrate(null, null);
@ -836,33 +844,15 @@ class TestData {
} }
} }
class FakeDirectiveMemento { class FakeDirectives {
callOnAllChangesDone:boolean; directives:List;
callOnChange:boolean;
directiveIndex:number;
constructor(directiveIndex:number = 0, callOnAllChangesDone:boolean = false, callOnChange:boolean = false) { constructor(directives:List) {
this.directiveIndex = directiveIndex; this.directives = directives;
this.callOnAllChangesDone = callOnAllChangesDone;
this.callOnChange = callOnChange;
} }
get name() { directive(directiveRecord:DirectiveRecord) {
return this.directiveIndex; return this.directives[directiveRecord.directiveIndex];
}
directive(directives) {
return directives[this.directiveIndex];
}
}
class FakeBindingMemento {
setter:Function;
propertyName:string;
constructor(setter:Function, propertyName:string) {
this.setter = setter;
this.propertyName = propertyName;
} }
} }
@ -880,8 +870,8 @@ class TestDispatcher extends ChangeDispatcher {
this.loggedValues = ListWrapper.create(); this.loggedValues = ListWrapper.create();
} }
invokeMementoFor(memento, value) { notifyOnBinding(binding, value) {
ListWrapper.push(this.log, `${memento}=${this._asString(value)}`); ListWrapper.push(this.log, `${binding.propertyName}=${this._asString(value)}`);
ListWrapper.push(this.loggedValues, value); ListWrapper.push(this.loggedValues, value);
} }

View File

@ -6,7 +6,7 @@ import {RECORD_TYPE_SELF, ProtoRecord} from 'angular2/src/change_detection/proto
export function main() { export function main() {
function r(funcOrValue, args, contextIndex, selfIndex, lastInBinding = false) { function r(funcOrValue, args, contextIndex, selfIndex, lastInBinding = false) {
return new ProtoRecord(99, "name", funcOrValue, args, null, contextIndex, selfIndex, return new ProtoRecord(99, "name", funcOrValue, args, null, contextIndex, selfIndex,
null, null, null, lastInBinding, false); null, null, lastInBinding, false);
} }
describe("change detection - coalesce", () => { describe("change detection - coalesce", () => {
@ -74,7 +74,7 @@ export function main() {
expect(rs[1]).toEqual(new ProtoRecord( expect(rs[1]).toEqual(new ProtoRecord(
RECORD_TYPE_SELF, "self", null, RECORD_TYPE_SELF, "self", null,
[], null, 1, 2, [], null, 1, 2,
null, null, null, null, null,
true, false) true, false)
); );
}); });

View File

@ -11,7 +11,8 @@ import {
ChangeDetection, ChangeDetection,
dynamicChangeDetection, dynamicChangeDetection,
jitChangeDetection, jitChangeDetection,
BindingRecord BindingRecord,
DirectiveRecord
} from 'angular2/change_detection'; } from 'angular2/change_detection';
@ -188,26 +189,26 @@ function setUpChangeDetection(changeDetection:ChangeDetection, iterations, objec
var parentProto = changeDetection.createProtoChangeDetector('parent'); var parentProto = changeDetection.createProtoChangeDetector('parent');
var parentCd = parentProto.instantiate(dispatcher, [], [], []); var parentCd = parentProto.instantiate(dispatcher, [], [], []);
var targetObj = new Obj();
var proto = changeDetection.createProtoChangeDetector("proto"); var proto = changeDetection.createProtoChangeDetector("proto");
var directiveMemento = new FakeDirectiveMemento("target", targetObj); var directiveRecord = new DirectiveRecord(0, 0, false, false);
var bindingRecords = [ var bindings = [
new BindingRecord(parser.parseBinding('field0', null), new FakeBindingMemento(reflector.setter("field0"), "field0"), directiveMemento), BindingRecord.createForDirective(parser.parseBinding('field0', null), "field0", reflector.setter("field0"), directiveRecord),
new BindingRecord(parser.parseBinding('field1', null), new FakeBindingMemento(reflector.setter("field1"), "field1"), directiveMemento), BindingRecord.createForDirective(parser.parseBinding('field1', null), "field1", reflector.setter("field1"), directiveRecord),
new BindingRecord(parser.parseBinding('field2', null), new FakeBindingMemento(reflector.setter("field2"), "field2"), directiveMemento), BindingRecord.createForDirective(parser.parseBinding('field2', null), "field2", reflector.setter("field2"), directiveRecord),
new BindingRecord(parser.parseBinding('field3', null), new FakeBindingMemento(reflector.setter("field3"), "field3"), directiveMemento), BindingRecord.createForDirective(parser.parseBinding('field3', null), "field3", reflector.setter("field3"), directiveRecord),
new BindingRecord(parser.parseBinding('field4', null), new FakeBindingMemento(reflector.setter("field4"), "field4"), directiveMemento), BindingRecord.createForDirective(parser.parseBinding('field4', null), "field4", reflector.setter("field4"), directiveRecord),
new BindingRecord(parser.parseBinding('field5', null), new FakeBindingMemento(reflector.setter("field5"), "field5"), directiveMemento), BindingRecord.createForDirective(parser.parseBinding('field5', null), "field5", reflector.setter("field5"), directiveRecord),
new BindingRecord(parser.parseBinding('field6', null), new FakeBindingMemento(reflector.setter("field6"), "field6"), directiveMemento), BindingRecord.createForDirective(parser.parseBinding('field6', null), "field6", reflector.setter("field6"), directiveRecord),
new BindingRecord(parser.parseBinding('field7', null), new FakeBindingMemento(reflector.setter("field7"), "field7"), directiveMemento), BindingRecord.createForDirective(parser.parseBinding('field7', null), "field7", reflector.setter("field7"), directiveRecord),
new BindingRecord(parser.parseBinding('field8', null), new FakeBindingMemento(reflector.setter("field8"), "field8"), directiveMemento), BindingRecord.createForDirective(parser.parseBinding('field8', null), "field8", reflector.setter("field8"), directiveRecord),
new BindingRecord(parser.parseBinding('field9', null), new FakeBindingMemento(reflector.setter("field9"), "field9"), directiveMemento) BindingRecord.createForDirective(parser.parseBinding('field9', null), "field9", reflector.setter("field9"), directiveRecord)
]; ];
var targetObj = new Obj();
for (var i = 0; i < iterations; ++i) { for (var i = 0; i < iterations; ++i) {
var cd = proto.instantiate(dispatcher, bindingRecords, [], [directiveMemento]); var cd = proto.instantiate(dispatcher, bindings, [], [directiveRecord]);
cd.hydrate(object, null, null); cd.hydrate(object, null, new FakeDirectives(targetObj));
parentCd.addChild(cd); parentCd.addChild(cd);
} }
return parentCd; return parentCd;
@ -298,36 +299,20 @@ export function main () {
} }
} }
class FakeBindingMemento { class FakeDirectives {
setter:Function;
propertyName:string;
constructor(setter:Function, propertyName:string) {
this.setter = setter;
this.propertyName = propertyName;
}
}
class FakeDirectiveMemento {
targetObj:Obj; targetObj:Obj;
name:string;
callOnChange:boolean;
callOnAllChangesDone:boolean;
constructor(name, targetObj) { constructor(targetObj) {
this.targetObj = targetObj; this.targetObj = targetObj;
this.name = name;
this.callOnChange = false;
this.callOnAllChangesDone = false;
} }
directive(dirs) { directive(record) {
return this.targetObj; return this.targetObj;
} }
} }
class DummyDispatcher extends ChangeDispatcher { class DummyDispatcher extends ChangeDispatcher {
invokeMementoFor(bindingMemento, newValue) { notifyOnBinding(bindingRecord, newValue) {
throw "Should not be used"; throw "Should not be used";
} }
} }