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:
parent
4a3fd5e855
commit
a2770c8a52
|
@ -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 {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
import {AbstractChangeDetector} from './abstract_change_detector';
|
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
|
* 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
|
* that "emulates" what the developer would write by hand to implement the same
|
||||||
* kind of behaviour.
|
* 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 ABSTRACT_CHANGE_DETECTOR = "AbstractChangeDetector";
|
||||||
var UTIL = "ChangeDetectionUtil";
|
var UTIL = "ChangeDetectionUtil";
|
||||||
|
@ -41,240 +37,24 @@ var IS_CHANGED_LOCAL = "isChanged";
|
||||||
var CHANGES_LOCAL = "changes";
|
var CHANGES_LOCAL = "changes";
|
||||||
var LOCALS_ACCESSOR = "this.locals";
|
var LOCALS_ACCESSOR = "this.locals";
|
||||||
var MODE_ACCESSOR = "this.mode";
|
var MODE_ACCESSOR = "this.mode";
|
||||||
var TEMP_LOCAL = "temp";
|
|
||||||
var CURRENT_PROTO = "currentProto";
|
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 {
|
export class ChangeDetectorJITGenerator {
|
||||||
localNames: List<string>;
|
_localNames: List<string>;
|
||||||
changeNames: List<string>;
|
_changeNames: List<string>;
|
||||||
fieldNames: List<string>;
|
_fieldNames: List<string>;
|
||||||
pipeNames: List<string>;
|
_pipeNames: List<string>;
|
||||||
|
|
||||||
constructor(public typeName: string, public changeDetectionStrategy: string,
|
constructor(public typeName: string, public changeDetectionStrategy: string,
|
||||||
public records: List<ProtoRecord>, public directiveRecords: List<any>) {
|
public records: List<ProtoRecord>, public directiveRecords: List<any>) {
|
||||||
this.localNames = this.getLocalNames(records);
|
this._localNames = this._getLocalNames(records);
|
||||||
this.changeNames = this.getChangeNames(this.localNames);
|
this._changeNames = this._getChangeNames(this._localNames);
|
||||||
this.fieldNames = this.getFieldNames(this.localNames);
|
this._fieldNames = this._getFieldNames(this._localNames);
|
||||||
this.pipeNames = this.getPipeNames(this.localNames);
|
this._pipeNames = this._getPipeNames(this._localNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
getLocalNames(records: List<ProtoRecord>): List<string> {
|
_getLocalNames(records: List<ProtoRecord>): List<string> {
|
||||||
var index = 0;
|
var index = 0;
|
||||||
var names = records.map((r) => {
|
var names = records.map((r) => {
|
||||||
var sanitizedName = r.name.replace(new RegExp("\\W", "g"), '');
|
var sanitizedName = r.name.replace(new RegExp("\\W", "g"), '');
|
||||||
|
@ -283,252 +63,349 @@ export class ChangeDetectorJITGenerator {
|
||||||
return ["context"].concat(names);
|
return ["context"].concat(names);
|
||||||
}
|
}
|
||||||
|
|
||||||
getChangeNames(localNames: List<string>): List<string> {
|
_getChangeNames(_localNames: List<string>): List<string> {
|
||||||
return localNames.map((n) => `change_${n}`);
|
return _localNames.map((n) => `change_${n}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
getFieldNames(localNames: List<string>): List<string> {
|
_getFieldNames(_localNames: List<string>): List<string> {
|
||||||
return localNames.map((n) => `this.${n}`);
|
return _localNames.map((n) => `this.${n}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPipeNames(localNames: List<string>): List<string> {
|
_getPipeNames(_localNames: List<string>): List<string> {
|
||||||
return localNames.map((n) => `this.${n}_pipe`);
|
return _localNames.map((n) => `this.${n}_pipe`);
|
||||||
}
|
}
|
||||||
|
|
||||||
generate(): Function {
|
generate(): Function {
|
||||||
var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(),
|
var classDefinition = `
|
||||||
this.genCallOnAllChangesDone(), this.genHydrate());
|
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',
|
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos',
|
||||||
'directiveRecords', text)(AbstractChangeDetector, ChangeDetectionUtil,
|
'directiveRecords', classDefinition)(
|
||||||
this.records, this.directiveRecords);
|
AbstractChangeDetector, ChangeDetectionUtil, this.records, this.directiveRecords);
|
||||||
}
|
}
|
||||||
|
|
||||||
genConstructor(): string {
|
_genGetDirectiveFieldNames(): List<string> {
|
||||||
return constructorTemplate(this.typeName, this.genFieldDefinitions());
|
return this.directiveRecords.map((d) => this._genGetDirective(d.directiveIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
genHydrate(): string {
|
_genGetDetectorFieldNames(): List<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> {
|
|
||||||
return this.directiveRecords.filter(r => r.isOnPushChangeDetection())
|
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() {
|
_getNonNullPipeNames(): List<string> {
|
||||||
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> {
|
|
||||||
var pipes = [];
|
var pipes = [];
|
||||||
this.records.forEach((r) => {
|
this.records.forEach((r) => {
|
||||||
if (r.mode === RECORD_TYPE_PIPE || r.mode === RECORD_TYPE_BINDING_PIPE) {
|
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;
|
return pipes;
|
||||||
}
|
}
|
||||||
|
|
||||||
genDetectChanges(): string {
|
_genFieldDefinitions() {
|
||||||
var body = this.genDetectChangesBody();
|
var fields = [];
|
||||||
return detectChangesTemplate(this.typeName, body);
|
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 notifications = [];
|
||||||
var dirs = this.directiveRecords;
|
var dirs = this.directiveRecords;
|
||||||
|
|
||||||
for (var i = dirs.length - 1; i >= 0; --i) {
|
for (var i = dirs.length - 1; i >= 0; --i) {
|
||||||
var dir = dirs[i];
|
var dir = dirs[i];
|
||||||
if (dir.callOnAllChangesDone) {
|
if (dir.callOnAllChangesDone) {
|
||||||
var directive = `this.directive_${dir.directiveIndex.name}`;
|
notifications.push(`${this._genGetDirective(dir.directiveIndex)}.onAllChangesDone();`);
|
||||||
notifications.push(onAllChangesDoneTemplate(directive));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return callOnAllChangesDoneTemplate(this.typeName, notifications.join(";\n"));
|
return notifications.join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
genDetectChangesBody(): string {
|
_genLocalDefinitions(): string { return this._localNames.map((n) => `var ${n};`).join("\n"); }
|
||||||
var rec = this.records.map((r) => this.genRecord(r)).join("\n");
|
|
||||||
return detectChangesBodyTemplate(this.genLocalDefinitions(), this.genChangeDefinitions(), rec);
|
_genChangeDefinitions(): string {
|
||||||
|
return this._changeNames.map((n) => `var ${n} = false;`).join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
genLocalDefinitions(): string { return localDefinitionsTemplate(this.localNames); }
|
_genRecord(r: ProtoRecord): string {
|
||||||
|
|
||||||
genChangeDefinitions(): string { return changeDefinitionsTemplate(this.changeNames); }
|
|
||||||
|
|
||||||
genRecord(r: ProtoRecord): string {
|
|
||||||
if (r.mode === RECORD_TYPE_PIPE || r.mode === RECORD_TYPE_BINDING_PIPE) {
|
if (r.mode === RECORD_TYPE_PIPE || r.mode === RECORD_TYPE_BINDING_PIPE) {
|
||||||
return this.genPipeCheck(r);
|
return this._genPipeCheck(r);
|
||||||
} else {
|
} else {
|
||||||
return this.genReferenceCheck(r);
|
return this._genReferenceCheck(r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
genPipeCheck(r: ProtoRecord): string {
|
_genPipeCheck(r: ProtoRecord): string {
|
||||||
var context = this.localNames[r.contextIndex];
|
var context = this._localNames[r.contextIndex];
|
||||||
var oldValue = this.fieldNames[r.selfIndex];
|
var oldValue = this._fieldNames[r.selfIndex];
|
||||||
var newValue = this.localNames[r.selfIndex];
|
var newValue = this._localNames[r.selfIndex];
|
||||||
var change = this.changeNames[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 cdRef = r.mode === RECORD_TYPE_BINDING_PIPE ? "this.ref" : "null";
|
||||||
|
|
||||||
var update = this.genUpdateDirectiveOrElement(r);
|
var protoIndex = r.selfIndex - 1;
|
||||||
var addToChanges = this.genAddToChanges(r);
|
var pipeType = r.name;
|
||||||
var lastInDirective = this.genLastInDirective(r);
|
|
||||||
|
|
||||||
return pipeCheckTemplate(r.selfIndex - 1, context, cdRef, pipe, r.name, oldValue, newValue,
|
return `
|
||||||
change, update, addToChanges, lastInDirective);
|
${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 {
|
_genReferenceCheck(r: ProtoRecord): string {
|
||||||
var oldValue = this.fieldNames[r.selfIndex];
|
var oldValue = this._fieldNames[r.selfIndex];
|
||||||
var newValue = this.localNames[r.selfIndex];
|
var newValue = this._localNames[r.selfIndex];
|
||||||
var change = this.changeNames[r.selfIndex];
|
|
||||||
var assignment = this.genUpdateCurrentValue(r);
|
|
||||||
|
|
||||||
var update = this.genUpdateDirectiveOrElement(r);
|
var protoIndex = r.selfIndex - 1;
|
||||||
var addToChanges = this.genAddToChanges(r);
|
var check = `
|
||||||
var lastInDirective = this.genLastInDirective(r);
|
${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()) {
|
if (r.isPureFunction()) {
|
||||||
return this.ifChangedGuard(r, check);
|
var condition = `${this._changeNames.join(" || ")}`;
|
||||||
|
return `if (${condition}) { ${check} }`;
|
||||||
} else {
|
} else {
|
||||||
return check;
|
return check;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
genUpdateCurrentValue(r: ProtoRecord): string {
|
_genUpdateCurrentValue(r: ProtoRecord): string {
|
||||||
var context = this.getContext(r);
|
var context = (r.contextIndex == -1) ? this._genGetDirective(r.directiveIndex) :
|
||||||
var newValue = this.localNames[r.selfIndex];
|
this._localNames[r.contextIndex];
|
||||||
var args = this.genArgs(r);
|
var newValue = this._localNames[r.selfIndex];
|
||||||
|
var argString = r.args.map((arg) => this._localNames[arg]).join(", ");
|
||||||
|
|
||||||
|
var rhs;
|
||||||
switch (r.mode) {
|
switch (r.mode) {
|
||||||
case RECORD_TYPE_SELF:
|
case RECORD_TYPE_SELF:
|
||||||
return assignmentTemplate(newValue, context);
|
rhs = context;
|
||||||
|
break;
|
||||||
|
|
||||||
case RECORD_TYPE_CONST:
|
case RECORD_TYPE_CONST:
|
||||||
return `${newValue} = ${this.genLiteral(r.funcOrValue)}`;
|
rhs = JSON.stringify(r.funcOrValue);
|
||||||
|
break;
|
||||||
|
|
||||||
case RECORD_TYPE_PROPERTY:
|
case RECORD_TYPE_PROPERTY:
|
||||||
return assignmentTemplate(newValue, `${context}.${r.name}`);
|
rhs = `${context}.${r.name}`;
|
||||||
|
break;
|
||||||
|
|
||||||
case RECORD_TYPE_LOCAL:
|
case RECORD_TYPE_LOCAL:
|
||||||
return assignmentTemplate(newValue, `${LOCALS_ACCESSOR}.get('${r.name}')`);
|
rhs = `${LOCALS_ACCESSOR}.get('${r.name}')`;
|
||||||
|
break;
|
||||||
|
|
||||||
case RECORD_TYPE_INVOKE_METHOD:
|
case RECORD_TYPE_INVOKE_METHOD:
|
||||||
return assignmentTemplate(newValue, `${context}.${r.name}(${args})`);
|
rhs = `${context}.${r.name}(${argString})`;
|
||||||
|
break;
|
||||||
|
|
||||||
case RECORD_TYPE_INVOKE_CLOSURE:
|
case RECORD_TYPE_INVOKE_CLOSURE:
|
||||||
return assignmentTemplate(newValue, `${context}(${args})`);
|
rhs = `${context}(${argString})`;
|
||||||
|
break;
|
||||||
|
|
||||||
case RECORD_TYPE_PRIMITIVE_OP:
|
case RECORD_TYPE_PRIMITIVE_OP:
|
||||||
return assignmentTemplate(newValue, `${UTIL}.${r.name}(${args})`);
|
rhs = `${UTIL}.${r.name}(${argString})`;
|
||||||
|
break;
|
||||||
|
|
||||||
case RECORD_TYPE_INTERPOLATE:
|
case RECORD_TYPE_INTERPOLATE:
|
||||||
return assignmentTemplate(newValue, this.genInterpolation(r));
|
rhs = this._genInterpolation(r);
|
||||||
|
break;
|
||||||
|
|
||||||
case RECORD_TYPE_KEYED_ACCESS:
|
case RECORD_TYPE_KEYED_ACCESS:
|
||||||
var key = this.localNames[r.args[0]];
|
rhs = `${context}[${this._localNames[r.args[0]]}]`;
|
||||||
return assignmentTemplate(newValue, `${context}[${key}]`);
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new BaseException(`Unknown operation ${r.mode}`);
|
throw new BaseException(`Unknown operation ${r.mode}`);
|
||||||
}
|
}
|
||||||
|
return `${newValue} = ${rhs}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
getContext(r: ProtoRecord): string {
|
_genInterpolation(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 {
|
|
||||||
var res = "";
|
var res = "";
|
||||||
for (var i = 0; i < r.args.length; ++i) {
|
for (var i = 0; i < r.args.length; ++i) {
|
||||||
res += this.genLiteral(r.fixedArgs[i]);
|
res += JSON.stringify(r.fixedArgs[i]);
|
||||||
res += " + ";
|
res += " + ";
|
||||||
res += this.localNames[r.args[i]];
|
res += this._localNames[r.args[i]];
|
||||||
res += " + ";
|
res += " + ";
|
||||||
}
|
}
|
||||||
res += this.genLiteral(r.fixedArgs[r.args.length]);
|
res += JSON.stringify(r.fixedArgs[r.args.length]);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
genLiteral(value): string { return JSON.stringify(value); }
|
_genUpdateDirectiveOrElement(r: ProtoRecord): string {
|
||||||
|
|
||||||
genUpdateDirectiveOrElement(r: ProtoRecord): string {
|
|
||||||
if (!r.lastInBinding) return "";
|
if (!r.lastInBinding) return "";
|
||||||
|
|
||||||
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 br = r.bindingRecord;
|
var br = r.bindingRecord;
|
||||||
if (br.isDirective()) {
|
if (br.isDirective()) {
|
||||||
var directiveProperty =
|
var directiveProperty =
|
||||||
`${this.getDirective(br.directiveRecord.directiveIndex)}.${br.propertyName}`;
|
`${this._genGetDirective(br.directiveRecord.directiveIndex)}.${br.propertyName}`;
|
||||||
return updateDirectiveTemplate(oldValue, newValue, directiveProperty);
|
return `
|
||||||
|
${this._genThrowOnChangeCheck(oldValue, newValue)}
|
||||||
|
${directiveProperty} = ${newValue};
|
||||||
|
${IS_CHANGED_LOCAL} = true;
|
||||||
|
`;
|
||||||
} else {
|
} else {
|
||||||
return updateElementTemplate(oldValue, newValue);
|
return `
|
||||||
|
${this._genThrowOnChangeCheck(oldValue, newValue)}
|
||||||
|
${DISPATCHER_ACCESSOR}.notifyOnBinding(${CURRENT_PROTO}.bindingRecord, ${newValue});
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
genAddToChanges(r: ProtoRecord): string {
|
_genThrowOnChangeCheck(oldValue: string, newValue: string): string {
|
||||||
var newValue = this.localNames[r.selfIndex];
|
return `
|
||||||
var oldValue = this.fieldNames[r.selfIndex];
|
if(throwOnChange) {
|
||||||
return r.bindingRecord.callOnChange() ? addToChangesTemplate(oldValue, newValue) : "";
|
${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue}));
|
||||||
|
}
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
genLastInDirective(r: ProtoRecord): string {
|
_genAddToChanges(r: ProtoRecord): string {
|
||||||
var onChanges = this.genNotifyOnChanges(r);
|
var newValue = this._localNames[r.selfIndex];
|
||||||
var onPush = this.genNotifyOnPushDetectors(r);
|
var oldValue = this._fieldNames[r.selfIndex];
|
||||||
return lastInDirectiveTemplate(onChanges, onPush);
|
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;
|
var br = r.bindingRecord;
|
||||||
if (r.lastInDirective && br.callOnChange()) {
|
if (!r.lastInDirective || !br.callOnChange()) return "";
|
||||||
return notifyOnChangesTemplate(this.getDirective(br.directiveRecord.directiveIndex));
|
return `
|
||||||
} else {
|
if(${CHANGES_LOCAL}) {
|
||||||
return "";
|
${this._genGetDirective(br.directiveRecord.directiveIndex)}.onChange(${CHANGES_LOCAL});
|
||||||
}
|
${CHANGES_LOCAL} = null;
|
||||||
|
}
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
genNotifyOnPushDetectors(r: ProtoRecord): string {
|
_genNotifyOnPushDetectors(r: ProtoRecord): string {
|
||||||
var br = r.bindingRecord;
|
var br = r.bindingRecord;
|
||||||
if (r.lastInDirective && br.isOnPushChangeDetection()) {
|
if (!r.lastInDirective || !br.isOnPushChangeDetection()) return "";
|
||||||
return notifyOnPushDetectorsTemplate(this.getDetector(br.directiveRecord.directiveIndex));
|
var retVal = `
|
||||||
} else {
|
if(${IS_CHANGED_LOCAL}) {
|
||||||
return "";
|
${this._genGetDetector(br.directiveRecord.directiveIndex)}.markAsCheckOnce();
|
||||||
}
|
}
|
||||||
|
`;
|
||||||
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
genArgs(r: ProtoRecord): string { return r.args.map((arg) => this.localNames[arg]).join(", "); }
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue