refactor(change_detect): Flatten Js change detector template

Update the `ChangeDetectionJITGenerator` for clarity and similarity with
the upcoming Dart generated `ChangeDetector` classes.
This commit is contained in:
Tim Blasi 2015-05-27 11:03:20 -07:00
parent 4a3fd5e855
commit a2770c8a52
1 changed files with 244 additions and 367 deletions

View File

@ -1,4 +1,4 @@
import {isPresent, isBlank, BaseException, Type} from 'angular2/src/facade/lang';
import {BaseException, Type, isBlank, isPresent} from 'angular2/src/facade/lang';
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {AbstractChangeDetector} from './abstract_change_detector';
@ -25,10 +25,6 @@ import {
* The code generator takes a list of proto records and creates a function/class
* that "emulates" what the developer would write by hand to implement the same
* kind of behaviour.
*
* The implementation comprises two parts:
* * ChangeDetectorJITGenerator has the logic of how everything fits together.
* * template functions (e.g., constructorTemplate) define what code is generated.
*/
var ABSTRACT_CHANGE_DETECTOR = "AbstractChangeDetector";
var UTIL = "ChangeDetectionUtil";
@ -41,240 +37,24 @@ var IS_CHANGED_LOCAL = "isChanged";
var CHANGES_LOCAL = "changes";
var LOCALS_ACCESSOR = "this.locals";
var MODE_ACCESSOR = "this.mode";
var TEMP_LOCAL = "temp";
var CURRENT_PROTO = "currentProto";
function typeTemplate(type: string, cons: string, detectChanges: string,
notifyOnAllChangesDone: string, setContext: string): string {
return `
${cons}
${detectChanges}
${notifyOnAllChangesDone}
${setContext};
return function(dispatcher, pipeRegistry) {
return new ${type}(dispatcher, pipeRegistry, protos, directiveRecords);
}
`;
}
function constructorTemplate(type: string, fieldsDefinitions: string): string {
return `
var ${type} = function ${type}(dispatcher, pipeRegistry, protos, directiveRecords) {
${ABSTRACT_CHANGE_DETECTOR}.call(this);
${DISPATCHER_ACCESSOR} = dispatcher;
${PIPE_REGISTRY_ACCESSOR} = pipeRegistry;
${PROTOS_ACCESSOR} = protos;
${DIRECTIVES_ACCESSOR} = directiveRecords;
${LOCALS_ACCESSOR} = null;
${fieldsDefinitions}
}
${type}.prototype = Object.create(${ABSTRACT_CHANGE_DETECTOR}.prototype);
`;
}
function pipeOnDestroyTemplate(pipeNames: List<any>) {
return pipeNames.map((p) => `${p}.onDestroy()`).join("\n");
}
function hydrateTemplate(type: string, mode: string, fieldDefinitions: string,
pipeOnDestroy: string, directiveFieldNames: List<String>,
detectorFieldNames: List<String>): string {
var directiveInit = "";
for (var i = 0; i < directiveFieldNames.length; ++i) {
directiveInit +=
`${directiveFieldNames[i]} = directives.getDirectiveFor(this.directiveRecords[${i}].directiveIndex);\n`;
}
var detectorInit = "";
for (var i = 0; i < detectorFieldNames.length; ++i) {
detectorInit +=
`${detectorFieldNames[i]} = directives.getDetectorFor(this.directiveRecords[${i}].directiveIndex);\n`;
}
return `
${type}.prototype.hydrate = function(context, locals, directives) {
${MODE_ACCESSOR} = "${mode}";
${CONTEXT_ACCESSOR} = context;
${LOCALS_ACCESSOR} = locals;
${directiveInit}
${detectorInit}
}
${type}.prototype.dehydrate = function() {
${pipeOnDestroy}
${fieldDefinitions}
${LOCALS_ACCESSOR} = null;
}
${type}.prototype.hydrated = function() {
return ${CONTEXT_ACCESSOR} !== ${UTIL}.uninitialized();
}
`;
}
function detectChangesTemplate(type: string, body: string): string {
return `
${type}.prototype.detectChangesInRecords = function(throwOnChange) {
${body}
}
`;
}
function callOnAllChangesDoneTemplate(type: string, body: string): string {
return `
${type}.prototype.callOnAllChangesDone = function() {
${body}
}
`;
}
function onAllChangesDoneTemplate(directive: string): string {
return `${directive}.onAllChangesDone();`;
}
function detectChangesBodyTemplate(localDefinitions: string, changeDefinitions: string,
records: string): string {
return `
${localDefinitions}
${changeDefinitions}
var ${TEMP_LOCAL};
var ${IS_CHANGED_LOCAL} = false;
var ${CURRENT_PROTO};
var ${CHANGES_LOCAL} = null;
context = ${CONTEXT_ACCESSOR};
${records}
`;
}
function pipeCheckTemplate(protoIndex: number, context: string, bindingPropagationConfig: string,
pipe: string, pipeType: string, oldValue: string, newValue: string,
change: string, update: string, addToChanges,
lastInDirective: string): string {
return `
${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}];
if (${pipe} === ${UTIL}.uninitialized()) {
${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context}, ${bindingPropagationConfig});
} else if (!${pipe}.supports(${context})) {
${pipe}.onDestroy();
${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context}, ${bindingPropagationConfig});
}
${newValue} = ${pipe}.transform(${context});
if (${oldValue} !== ${newValue}) {
${newValue} = ${UTIL}.unwrapValue(${newValue});
${change} = true;
${update}
${addToChanges}
${oldValue} = ${newValue};
}
${lastInDirective}
`;
}
function referenceCheckTemplate(protoIndex: number, assignment: string, oldValue: string,
newValue: string, change: string, update: string,
addToChanges: string, lastInDirective: string): string {
return `
${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}];
${assignment}
if (${newValue} !== ${oldValue} || (${newValue} !== ${newValue}) && (${oldValue} !== ${oldValue})) {
${change} = true;
${update}
${addToChanges}
${oldValue} = ${newValue};
}
${lastInDirective}
`;
}
function assignmentTemplate(field: string, value: string) {
return `${field} = ${value};`;
}
function localDefinitionsTemplate(names: List<any>): string {
return names.map((n) => `var ${n};`).join("\n");
}
function changeDefinitionsTemplate(names: List<any>): string {
return names.map((n) => `var ${n} = false;`).join("\n");
}
function fieldDefinitionsTemplate(names: List<any>): string {
return names.map((n) => `${n} = ${UTIL}.uninitialized();`).join("\n");
}
function ifChangedGuardTemplate(changeNames: List<any>, body: string): string {
var cond = changeNames.join(" || ");
return `
if (${cond}) {
${body}
}
`;
}
function addToChangesTemplate(oldValue: string, newValue: string): string {
return `${CHANGES_LOCAL} = ${UTIL}.addChange(${CHANGES_LOCAL}, ${CURRENT_PROTO}.bindingRecord.propertyName, ${UTIL}.simpleChange(${oldValue}, ${newValue}));`;
}
function updateDirectiveTemplate(oldValue: string, newValue: string,
directiveProperty: string): string {
return `
if(throwOnChange) ${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue}));
${directiveProperty} = ${newValue};
${IS_CHANGED_LOCAL} = true;
`;
}
function updateElementTemplate(oldValue: string, newValue: string): string {
return `
if(throwOnChange) ${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue}));
${DISPATCHER_ACCESSOR}.notifyOnBinding(${CURRENT_PROTO}.bindingRecord, ${newValue});
`;
}
function notifyOnChangesTemplate(directive: string): string {
return `
if(${CHANGES_LOCAL}) {
${directive}.onChange(${CHANGES_LOCAL});
${CHANGES_LOCAL} = null;
}
`;
}
function notifyOnPushDetectorsTemplate(detector: string): string {
return `
if(${IS_CHANGED_LOCAL}) {
${detector}.markAsCheckOnce();
}
`;
}
function lastInDirectiveTemplate(notifyOnChanges: string, notifyOnPush: string): string {
return `
${notifyOnChanges}
${notifyOnPush}
${IS_CHANGED_LOCAL} = false;
`;
}
export class ChangeDetectorJITGenerator {
localNames: List<string>;
changeNames: List<string>;
fieldNames: List<string>;
pipeNames: List<string>;
_localNames: List<string>;
_changeNames: List<string>;
_fieldNames: List<string>;
_pipeNames: List<string>;
constructor(public typeName: string, public changeDetectionStrategy: string,
public records: List<ProtoRecord>, public directiveRecords: List<any>) {
this.localNames = this.getLocalNames(records);
this.changeNames = this.getChangeNames(this.localNames);
this.fieldNames = this.getFieldNames(this.localNames);
this.pipeNames = this.getPipeNames(this.localNames);
this._localNames = this._getLocalNames(records);
this._changeNames = this._getChangeNames(this._localNames);
this._fieldNames = this._getFieldNames(this._localNames);
this._pipeNames = this._getPipeNames(this._localNames);
}
getLocalNames(records: List<ProtoRecord>): List<string> {
_getLocalNames(records: List<ProtoRecord>): List<string> {
var index = 0;
var names = records.map((r) => {
var sanitizedName = r.name.replace(new RegExp("\\W", "g"), '');
@ -283,252 +63,349 @@ export class ChangeDetectorJITGenerator {
return ["context"].concat(names);
}
getChangeNames(localNames: List<string>): List<string> {
return localNames.map((n) => `change_${n}`);
_getChangeNames(_localNames: List<string>): List<string> {
return _localNames.map((n) => `change_${n}`);
}
getFieldNames(localNames: List<string>): List<string> {
return localNames.map((n) => `this.${n}`);
_getFieldNames(_localNames: List<string>): List<string> {
return _localNames.map((n) => `this.${n}`);
}
getPipeNames(localNames: List<string>): List<string> {
return localNames.map((n) => `this.${n}_pipe`);
_getPipeNames(_localNames: List<string>): List<string> {
return _localNames.map((n) => `this.${n}_pipe`);
}
generate(): Function {
var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(),
this.genCallOnAllChangesDone(), this.genHydrate());
var classDefinition = `
var ${this.typeName} = function ${this.typeName}(dispatcher, pipeRegistry, protos, directiveRecords) {
${ABSTRACT_CHANGE_DETECTOR}.call(this);
${DISPATCHER_ACCESSOR} = dispatcher;
${PIPE_REGISTRY_ACCESSOR} = pipeRegistry;
${PROTOS_ACCESSOR} = protos;
${DIRECTIVES_ACCESSOR} = directiveRecords;
${LOCALS_ACCESSOR} = null;
${this._genFieldDefinitions()}
}
${this.typeName}.prototype = Object.create(${ABSTRACT_CHANGE_DETECTOR}.prototype);
${this.typeName}.prototype.detectChangesInRecords = function(throwOnChange) {
${this._genLocalDefinitions()}
${this._genChangeDefinitions()}
var ${IS_CHANGED_LOCAL} = false;
var ${CURRENT_PROTO};
var ${CHANGES_LOCAL} = null;
context = ${CONTEXT_ACCESSOR};
${this.records.map((r) => this._genRecord(r)).join("\n")}
}
${this.typeName}.prototype.callOnAllChangesDone = function() {
${this._genCallOnAllChangesDoneBody()}
}
${this.typeName}.prototype.hydrate = function(context, locals, directives) {
${MODE_ACCESSOR} = "${ChangeDetectionUtil.changeDetectionMode(this.changeDetectionStrategy)}";
${CONTEXT_ACCESSOR} = context;
${LOCALS_ACCESSOR} = locals;
${this._genHydrateDirectives()}
${this._genHydrateDetectors()}
}
${this.typeName}.prototype.dehydrate = function() {
${this._genPipeOnDestroy()}
${this._genFieldDefinitions()}
${LOCALS_ACCESSOR} = null;
}
${this.typeName}.prototype.hydrated = function() {
return ${CONTEXT_ACCESSOR} !== ${UTIL}.uninitialized();
}
return function(dispatcher, pipeRegistry) {
return new ${this.typeName}(dispatcher, pipeRegistry, protos, directiveRecords);
}
`;
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos',
'directiveRecords', text)(AbstractChangeDetector, ChangeDetectionUtil,
this.records, this.directiveRecords);
'directiveRecords', classDefinition)(
AbstractChangeDetector, ChangeDetectionUtil, this.records, this.directiveRecords);
}
genConstructor(): string {
return constructorTemplate(this.typeName, this.genFieldDefinitions());
_genGetDirectiveFieldNames(): List<string> {
return this.directiveRecords.map((d) => this._genGetDirective(d.directiveIndex));
}
genHydrate(): string {
var mode = ChangeDetectionUtil.changeDetectionMode(this.changeDetectionStrategy);
return hydrateTemplate(this.typeName, mode, this.genFieldDefinitions(),
pipeOnDestroyTemplate(this.getNonNullPipeNames()),
this.getDirectiveFieldNames(), this.getDetectorFieldNames());
}
getDirectiveFieldNames(): List<string> {
return this.directiveRecords.map((d) => this.getDirective(d.directiveIndex));
}
getDetectorFieldNames(): List<string> {
_genGetDetectorFieldNames(): List<string> {
return this.directiveRecords.filter(r => r.isOnPushChangeDetection())
.map((d) => this.getDetector(d.directiveIndex));
.map((d) => this._genGetDetector(d.directiveIndex));
}
getDirective(d: DirectiveIndex) { return `this.directive_${d.name}`; }
_genGetDirective(d: DirectiveIndex) { return `this.directive_${d.name}`; }
getDetector(d: DirectiveIndex) { return `this.detector_${d.name}`; }
_genGetDetector(d: DirectiveIndex) { return `this.detector_${d.name}`; }
genFieldDefinitions() {
var fields = [];
fields = fields.concat(this.fieldNames);
fields = fields.concat(this.getNonNullPipeNames());
fields = fields.concat(this.getDirectiveFieldNames());
fields = fields.concat(this.getDetectorFieldNames());
return fieldDefinitionsTemplate(fields);
}
getNonNullPipeNames(): List<string> {
_getNonNullPipeNames(): List<string> {
var pipes = [];
this.records.forEach((r) => {
if (r.mode === RECORD_TYPE_PIPE || r.mode === RECORD_TYPE_BINDING_PIPE) {
pipes.push(this.pipeNames[r.selfIndex]);
pipes.push(this._pipeNames[r.selfIndex]);
}
});
return pipes;
}
genDetectChanges(): string {
var body = this.genDetectChangesBody();
return detectChangesTemplate(this.typeName, body);
_genFieldDefinitions() {
var fields = [];
fields = fields.concat(this._fieldNames);
fields = fields.concat(this._getNonNullPipeNames());
fields = fields.concat(this._genGetDirectiveFieldNames());
fields = fields.concat(this._genGetDetectorFieldNames());
return fields.map((n) => `${n} = ${UTIL}.uninitialized();`).join("\n");
}
genCallOnAllChangesDone(): string {
_genHydrateDirectives(): string {
var directiveFieldNames = this._genGetDirectiveFieldNames();
var lines = ListWrapper.createFixedSize(directiveFieldNames.length);
for (var i = 0, iLen = directiveFieldNames.length; i < iLen; ++i) {
lines[i] =
`${directiveFieldNames[i]} = directives.getDirectiveFor(${DIRECTIVES_ACCESSOR}[${i}].directiveIndex);`
}
return lines.join('\n');
}
_genHydrateDetectors(): string {
var detectorFieldNames = this._genGetDetectorFieldNames();
var lines = ListWrapper.createFixedSize(detectorFieldNames.length);
for (var i = 0, iLen = detectorFieldNames.length; i < iLen; ++i) {
lines[i] = `${detectorFieldNames[i]} =
directives.getDetectorFor(${DIRECTIVES_ACCESSOR}[${i}].directiveIndex);`
}
return lines.join('\n');
}
_genPipeOnDestroy(): string {
return this._getNonNullPipeNames().map((p) => `${p}.onDestroy();`).join("\n");
}
_genCallOnAllChangesDoneBody(): string {
var notifications = [];
var dirs = this.directiveRecords;
for (var i = dirs.length - 1; i >= 0; --i) {
var dir = dirs[i];
if (dir.callOnAllChangesDone) {
var directive = `this.directive_${dir.directiveIndex.name}`;
notifications.push(onAllChangesDoneTemplate(directive));
notifications.push(`${this._genGetDirective(dir.directiveIndex)}.onAllChangesDone();`);
}
}
return callOnAllChangesDoneTemplate(this.typeName, notifications.join(";\n"));
return notifications.join("\n");
}
genDetectChangesBody(): string {
var rec = this.records.map((r) => this.genRecord(r)).join("\n");
return detectChangesBodyTemplate(this.genLocalDefinitions(), this.genChangeDefinitions(), rec);
_genLocalDefinitions(): string { return this._localNames.map((n) => `var ${n};`).join("\n"); }
_genChangeDefinitions(): string {
return this._changeNames.map((n) => `var ${n} = false;`).join("\n");
}
genLocalDefinitions(): string { return localDefinitionsTemplate(this.localNames); }
genChangeDefinitions(): string { return changeDefinitionsTemplate(this.changeNames); }
genRecord(r: ProtoRecord): string {
_genRecord(r: ProtoRecord): string {
if (r.mode === RECORD_TYPE_PIPE || r.mode === RECORD_TYPE_BINDING_PIPE) {
return this.genPipeCheck(r);
return this._genPipeCheck(r);
} else {
return this.genReferenceCheck(r);
return this._genReferenceCheck(r);
}
}
genPipeCheck(r: ProtoRecord): string {
var context = this.localNames[r.contextIndex];
var oldValue = this.fieldNames[r.selfIndex];
var newValue = this.localNames[r.selfIndex];
var change = this.changeNames[r.selfIndex];
_genPipeCheck(r: ProtoRecord): string {
var context = this._localNames[r.contextIndex];
var oldValue = this._fieldNames[r.selfIndex];
var newValue = this._localNames[r.selfIndex];
var change = this._changeNames[r.selfIndex];
var pipe = this.pipeNames[r.selfIndex];
var pipe = this._pipeNames[r.selfIndex];
var cdRef = r.mode === RECORD_TYPE_BINDING_PIPE ? "this.ref" : "null";
var update = this.genUpdateDirectiveOrElement(r);
var addToChanges = this.genAddToChanges(r);
var lastInDirective = this.genLastInDirective(r);
var protoIndex = r.selfIndex - 1;
var pipeType = r.name;
return pipeCheckTemplate(r.selfIndex - 1, context, cdRef, pipe, r.name, oldValue, newValue,
change, update, addToChanges, lastInDirective);
return `
${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}];
if (${pipe} === ${UTIL}.uninitialized()) {
${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context}, ${cdRef});
} else if (!${pipe}.supports(${context})) {
${pipe}.onDestroy();
${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context}, ${cdRef});
}
${newValue} = ${pipe}.transform(${context});
if (${oldValue} !== ${newValue}) {
${newValue} = ${UTIL}.unwrapValue(${newValue});
${change} = true;
${this._genUpdateDirectiveOrElement(r)}
${this._genAddToChanges(r)}
${oldValue} = ${newValue};
}
${this._genLastInDirective(r)}
`;
}
genReferenceCheck(r: ProtoRecord): string {
var oldValue = this.fieldNames[r.selfIndex];
var newValue = this.localNames[r.selfIndex];
var change = this.changeNames[r.selfIndex];
var assignment = this.genUpdateCurrentValue(r);
_genReferenceCheck(r: ProtoRecord): string {
var oldValue = this._fieldNames[r.selfIndex];
var newValue = this._localNames[r.selfIndex];
var update = this.genUpdateDirectiveOrElement(r);
var addToChanges = this.genAddToChanges(r);
var lastInDirective = this.genLastInDirective(r);
var protoIndex = r.selfIndex - 1;
var check = `
${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}];
${this._genUpdateCurrentValue(r)}
if (${newValue} !== ${oldValue}) {
${this._changeNames[r.selfIndex]} = true;
${this._genUpdateDirectiveOrElement(r)}
${this._genAddToChanges(r)}
${oldValue} = ${newValue};
}
${this._genLastInDirective(r)}
`;
var check = referenceCheckTemplate(r.selfIndex - 1, assignment, oldValue, newValue, change,
update, addToChanges, lastInDirective);
if (r.isPureFunction()) {
return this.ifChangedGuard(r, check);
var condition = `${this._changeNames.join(" || ")}`;
return `if (${condition}) { ${check} }`;
} else {
return check;
}
}
genUpdateCurrentValue(r: ProtoRecord): string {
var context = this.getContext(r);
var newValue = this.localNames[r.selfIndex];
var args = this.genArgs(r);
_genUpdateCurrentValue(r: ProtoRecord): string {
var context = (r.contextIndex == -1) ? this._genGetDirective(r.directiveIndex) :
this._localNames[r.contextIndex];
var newValue = this._localNames[r.selfIndex];
var argString = r.args.map((arg) => this._localNames[arg]).join(", ");
var rhs;
switch (r.mode) {
case RECORD_TYPE_SELF:
return assignmentTemplate(newValue, context);
rhs = context;
break;
case RECORD_TYPE_CONST:
return `${newValue} = ${this.genLiteral(r.funcOrValue)}`;
rhs = JSON.stringify(r.funcOrValue);
break;
case RECORD_TYPE_PROPERTY:
return assignmentTemplate(newValue, `${context}.${r.name}`);
rhs = `${context}.${r.name}`;
break;
case RECORD_TYPE_LOCAL:
return assignmentTemplate(newValue, `${LOCALS_ACCESSOR}.get('${r.name}')`);
rhs = `${LOCALS_ACCESSOR}.get('${r.name}')`;
break;
case RECORD_TYPE_INVOKE_METHOD:
return assignmentTemplate(newValue, `${context}.${r.name}(${args})`);
rhs = `${context}.${r.name}(${argString})`;
break;
case RECORD_TYPE_INVOKE_CLOSURE:
return assignmentTemplate(newValue, `${context}(${args})`);
rhs = `${context}(${argString})`;
break;
case RECORD_TYPE_PRIMITIVE_OP:
return assignmentTemplate(newValue, `${UTIL}.${r.name}(${args})`);
rhs = `${UTIL}.${r.name}(${argString})`;
break;
case RECORD_TYPE_INTERPOLATE:
return assignmentTemplate(newValue, this.genInterpolation(r));
rhs = this._genInterpolation(r);
break;
case RECORD_TYPE_KEYED_ACCESS:
var key = this.localNames[r.args[0]];
return assignmentTemplate(newValue, `${context}[${key}]`);
rhs = `${context}[${this._localNames[r.args[0]]}]`;
break;
default:
throw new BaseException(`Unknown operation ${r.mode}`);
}
return `${newValue} = ${rhs}`;
}
getContext(r: ProtoRecord): string {
if (r.contextIndex == -1) {
return this.getDirective(r.directiveIndex);
} else {
return this.localNames[r.contextIndex];
}
}
ifChangedGuard(r: ProtoRecord, body: string): string {
return ifChangedGuardTemplate(r.args.map((a) => this.changeNames[a]), body);
}
genInterpolation(r: ProtoRecord): string {
_genInterpolation(r: ProtoRecord): string {
var res = "";
for (var i = 0; i < r.args.length; ++i) {
res += this.genLiteral(r.fixedArgs[i]);
res += JSON.stringify(r.fixedArgs[i]);
res += " + ";
res += this.localNames[r.args[i]];
res += this._localNames[r.args[i]];
res += " + ";
}
res += this.genLiteral(r.fixedArgs[r.args.length]);
res += JSON.stringify(r.fixedArgs[r.args.length]);
return res;
}
genLiteral(value): string { return JSON.stringify(value); }
genUpdateDirectiveOrElement(r: ProtoRecord): string {
_genUpdateDirectiveOrElement(r: ProtoRecord): string {
if (!r.lastInBinding) return "";
var newValue = this.localNames[r.selfIndex];
var oldValue = this.fieldNames[r.selfIndex];
var newValue = this._localNames[r.selfIndex];
var oldValue = this._fieldNames[r.selfIndex];
var br = r.bindingRecord;
if (br.isDirective()) {
var directiveProperty =
`${this.getDirective(br.directiveRecord.directiveIndex)}.${br.propertyName}`;
return updateDirectiveTemplate(oldValue, newValue, directiveProperty);
`${this._genGetDirective(br.directiveRecord.directiveIndex)}.${br.propertyName}`;
return `
${this._genThrowOnChangeCheck(oldValue, newValue)}
${directiveProperty} = ${newValue};
${IS_CHANGED_LOCAL} = true;
`;
} else {
return updateElementTemplate(oldValue, newValue);
return `
${this._genThrowOnChangeCheck(oldValue, newValue)}
${DISPATCHER_ACCESSOR}.notifyOnBinding(${CURRENT_PROTO}.bindingRecord, ${newValue});
`;
}
}
genAddToChanges(r: ProtoRecord): string {
var newValue = this.localNames[r.selfIndex];
var oldValue = this.fieldNames[r.selfIndex];
return r.bindingRecord.callOnChange() ? addToChangesTemplate(oldValue, newValue) : "";
_genThrowOnChangeCheck(oldValue: string, newValue: string): string {
return `
if(throwOnChange) {
${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue}));
}
`;
}
genLastInDirective(r: ProtoRecord): string {
var onChanges = this.genNotifyOnChanges(r);
var onPush = this.genNotifyOnPushDetectors(r);
return lastInDirectiveTemplate(onChanges, onPush);
_genAddToChanges(r: ProtoRecord): string {
var newValue = this._localNames[r.selfIndex];
var oldValue = this._fieldNames[r.selfIndex];
if (!r.bindingRecord.callOnChange()) return "";
return `
${CHANGES_LOCAL} = ${UTIL}.addChange(
${CHANGES_LOCAL}, ${CURRENT_PROTO}.bindingRecord.propertyName,
${UTIL}.simpleChange(${oldValue}, ${newValue}));
`;
}
genNotifyOnChanges(r: ProtoRecord): string {
_genLastInDirective(r: ProtoRecord): string {
return `
${this._genNotifyOnChanges(r)}
${this._genNotifyOnPushDetectors(r)}
${IS_CHANGED_LOCAL} = false;
`;
}
_genNotifyOnChanges(r: ProtoRecord): string {
var br = r.bindingRecord;
if (r.lastInDirective && br.callOnChange()) {
return notifyOnChangesTemplate(this.getDirective(br.directiveRecord.directiveIndex));
} else {
return "";
}
if (!r.lastInDirective || !br.callOnChange()) return "";
return `
if(${CHANGES_LOCAL}) {
${this._genGetDirective(br.directiveRecord.directiveIndex)}.onChange(${CHANGES_LOCAL});
${CHANGES_LOCAL} = null;
}
`;
}
genNotifyOnPushDetectors(r: ProtoRecord): string {
_genNotifyOnPushDetectors(r: ProtoRecord): string {
var br = r.bindingRecord;
if (r.lastInDirective && br.isOnPushChangeDetection()) {
return notifyOnPushDetectorsTemplate(this.getDetector(br.directiveRecord.directiveIndex));
} else {
return "";
}
if (!r.lastInDirective || !br.isOnPushChangeDetection()) return "";
var retVal = `
if(${IS_CHANGED_LOCAL}) {
${this._genGetDetector(br.directiveRecord.directiveIndex)}.markAsCheckOnce();
}
`;
return retVal;
}
genArgs(r: ProtoRecord): string { return r.args.map((arg) => this.localNames[arg]).join(", "); }
}