feat(change_detection): updated change detection to update directive directly, without the dispatcher

This commit is contained in:
vsavkin
2015-04-01 15:49:14 -07:00
parent 50098767fc
commit 69c3bff086
8 changed files with 322 additions and 177 deletions

View File

@ -5,6 +5,6 @@ class ChangeDetectorJITGenerator {
}
generate() {
throw "Not supported in Dart";
throw "Jit Change Detection is not supported in Dart";
}
}

View File

@ -76,16 +76,23 @@ function pipeOnDestroyTemplate(pipeNames:List) {
return pipeNames.map((p) => `${p}.onDestroy()`).join("\n");
}
function hydrateTemplate(type:string, mode:string, fieldsDefinitions:string, pipeOnDestroy:string):string {
function hydrateTemplate(type:string, mode:string, fieldDefinitions:string, pipeOnDestroy:string,
directiveFieldNames:List<String>):string {
var directiveInit = "";
for(var i = 0; i < directiveFieldNames.length; ++i) {
directiveInit += `${directiveFieldNames[i]} = this.directiveMementos[${i}].directive(directives);\n`;
}
return `
${type}.prototype.hydrate = function(context, locals) {
${type}.prototype.hydrate = function(context, locals, directives) {
${MODE_ACCESSOR} = "${mode}";
${CONTEXT_ACCESSOR} = context;
${LOCALS_ACCESSOR} = locals;
${directiveInit}
}
${type}.prototype.dehydrate = function() {
${pipeOnDestroy}
${fieldsDefinitions}
${fieldDefinitions}
${LOCALS_ACCESSOR} = null;
}
${type}.prototype.hydrated = function() {
@ -110,8 +117,8 @@ ${type}.prototype.callOnAllChangesDone = function() {
`;
}
function onAllChangesDoneTemplate(index:number):string {
return `${DISPATCHER_ACCESSOR}.onAllChangesDone(${MEMENTOS_ACCESSOR}[${index}]);`;
function onAllChangesDoneTemplate(directive:string):string {
return `${directive}.onAllChangesDone();`;
}
@ -130,7 +137,7 @@ ${records}
}
function pipeCheckTemplate(protoIndex:number, context:string, bindingPropagationConfig:string, pipe:string, pipeType:string,
oldValue:string, newValue:string, change:string, invokeMementoAndAddChange:string,
oldValue:string, newValue:string, change:string, invokeMemento:string,
addToChanges, lastInDirective:string):string{
return `
${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}];
@ -144,7 +151,7 @@ if (${pipe} === ${UTIL}.unitialized()) {
${newValue} = ${pipe}.transform(${context});
if (! ${UTIL}.noChangeMarker(${newValue})) {
${change} = true;
${invokeMementoAndAddChange}
${invokeMemento}
${addToChanges}
${oldValue} = ${newValue};
}
@ -153,13 +160,13 @@ ${lastInDirective}
}
function referenceCheckTemplate(protoIndex:number, assignment:string, oldValue:string, newValue:string, change:string,
invokeMementoAndAddChange:string, addToChanges:string, lastInDirective:string):string {
invokeMemento:string, addToChanges:string, lastInDirective:string):string {
return `
${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}];
${assignment}
if (${newValue} !== ${oldValue} || (${newValue} !== ${newValue}) && (${oldValue} !== ${oldValue})) {
${change} = true;
${invokeMementoAndAddChange}
${invokeMemento}
${addToChanges}
${oldValue} = ${newValue};
}
@ -196,20 +203,26 @@ function addToChangesTemplate(oldValue:string, newValue:string):string {
return `${CHANGES_LOCAL} = ${UTIL}.addChange(${CHANGES_LOCAL}, ${CURRENT_PROTO}.bindingMemento, ${UTIL}.simpleChange(${oldValue}, ${newValue}));`;
}
function invokeBindingMemento(oldValue:string, newValue:string):string {
function updateDirectiveTemplate(oldValue:string, newValue:string, directiveProperty:string):string {
return `
if(throwOnChange) ${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue}));
${directiveProperty} = ${newValue};
`;
}
function updateElementTemplate(oldValue:string, newValue:string):string {
return `
if(throwOnChange) ${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue}));
${DISPATCHER_ACCESSOR}.invokeMementoFor(${CURRENT_PROTO}.bindingMemento, ${newValue});
`;
}
function lastInDirectiveTemplate(protoIndex:number):string{
function notifyOnChangesTemplate(directive:string):string{
return `
if (${CHANGES_LOCAL}) {
${DISPATCHER_ACCESSOR}.onChange(${PROTOS_ACCESSOR}[${protoIndex}].directiveMemento, ${CHANGES_LOCAL});
if(${CHANGES_LOCAL}) {
${directive}.onChange(${CHANGES_LOCAL});
${CHANGES_LOCAL} = null;
}
${CHANGES_LOCAL} = null;
`;
}
@ -271,13 +284,22 @@ export class ChangeDetectorJITGenerator {
genHydrate():string {
var mode = ChangeDetectionUtil.changeDetectionMode(this.changeDetectionStrategy);
return hydrateTemplate(this.typeName, mode, this.genFieldDefinitions(),
pipeOnDestroyTemplate(this.getNonNullPipeNames()));
pipeOnDestroyTemplate(this.getNonNullPipeNames()), this.getDirectiveFieldNames());
}
getDirectiveFieldNames():List<string> {
return this.directiveMementos.map((d) => this.getDirective(d));
}
getDirective(memento) {
return `this.directive_${memento.name}`;
}
genFieldDefinitions() {
var fields = [];
fields = fields.concat(this.fieldNames);
fields = fields.concat(this.getNonNullPipeNames());
fields = fields.concat(this.getDirectiveFieldNames());
return fieldDefinitionsTemplate(fields);
}
@ -303,7 +325,8 @@ export class ChangeDetectorJITGenerator {
for (var i = mementos.length - 1; i >= 0; --i) {
var memento = mementos[i];
if (memento.callOnAllChangesDone) {
notifications.push(onAllChangesDoneTemplate(i));
var directive = `this.directive_${memento.name}`;
notifications.push(onAllChangesDoneTemplate(directive));
}
}
@ -340,9 +363,9 @@ export class ChangeDetectorJITGenerator {
var pipe = this.pipeNames[r.selfIndex];
var bpc = r.mode === RECORD_TYPE_BINDING_PIPE ? "this.bindingPropagationConfig" : "null";
var invokeMemento = this.getInvokeMementoAndAddChangeTemplate(r);
var invokeMemento = this.genUpdateDirectiveOrElement(r);
var addToChanges = this.genAddToChanges(r);
var lastInDirective = this.genLastInDirective(r);
var lastInDirective = this.genNotifyOnChanges(r);
return pipeCheckTemplate(r.selfIndex - 1, context, bpc, pipe, r.name, oldValue, newValue, change,
invokeMemento, addToChanges, lastInDirective);
@ -354,9 +377,9 @@ export class ChangeDetectorJITGenerator {
var change = this.changeNames[r.selfIndex];
var assignment = this.genUpdateCurrentValue(r);
var invokeMemento = this.getInvokeMementoAndAddChangeTemplate(r);
var invokeMemento = this.genUpdateDirectiveOrElement(r);
var addToChanges = this.genAddToChanges(r);
var lastInDirective = this.genLastInDirective(r);
var lastInDirective = this.genNotifyOnChanges(r);
var check = referenceCheckTemplate(r.selfIndex - 1, assignment, oldValue, newValue, change,
invokeMemento, addToChanges, lastInDirective);
@ -426,10 +449,18 @@ export class ChangeDetectorJITGenerator {
return JSON.stringify(value);
}
getInvokeMementoAndAddChangeTemplate(r:ProtoRecord):string {
genUpdateDirectiveOrElement(r:ProtoRecord):string {
if (! r.lastInBinding) return "";
var newValue = this.localNames[r.selfIndex];
var oldValue = this.fieldNames[r.selfIndex];
return r.lastInBinding ? invokeBindingMemento(oldValue, newValue) : "";
if (isPresent(r.directiveMemento)) {
var directiveProperty = `${this.getDirective(r.directiveMemento)}.${r.bindingMemento.propertyName}`;
return updateDirectiveTemplate(oldValue, newValue, directiveProperty);
} else {
return updateElementTemplate(oldValue, newValue);
}
}
genAddToChanges(r:ProtoRecord):string {
@ -439,9 +470,13 @@ export class ChangeDetectorJITGenerator {
return callOnChange ? addToChangesTemplate(oldValue, newValue) : "";
}
genLastInDirective(r:ProtoRecord):string{
genNotifyOnChanges(r:ProtoRecord):string{
var callOnChange = r.directiveMemento && r.directiveMemento.callOnChange;
return r.lastInDirective && callOnChange ? lastInDirectiveTemplate(r.selfIndex - 1) : '';
if (r.lastInDirective && callOnChange) {
return notifyOnChangesTemplate(this.getDirective(r.directiveMemento));
} else {
return "";
}
}
genArgs(r:ProtoRecord):string {

View File

@ -34,6 +34,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
prevContexts:List;
protos:List<ProtoRecord>;
directives:any;
directiveMementos:List;
changeControlStrategy:string;
@ -53,16 +54,18 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
ListWrapper.fill(this.prevContexts, uninitialized);
ListWrapper.fill(this.changes, false);
this.locals = null;
this.directives = null;
this.protos = protoRecords;
this.directiveMementos = directiveMementos;
this.changeControlStrategy = changeControlStrategy;
}
hydrate(context:any, locals:any) {
hydrate(context:any, locals:any, directives:any) {
this.mode = ChangeDetectionUtil.changeDetectionMode(this.changeControlStrategy);
this.values[0] = context;
this.locals = locals;
this.directives = directives;
}
dehydrate() {
@ -90,29 +93,18 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
var protos:List<ProtoRecord> = this.protos;
var changes = null;
var currentDirectiveMemento = null;
for (var i = 0; i < protos.length; ++i) {
var proto:ProtoRecord = protos[i];
if (isBlank(currentDirectiveMemento)) {
currentDirectiveMemento = proto.directiveMemento;
}
var change = this._check(proto);
if (isPresent(change)) {
if (throwOnChange) ChangeDetectionUtil.throwOnChange(proto, change);
this.dispatcher.invokeMementoFor(proto.bindingMemento, change.currentValue);
if (isPresent(currentDirectiveMemento) && currentDirectiveMemento.callOnChange) {
changes = ChangeDetectionUtil.addChange(changes, proto.bindingMemento, change);
}
this._updateDirectiveOrElement(change, proto.directiveMemento, proto.bindingMemento);
changes = this._addChange(proto.directiveMemento, proto.bindingMemento, change, changes);
}
if (proto.lastInDirective) {
if (isPresent(changes)) {
this.dispatcher.onChange(currentDirectiveMemento, changes);
}
currentDirectiveMemento = null;
if (proto.lastInDirective && isPresent(changes)) {
this._directive(proto.directiveMemento).onChange(changes);
changes = null;
}
}
@ -123,11 +115,31 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
for (var i = mementos.length - 1; i >= 0; --i) {
var memento = mementos[i];
if (memento.callOnAllChangesDone) {
this.dispatcher.onAllChangesDone(memento);
this._directive(memento).onAllChangesDone();
}
}
}
_updateDirectiveOrElement(change, directiveMemento, bindingMemento) {
if (isBlank(directiveMemento)) {
this.dispatcher.invokeMementoFor(bindingMemento, change.currentValue);
} else {
bindingMemento.setter(this._directive(directiveMemento), change.currentValue);
}
}
_addChange(directiveMemento, bindingMemento, change, changes) {
if (isPresent(directiveMemento) && directiveMemento.callOnChange) {
return ChangeDetectionUtil.addChange(changes, bindingMemento, change);
} else {
return changes;
}
}
_directive(memento) {
return memento.directive(this.directives);
}
_check(proto:ProtoRecord) {
try {
if (proto.mode === RECORD_TYPE_PIPE || proto.mode === RECORD_TYPE_BINDING_PIPE) {

View File

@ -1,16 +1,17 @@
import {List} from 'angular2/src/facade/collection';
import {Locals} from './parser/locals';
import {AST} from './parser/ast';
import {DEFAULT} from './constants';
export class ProtoChangeDetector {
addAst(ast:AST, bindingMemento:any, directiveMemento:any = null){}
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveMemento:List):ChangeDetector{
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveMementos:List):ChangeDetector{
return null;
}
}
export class ChangeDetection {
createProtoChangeDetector(name:string, changeControlStrategy:string):ProtoChangeDetector{
createProtoChangeDetector(name:string, changeControlStrategy:string=DEFAULT):ProtoChangeDetector{
return null;
}
}
@ -35,7 +36,7 @@ export class ChangeRecord {
}
export class ChangeDispatcher {
onRecordChange(directiveMemento, records:List<ChangeRecord>) {}
invokeMementoFor(memento:any, value) {}
}
export class ChangeDetector {
@ -45,7 +46,7 @@ export class ChangeDetector {
addChild(cd:ChangeDetector) {}
removeChild(cd:ChangeDetector) {}
remove() {}
hydrate(context:any, locals:Locals) {}
hydrate(context:any, locals:Locals, directives:any) {}
dehydrate() {}
markPathToRootAsCheckOnce() {}