diff --git a/modules/angular2/src/change_detection/change_detection_jit_generator.ts b/modules/angular2/src/change_detection/change_detection_jit_generator.ts index 5bc2c0fa51..a1a5ad2b3c 100644 --- a/modules/angular2/src/change_detection/change_detection_jit_generator.ts +++ b/modules/angular2/src/change_detection/change_detection_jit_generator.ts @@ -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) { - return pipeNames.map((p) => `${p}.onDestroy()`).join("\n"); -} - -function hydrateTemplate(type: string, mode: string, fieldDefinitions: string, - pipeOnDestroy: string, directiveFieldNames: List, - detectorFieldNames: List): 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): string { - return names.map((n) => `var ${n};`).join("\n"); -} - -function changeDefinitionsTemplate(names: List): string { - return names.map((n) => `var ${n} = false;`).join("\n"); -} - -function fieldDefinitionsTemplate(names: List): string { - return names.map((n) => `${n} = ${UTIL}.uninitialized();`).join("\n"); -} - -function ifChangedGuardTemplate(changeNames: List, 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; - changeNames: List; - fieldNames: List; - pipeNames: List; + _localNames: List; + _changeNames: List; + _fieldNames: List; + _pipeNames: List; constructor(public typeName: string, public changeDetectionStrategy: string, public records: List, public directiveRecords: List) { - 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): List { + _getLocalNames(records: List): List { 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): List { - return localNames.map((n) => `change_${n}`); + _getChangeNames(_localNames: List): List { + return _localNames.map((n) => `change_${n}`); } - getFieldNames(localNames: List): List { - return localNames.map((n) => `this.${n}`); + _getFieldNames(_localNames: List): List { + return _localNames.map((n) => `this.${n}`); } - getPipeNames(localNames: List): List { - return localNames.map((n) => `this.${n}_pipe`); + _getPipeNames(_localNames: List): List { + 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 { + 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 { - return this.directiveRecords.map((d) => this.getDirective(d.directiveIndex)); - } - - getDetectorFieldNames(): List { + _genGetDetectorFieldNames(): List { 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 { + _getNonNullPipeNames(): List { 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(", "); } }