From 192cf9ddf5e214225019584ae143981e3d60422a Mon Sep 17 00:00:00 2001 From: Tim Blasi Date: Tue, 28 Jul 2015 12:43:41 -0700 Subject: [PATCH] refactor(change_detect): Move common fields to AbstractChangeDetector Move fields common to Dynamic, Jit, and Pregen change detectors into the `AbstractChangeDetector` superclass to save on codegen size and reduce code duplication. Update to #3248, closes #3243 --- .../abstract_change_detector.ts | 29 ++++- .../src/change_detection/change_detection.ts | 3 +- .../change_detection_jit_generator.ts | 74 +++++------- .../src/change_detection/codegen_name_util.ts | 84 ++++++++----- .../dynamic_change_detector.ts | 9 +- .../src/change_detection/interfaces.ts | 10 +- modules/angular2/src/core/compiler/view.ts | 20 ++-- .../change_detector_codegen.dart | 110 ++++++++---------- .../expected/bar.ng_deps.dart | 80 +++++++------ .../change_detection_benchmark.ts | 4 + 10 files changed, 224 insertions(+), 199 deletions(-) diff --git a/modules/angular2/src/change_detection/abstract_change_detector.ts b/modules/angular2/src/change_detection/abstract_change_detector.ts index f4efc51b5e..621f5a9fba 100644 --- a/modules/angular2/src/change_detection/abstract_change_detector.ts +++ b/modules/angular2/src/change_detection/abstract_change_detector.ts @@ -1,10 +1,12 @@ import {isPresent, BaseException} from 'angular2/src/facade/lang'; import {List, ListWrapper} from 'angular2/src/facade/collection'; import {ChangeDetectorRef} from './change_detector_ref'; -import {ChangeDetector} from './interfaces'; +import {DirectiveRecord} from './directive_record'; +import {ChangeDetector, ChangeDispatcher} from './interfaces'; import {ChangeDetectionError} from './exceptions'; import {ProtoRecord} from './proto_record'; import {Locals} from './parser/locals'; +import {Pipes} from './pipes/pipes'; import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH} from './constants'; class _Context { @@ -13,14 +15,31 @@ class _Context { public expression: any) {} } -export class AbstractChangeDetector implements ChangeDetector { +export class AbstractChangeDetector implements ChangeDetector { lightDomChildren: List = []; shadowDomChildren: List = []; parent: ChangeDetector; - mode: string = null; ref: ChangeDetectorRef; - constructor(public id: string, public dispatcher: any) { this.ref = new ChangeDetectorRef(this); } + // The names of the below fields must be kept in sync with codegen_name_util.ts or + // change detection will fail. + alreadyChecked: any = false; + context: T; + currentProto: ProtoRecord = null; + directiveRecords: List; + dispatcher: ChangeDispatcher; + locals: Locals = null; + mode: string = null; + pipes: Pipes = null; + protos: List; + + constructor(public id: string, dispatcher: ChangeDispatcher, protos: List, + directiveRecords: List) { + this.ref = new ChangeDetectorRef(this); + this.directiveRecords = directiveRecords; + this.dispatcher = dispatcher; + this.protos = protos; + } addChild(cd: ChangeDetector): void { this.lightDomChildren.push(cd); @@ -58,7 +77,7 @@ export class AbstractChangeDetector implements ChangeDetector { detectChangesInRecords(throwOnChange: boolean): void {} - hydrate(context: any, locals: Locals, directives: any, pipes: any): void {} + hydrate(context: T, locals: Locals, directives: any, pipes: any): void {} hydrateDirectives(directives: any): void {} diff --git a/modules/angular2/src/change_detection/change_detection.ts b/modules/angular2/src/change_detection/change_detection.ts index a9afed0b4d..7f8f467311 100644 --- a/modules/angular2/src/change_detection/change_detection.ts +++ b/modules/angular2/src/change_detection/change_detection.ts @@ -42,7 +42,8 @@ export { ChangeDetector, ChangeDispatcher, ChangeDetection, - ChangeDetectorDefinition + ChangeDetectorDefinition, + DebugContext } from './interfaces'; export {CHECK_ONCE, CHECK_ALWAYS, DETACHED, CHECKED, ON_PUSH, DEFAULT} from './constants'; export {DynamicProtoChangeDetector} from './proto_change_detector'; 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 0410acd181..cd4e82144c 100644 --- a/modules/angular2/src/change_detection/change_detection_jit_generator.ts +++ b/modules/angular2/src/change_detection/change_detection_jit_generator.ts @@ -6,7 +6,7 @@ import {ChangeDetectionUtil} from './change_detection_util'; import {DirectiveIndex, DirectiveRecord} from './directive_record'; import {ProtoRecord, RecordType} from './proto_record'; -import {CodegenNameUtil, sanitizeName} from './codegen_name_util'; +import {CONTEXT_INDEX, CodegenNameUtil, sanitizeName} from './codegen_name_util'; /** @@ -20,16 +20,8 @@ import {CodegenNameUtil, sanitizeName} from './codegen_name_util'; */ var ABSTRACT_CHANGE_DETECTOR = "AbstractChangeDetector"; var UTIL = "ChangeDetectionUtil"; -var DISPATCHER_ACCESSOR = "this.dispatcher"; -var PIPES_ACCESSOR = "this.pipes"; -var PROTOS_ACCESSOR = "this.protos"; -var DIRECTIVES_ACCESSOR = "this.directiveRecords"; var IS_CHANGED_LOCAL = "isChanged"; var CHANGES_LOCAL = "changes"; -var LOCALS_ACCESSOR = "this.locals"; -var MODE_ACCESSOR = "this.mode"; -var CURRENT_PROTO = "this.currentProto"; -var ALREADY_CHECKED_ACCESSOR = "this.alreadyChecked"; export class ChangeDetectorJITGenerator { _names: CodegenNameUtil; @@ -38,20 +30,14 @@ export class ChangeDetectorJITGenerator { constructor(public id: string, public changeDetectionStrategy: string, public records: List, public directiveRecords: List, private generateCheckNoChanges: boolean) { - this._names = new CodegenNameUtil(this.records, this.directiveRecords, 'this._', UTIL); + this._names = new CodegenNameUtil(this.records, this.directiveRecords, UTIL); this._typeName = sanitizeName(`ChangeDetector_${this.id}`); } generate(): Function { var classDefinition = ` var ${this._typeName} = function ${this._typeName}(dispatcher, protos, directiveRecords) { - ${ABSTRACT_CHANGE_DETECTOR}.call(this, ${JSON.stringify(this.id)}, dispatcher); - ${PROTOS_ACCESSOR} = protos; - ${DIRECTIVES_ACCESSOR} = directiveRecords; - ${LOCALS_ACCESSOR} = null; - ${CURRENT_PROTO} = null; - ${PIPES_ACCESSOR} = null; - ${ALREADY_CHECKED_ACCESSOR} = false; + ${ABSTRACT_CHANGE_DETECTOR}.call(this, ${JSON.stringify(this.id)}, dispatcher, protos, directiveRecords); this.dehydrateDirectives(false); } @@ -64,22 +50,20 @@ export class ChangeDetectorJITGenerator { try { this.__detectChangesInRecords(throwOnChange); } catch (e) { - this.throwError(${CURRENT_PROTO}, e, e.stack); + this.throwError(${this._names.getCurrentProtoName()}, e, e.stack); } } ${this._typeName}.prototype.__detectChangesInRecords = function(throwOnChange) { - ${CURRENT_PROTO} = null; + ${this._names.getCurrentProtoName()} = null; ${this._names.genInitLocals()} var ${IS_CHANGED_LOCAL} = false; var ${CHANGES_LOCAL} = null; - context = ${this._names.getContextName()}; - ${this.records.map((r) => this._genRecord(r)).join("\n")} - ${ALREADY_CHECKED_ACCESSOR} = true; + ${this._names.getAlreadyCheckedName()} = true; } ${this._genCheckNoChanges()} @@ -89,26 +73,27 @@ export class ChangeDetectorJITGenerator { } ${this._typeName}.prototype.hydrate = function(context, locals, directives, pipes) { - ${MODE_ACCESSOR} = "${ChangeDetectionUtil.changeDetectionMode(this.changeDetectionStrategy)}"; - ${this._names.getContextName()} = context; - ${LOCALS_ACCESSOR} = locals; + ${this._names.getModeName()} = + "${ChangeDetectionUtil.changeDetectionMode(this.changeDetectionStrategy)}"; + ${this._names.getFieldName(CONTEXT_INDEX)} = context; + ${this._names.getLocalsAccessorName()} = locals; this.hydrateDirectives(directives); - ${PIPES_ACCESSOR} = pipes; - ${ALREADY_CHECKED_ACCESSOR} = false; + ${this._names.getPipesAccessorName()} = pipes; + ${this._names.getAlreadyCheckedName()} = false; } ${this._maybeGenHydrateDirectives()} ${this._typeName}.prototype.dehydrate = function() { this.dehydrateDirectives(true); - ${LOCALS_ACCESSOR} = null; - ${PIPES_ACCESSOR} = null; + ${this._names.getLocalsAccessorName()} = null; + ${this._names.getPipesAccessorName()} = null; } ${this._maybeGenDehydrateDirectives()} ${this._typeName}.prototype.hydrated = function() { - return ${this._names.getContextName()} !== null; + return ${this._names.getFieldName(CONTEXT_INDEX)} !== null; } return function(dispatcher) { @@ -148,8 +133,8 @@ export class ChangeDetectorJITGenerator { var directiveFieldNames = this._names.getAllDirectiveNames(); 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);`; + lines[i] = `${directiveFieldNames[i]} = directives.getDirectiveFor( + ${this._names.getDirectivesAccessorName()}[${i}].directiveIndex);`; } return lines.join('\n'); } @@ -158,8 +143,8 @@ export class ChangeDetectorJITGenerator { var detectorFieldNames = this._names.getAllDetectorNames(); 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);`; + lines[i] = `${detectorFieldNames[i]} = directives.getDetectorFor( + ${this._names.getDirectivesAccessorName()}[${i}].directiveIndex);`; } return lines.join('\n'); } @@ -179,7 +164,7 @@ export class ChangeDetectorJITGenerator { var directiveNotifications = notifications.join("\n"); return ` - this.dispatcher.notifyOnAllChangesDone(); + ${this._names.getDispatcherName()}.notifyOnAllChangesDone(); ${directiveNotifications} `; } @@ -223,12 +208,12 @@ export class ChangeDetectorJITGenerator { var pipeType = r.name; return ` - ${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}]; + ${this._names.getCurrentProtoName()} = ${this._names.getProtosName()}[${protoIndex}]; if (${pipe} === ${UTIL}.uninitialized) { - ${pipe} = ${PIPES_ACCESSOR}.get('${pipeType}', ${context}, ${cdRef}); + ${pipe} = ${this._names.getPipesAccessorName()}.get('${pipeType}', ${context}, ${cdRef}); } else if (!${pipe}.supports(${context})) { ${pipe}.onDestroy(); - ${pipe} = ${PIPES_ACCESSOR}.get('${pipeType}', ${context}, ${cdRef}); + ${pipe} = ${this._names.getPipesAccessorName()}.get('${pipeType}', ${context}, ${cdRef}); } ${newValue} = ${pipe}.transform(${context}, [${argString}]); @@ -248,7 +233,7 @@ export class ChangeDetectorJITGenerator { var protoIndex = r.selfIndex - 1; var check = ` - ${CURRENT_PROTO} = ${PROTOS_ACCESSOR}[${protoIndex}]; + ${this._names.getCurrentProtoName()} = ${this._names.getProtosName()}[${protoIndex}]; ${this._genUpdateCurrentValue(r)} if (${newValue} !== ${oldValue}) { ${this._names.getChangeName(r.selfIndex)} = true; @@ -291,7 +276,7 @@ export class ChangeDetectorJITGenerator { break; case RecordType.LOCAL: - rhs = `${LOCALS_ACCESSOR}.get('${r.name}')`; + rhs = `${this._names.getLocalsAccessorName()}.get('${r.name}')`; break; case RecordType.INVOKE_METHOD: @@ -354,7 +339,8 @@ export class ChangeDetectorJITGenerator { } else { return ` ${this._genThrowOnChangeCheck(oldValue, newValue)} - ${DISPATCHER_ACCESSOR}.notifyOnBinding(${CURRENT_PROTO}.bindingRecord, ${newValue}); + ${this._names.getDispatcherName()}.notifyOnBinding( + ${this._names.getCurrentProtoName()}.bindingRecord, ${newValue}); `; } } @@ -363,7 +349,7 @@ export class ChangeDetectorJITGenerator { if (this.generateCheckNoChanges) { return ` if(throwOnChange) { - ${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue})); + ${UTIL}.throwOnChange(${this._names.getCurrentProtoName()}, ${UTIL}.simpleChange(${oldValue}, ${newValue})); } `; } else { @@ -385,7 +371,7 @@ export class ChangeDetectorJITGenerator { if (!r.bindingRecord.callOnChange()) return ""; return ` ${CHANGES_LOCAL} = ${UTIL}.addChange( - ${CHANGES_LOCAL}, ${CURRENT_PROTO}.bindingRecord.propertyName, + ${CHANGES_LOCAL}, ${this._names.getCurrentProtoName()}.bindingRecord.propertyName, ${UTIL}.simpleChange(${oldValue}, ${newValue})); `; } @@ -406,7 +392,7 @@ export class ChangeDetectorJITGenerator { _genOnInit(r: ProtoRecord): string { var br = r.bindingRecord; - return `if (!throwOnChange && !${ALREADY_CHECKED_ACCESSOR}) ${this._names.getDirectiveName(br.directiveRecord.directiveIndex)}.onInit();`; + return `if (!throwOnChange && !${this._names.getAlreadyCheckedName()}) ${this._names.getDirectiveName(br.directiveRecord.directiveIndex)}.onInit();`; } _genOnChange(r: ProtoRecord): string { diff --git a/modules/angular2/src/change_detection/codegen_name_util.ts b/modules/angular2/src/change_detection/codegen_name_util.ts index 22d68890ab..ec5cce9d55 100644 --- a/modules/angular2/src/change_detection/codegen_name_util.ts +++ b/modules/angular2/src/change_detection/codegen_name_util.ts @@ -5,8 +5,21 @@ import {DirectiveIndex} from './directive_record'; import {ProtoRecord} from './proto_record'; -// `context` is always the first field. -var _CONTEXT_IDX = 0; +// The names of these fields must be kept in sync with abstract_change_detector.ts or change +// detection will fail. +const _ALREADY_CHECKED_ACCESSOR = "alreadyChecked"; +const _CONTEXT_ACCESSOR = "context"; +const _CURRENT_PROTO = "currentProto"; +const _DIRECTIVES_ACCESSOR = "directiveRecords"; +const _DISPATCHER_ACCESSOR = "dispatcher"; +const _LOCALS_ACCESSOR = "locals"; +const _MODE_ACCESSOR = "mode"; +const _PIPES_ACCESSOR = "pipes"; +const _PROTOS_ACCESSOR = "protos"; + +// `context` is always first. +export const CONTEXT_INDEX = 0; +const _FIELD_PREFIX = 'this.'; var _whiteSpaceRegExp = RegExpWrapper.create("\\W", "g"); @@ -29,18 +42,34 @@ export class CodegenNameUtil { */ _sanitizedNames: List; - constructor(public records: List, public directiveRecords: List, - public fieldPrefix: string, public utilName: string) { + constructor(private records: List, private directiveRecords: List, + private utilName: string) { this._sanitizedNames = ListWrapper.createFixedSize(this.records.length + 1); - this._sanitizedNames[_CONTEXT_IDX] = 'context'; + this._sanitizedNames[CONTEXT_INDEX] = _CONTEXT_ACCESSOR; for (var i = 0, iLen = this.records.length; i < iLen; ++i) { this._sanitizedNames[i + 1] = sanitizeName(`${this.records[i].name}${i}`); } } - getContextName(): string { return this.getFieldName(_CONTEXT_IDX); } + _addFieldPrefix(name: string): string { return `${_FIELD_PREFIX}${name}`; } - getLocalName(idx: int): string { return this._sanitizedNames[idx]; } + getDispatcherName(): string { return this._addFieldPrefix(_DISPATCHER_ACCESSOR); } + + getPipesAccessorName(): string { return this._addFieldPrefix(_PIPES_ACCESSOR); } + + getProtosName(): string { return this._addFieldPrefix(_PROTOS_ACCESSOR); } + + getDirectivesAccessorName(): string { return this._addFieldPrefix(_DIRECTIVES_ACCESSOR); } + + getLocalsAccessorName(): string { return this._addFieldPrefix(_LOCALS_ACCESSOR); } + + getAlreadyCheckedName(): string { return this._addFieldPrefix(_ALREADY_CHECKED_ACCESSOR); } + + getModeName(): string { return this._addFieldPrefix(_MODE_ACCESSOR); } + + getCurrentProtoName(): string { return this._addFieldPrefix(_CURRENT_PROTO); } + + getLocalName(idx: int): string { return `l_${this._sanitizedNames[idx]}`; } getChangeName(idx: int): string { return `c_${this._sanitizedNames[idx]}`; } @@ -51,17 +80,22 @@ export class CodegenNameUtil { var declarations = []; var assignments = []; for (var i = 0, iLen = this.getFieldCount(); i < iLen; ++i) { - var changeName = this.getChangeName(i); - declarations.push(`${this.getLocalName(i)},${changeName}`); - assignments.push(changeName); + if (i == CONTEXT_INDEX) { + declarations.push(`${this.getLocalName(i)} = ${this.getFieldName(i)}`); + } else { + var changeName = this.getChangeName(i); + declarations.push(`${this.getLocalName(i)},${changeName}`); + assignments.push(changeName); + } } - return `var ${ListWrapper.join(declarations, ',')};` + - `${ListWrapper.join(assignments, '=')} = false;`; + var assignmentsCode = + ListWrapper.isEmpty(assignments) ? '' : `${ListWrapper.join(assignments, '=')} = false;`; + return `var ${ListWrapper.join(declarations, ',')};${assignmentsCode}`; } getFieldCount(): int { return this._sanitizedNames.length; } - getFieldName(idx: int): string { return `${this.fieldPrefix}${this._sanitizedNames[idx]}`; } + getFieldName(idx: int): string { return this._addFieldPrefix(this._sanitizedNames[idx]); } getAllFieldNames(): List { var fieldList = []; @@ -84,27 +118,17 @@ export class CodegenNameUtil { return fieldList; } - /** - * Generates a statement which declares all fields. - * This is only necessary for Dart change detectors. - */ - genDeclareFields(): string { - var fields = this.getAllFieldNames(); - ListWrapper.removeAt(fields, _CONTEXT_IDX); - return ListWrapper.isEmpty(fields) ? '' : `var ${ListWrapper.join(fields, ', ')};`; - } - /** * Generates statements which clear all fields so that the change detector is dehydrated. */ genDehydrateFields(): string { var fields = this.getAllFieldNames(); - ListWrapper.removeAt(fields, _CONTEXT_IDX); + ListWrapper.removeAt(fields, CONTEXT_INDEX); if (!ListWrapper.isEmpty(fields)) { // At least one assignment. fields.push(`${this.utilName}.uninitialized;`); } - return `${this.getContextName()} = null; ${ListWrapper.join(fields, ' = ')}`; + return `${this.getFieldName(CONTEXT_INDEX)} = null; ${ListWrapper.join(fields, ' = ')}`; } /** @@ -116,13 +140,17 @@ export class CodegenNameUtil { }), (r) => { return `${this.getPipeName(r.selfIndex)}.onDestroy();`; }), '\n'); } - getPipeName(idx: int): string { return `${this.fieldPrefix}${this._sanitizedNames[idx]}_pipe`; } + getPipeName(idx: int): string { + return this._addFieldPrefix(`${this._sanitizedNames[idx]}_pipe`); + } getAllDirectiveNames(): List { return ListWrapper.map(this.directiveRecords, d => this.getDirectiveName(d.directiveIndex)); } - getDirectiveName(d: DirectiveIndex): string { return `${this.fieldPrefix}directive_${d.name}`; } + getDirectiveName(d: DirectiveIndex): string { + return this._addFieldPrefix(`directive_${d.name}`); + } getAllDetectorNames(): List { return ListWrapper.map( @@ -130,5 +158,5 @@ export class CodegenNameUtil { (d) => this.getDetectorName(d.directiveIndex)); } - getDetectorName(d: DirectiveIndex): string { return `${this.fieldPrefix}detector_${d.name}`; } + getDetectorName(d: DirectiveIndex): string { return this._addFieldPrefix(`detector_${d.name}`); } } diff --git a/modules/angular2/src/change_detection/dynamic_change_detector.ts b/modules/angular2/src/change_detection/dynamic_change_detector.ts index 13eff93fb6..a0d4bb47de 100644 --- a/modules/angular2/src/change_detection/dynamic_change_detector.ts +++ b/modules/angular2/src/change_detection/dynamic_change_detector.ts @@ -10,19 +10,16 @@ import {ChangeDetectionUtil, SimpleChange} from './change_detection_util'; import {ProtoRecord, RecordType} from './proto_record'; -export class DynamicChangeDetector extends AbstractChangeDetector { - locals: Locals = null; +export class DynamicChangeDetector extends AbstractChangeDetector { values: List; changes: List; localPipes: List; prevContexts: List; directives: any = null; - alreadyChecked: boolean = false; - private pipes: Pipes = null; constructor(id: string, private changeControlStrategy: string, dispatcher: any, - private protos: List, private directiveRecords: List) { - super(id, dispatcher); + protos: List, directiveRecords: List) { + super(id, dispatcher, protos, directiveRecords); this.values = ListWrapper.createFixedSize(protos.length + 1); this.localPipes = ListWrapper.createFixedSize(protos.length + 1); this.prevContexts = ListWrapper.createFixedSize(protos.length + 1); diff --git a/modules/angular2/src/change_detection/interfaces.ts b/modules/angular2/src/change_detection/interfaces.ts index b93698e9c0..6b49661846 100644 --- a/modules/angular2/src/change_detection/interfaces.ts +++ b/modules/angular2/src/change_detection/interfaces.ts @@ -2,7 +2,7 @@ import {List} from 'angular2/src/facade/collection'; import {CONST} from 'angular2/src/facade/lang'; import {Locals} from './parser/locals'; import {BindingRecord} from './binding_record'; -import {DirectiveRecord} from './directive_record'; +import {DirectiveIndex, DirectiveRecord} from './directive_record'; /** * Interface used by Angular to control the change detection strategy for an application. @@ -36,7 +36,13 @@ export class ChangeDetection { } } +export class DebugContext { + constructor(public element: any, public componentElement: any, public directive: any, + public context: any, public locals: any, public injector: any) {} +} + export interface ChangeDispatcher { + getDebugContext(elementIndex: number, directiveIndex: DirectiveIndex): DebugContext; notifyOnBinding(bindingRecord: BindingRecord, value: any): void; notifyOnAllChangesDone(): void; } @@ -58,7 +64,7 @@ export interface ChangeDetector { checkNoChanges(): void; } -export interface ProtoChangeDetector { instantiate(dispatcher: any): ChangeDetector; } +export interface ProtoChangeDetector { instantiate(dispatcher: ChangeDispatcher): ChangeDetector; } export class ChangeDetectorDefinition { constructor(public id: string, public strategy: string, public variableNames: List, diff --git a/modules/angular2/src/core/compiler/view.ts b/modules/angular2/src/core/compiler/view.ts index a4ed079186..7cfadfcd6e 100644 --- a/modules/angular2/src/core/compiler/view.ts +++ b/modules/angular2/src/core/compiler/view.ts @@ -8,15 +8,16 @@ import { } from 'angular2/src/facade/collection'; import { AST, - Locals, - ChangeDispatcher, - ProtoChangeDetector, - ChangeDetector, BindingRecord, - DirectiveRecord, + ChangeDetector, + ChangeDetectorRef, + ChangeDispatcher, DirectiveIndex, - ChangeDetectorRef + DirectiveRecord, + Locals, + ProtoChangeDetector } from 'angular2/src/change_detection/change_detection'; +import {DebugContext} from 'angular2/src/change_detection/interfaces'; import { ProtoElementInjector, @@ -31,6 +32,8 @@ import {RenderEventDispatcher} from 'angular2/src/render/api'; import {ViewRef, ProtoViewRef, internalView} from './view_ref'; import {ElementRef} from './element_ref'; +export {DebugContext} from 'angular2/src/change_detection/interfaces'; + export class AppProtoViewMergeMapping { renderProtoViewRef: renderApi.RenderProtoViewRef; renderFragmentCount: number; @@ -301,11 +304,6 @@ function _localsToStringMap(locals: Locals): StringMap { return res; } -export class DebugContext { - constructor(public element: any, public componentElement: any, public directive: any, - public context: any, public locals: any, public injector: any) {} -} - /** * Error context included when an event handler throws an exception. */ diff --git a/modules/angular2/src/transform/template_compiler/change_detector_codegen.dart b/modules/angular2/src/transform/template_compiler/change_detector_codegen.dart index fc8b81e1fd..a93f913ba7 100644 --- a/modules/angular2/src/transform/template_compiler/change_detector_codegen.dart +++ b/modules/angular2/src/transform/template_compiler/change_detector_codegen.dart @@ -82,7 +82,7 @@ class _CodegenState { this._generateCheckNoChanges) : _records = records, _directiveRecords = directiveRecords, - _names = new CodegenNameUtil(records, directiveRecords, '_', _UTIL), + _names = new CodegenNameUtil(records, directiveRecords, _UTIL), _changeDetectionMode = ChangeDetectionUtil .changeDetectionMode(changeDetectionStrategy); @@ -99,22 +99,12 @@ class _CodegenState { void _writeToBuf(StringBuffer buf) { buf.write('''\n - class $_changeDetectorTypeName extends $_BASE_CLASS { - $_GEN_PREFIX.Pipes $_PIPES_ACCESSOR; - final $_GEN_PREFIX.List<$_GEN_PREFIX.ProtoRecord> $_PROTOS_ACCESSOR; - final $_GEN_PREFIX.List<$_GEN_PREFIX.DirectiveRecord> - $_DIRECTIVES_ACCESSOR; - dynamic $_LOCALS_ACCESSOR = null; - dynamic $_ALREADY_CHECKED_ACCESSOR = false; - dynamic $_CURRENT_PROTO = null; - $_contextTypeName ${_names.getContextName()}; - ${_names.genDeclareFields()} + class $_changeDetectorTypeName extends $_BASE_CLASS<$_contextTypeName> { + ${_genDeclareFields()} - $_changeDetectorTypeName( - dynamic $_DISPATCHER_ACCESSOR, - this.$_PROTOS_ACCESSOR, - this.$_DIRECTIVES_ACCESSOR) - : super(${_encodeValue(_changeDetectorDefId)}, $_DISPATCHER_ACCESSOR) { + $_changeDetectorTypeName(dispatcher, protos, directiveRecords) + : super(${_encodeValue(_changeDetectorDefId)}, + dispatcher, protos, directiveRecords) { dehydrateDirectives(false); } @@ -125,20 +115,20 @@ class _CodegenState { try { __detectChangesInRecords(throwOnChange); } catch (e, s) { - throwError($_CURRENT_PROTO, e, s); + throwError(${_names.getCurrentProtoName()}, e, s); } } void __detectChangesInRecords(throwOnChange) { + ${_names.getCurrentProtoName()} = null; + ${_names.genInitLocals()} var $_IS_CHANGED_LOCAL = false; - $_CURRENT_PROTO = null; var $_CHANGES_LOCAL = null; - context = ${_names.getContextName()}; ${_records.map(_genRecord).join('')} - $_ALREADY_CHECKED_ACCESSOR = true; + ${_names.getAlreadyCheckedName()} = true; } ${_genCheckNoChanges()} @@ -147,26 +137,27 @@ class _CodegenState { ${_getCallOnAllChangesDoneBody()} } - void hydrate($_contextTypeName context, locals, directives, pipes) { - $_MODE_ACCESSOR = '$_changeDetectionMode'; - ${_names.getContextName()} = context; - $_LOCALS_ACCESSOR = locals; + void hydrate( + $_contextTypeName context, locals, directives, pipes) { + ${_names.getModeName()} = '$_changeDetectionMode'; + ${_names.getFieldName(CONTEXT_INDEX)} = context; + ${_names.getLocalsAccessorName()} = locals; hydrateDirectives(directives); - $_ALREADY_CHECKED_ACCESSOR = false; - $_PIPES_ACCESSOR = pipes; + ${_names.getAlreadyCheckedName()} = false; + ${_names.getPipesAccessorName()} = pipes; } ${_maybeGenHydrateDirectives()} void dehydrate() { dehydrateDirectives(true); - $_LOCALS_ACCESSOR = null; - $_PIPES_ACCESSOR = null; + ${_names.getLocalsAccessorName()} = null; + ${_names.getPipesAccessorName()} = null; } ${_maybeGenDehydrateDirectives()} - hydrated() => ${_names.getContextName()} != null; + hydrated() => ${_names.getFieldName(CONTEXT_INDEX)} != null; static $_GEN_PREFIX.ProtoChangeDetector $PROTO_CHANGE_DETECTOR_FACTORY_METHOD( @@ -190,16 +181,12 @@ class _CodegenState { var destroyPipesParamName = 'destroyPipes'; var destroyPipesCode = _names.genPipeOnDestroy(); if (destroyPipesCode.isNotEmpty) { - destroyPipesCode = 'if (${destroyPipesParamName}) { ' - '${destroyPipesCode}' - '}'; + destroyPipesCode = 'if (${destroyPipesParamName}) {${destroyPipesCode}}'; } var dehydrateFieldsCode = _names.genDehydrateFields(); if (destroyPipesCode.isEmpty && dehydrateFieldsCode.isEmpty) return ''; - return 'void dehydrateDirectives(${destroyPipesParamName}) {' - '${destroyPipesCode}' - '${dehydrateFieldsCode}' - '}'; + return 'void dehydrateDirectives(${destroyPipesParamName}) ' + '{ ${destroyPipesCode} ${dehydrateFieldsCode} }'; } String _maybeGenHydrateDirectives() { @@ -208,10 +195,8 @@ class _CodegenState { if (hydrateDirectivesCode.isEmpty && hydrateDetectorsCode.isEmpty) { return ''; } - return 'void hydrateDirectives(directives) { ' - '$hydrateDirectivesCode' - '$hydrateDetectorsCode' - '}'; + return 'void hydrateDirectives(directives) ' + '{ $hydrateDirectivesCode $hydrateDetectorsCode }'; } String _genHydrateDirectives() { @@ -219,7 +204,7 @@ class _CodegenState { var directiveFieldNames = _names.getAllDirectiveNames(); for (var i = 0; i < directiveFieldNames.length; ++i) { buf.writeln('${directiveFieldNames[i]} = directives.getDirectiveFor(' - '$_DIRECTIVES_ACCESSOR[$i].directiveIndex);'); + '${_names.getDirectivesAccessorName()}[$i].directiveIndex);'); } return '$buf'; } @@ -229,7 +214,7 @@ class _CodegenState { var detectorFieldNames = _names.getAllDetectorNames(); for (var i = 0; i < detectorFieldNames.length; ++i) { buf.writeln('${detectorFieldNames[i]} = directives.getDetectorFor(' - '$_DIRECTIVES_ACCESSOR[$i].directiveIndex);'); + '${_names.getDirectivesAccessorName()}[$i].directiveIndex);'); } return '$buf'; } @@ -244,11 +229,22 @@ class _CodegenState { '${_names.getDirectiveName(rec.directiveIndex)}.onAllChangesDone();') .join(''); return ''' - $_DISPATCHER_ACCESSOR.notifyOnAllChangesDone(); + ${_names.getDispatcherName()}.notifyOnAllChangesDone(); ${directiveNotifications} '''; } + String _genDeclareFields() { + var fields = _names.getAllFieldNames(); + // If there's only one field, it's `context`, declared in the superclass. + if (fields.length == 1) return ''; + fields.removeAt(CONTEXT_INDEX); + var toRemove = 'this.'; + var declareNames = fields + .map((f) => f.startsWith(toRemove) ? f.substring(toRemove.length) : f); + return 'var ${declareNames.join(', ')};'; + } + String _genRecord(ProtoRecord r) { var rec = null; if (r.isLifeCycleRecord()) { @@ -287,12 +283,12 @@ class _CodegenState { var protoIndex = r.selfIndex - 1; var pipeType = r.name; return ''' - $_CURRENT_PROTO = $_PROTOS_ACCESSOR[$protoIndex]; + ${_names.getCurrentProtoName()} = ${_names.getProtosName()}[$protoIndex]; if ($_IDENTICAL_CHECK_FN($pipe, $_UTIL.uninitialized)) { - $pipe = $_PIPES_ACCESSOR.get('$pipeType', $context, $cdRef); + $pipe = ${_names.getPipesAccessorName()}.get('$pipeType', $context, $cdRef); } else if (!$pipe.supports($context)) { $pipe.onDestroy(); - $pipe = $_PIPES_ACCESSOR.get('$pipeType', $context, $cdRef); + $pipe = ${_names.getPipesAccessorName()}.get('$pipeType', $context, $cdRef); } $newValue = $pipe.transform($context, [$argString]); @@ -312,7 +308,7 @@ class _CodegenState { var protoIndex = r.selfIndex - 1; var check = ''' - $_CURRENT_PROTO = $_PROTOS_ACCESSOR[$protoIndex]; + ${_names.getCurrentProtoName()} = ${_names.getProtosName()}[$protoIndex]; ${_genUpdateCurrentValue(r)} if ($_NOT_IDENTICAL_CHECK_FN($newValue, $oldValue)) { ${_names.getChangeName(r.selfIndex)} = true; @@ -357,7 +353,7 @@ class _CodegenState { break; case RecordType.LOCAL: - rhs = '$_LOCALS_ACCESSOR.get("${r.name}")'; + rhs = '${_names.getLocalsAccessorName()}.get("${r.name}")'; break; case RecordType.INVOKE_METHOD: @@ -421,8 +417,8 @@ class _CodegenState { } else { return ''' ${_genThrowOnChangeCheck(oldValue, newValue)} - $_DISPATCHER_ACCESSOR.notifyOnBinding( - $_CURRENT_PROTO.bindingRecord, ${newValue}); + ${_names.getDispatcherName()}.notifyOnBinding( + ${_names.getCurrentProtoName()}.bindingRecord, ${newValue}); '''; } } @@ -432,7 +428,7 @@ class _CodegenState { return ''' if(throwOnChange) { $_UTIL.throwOnChange( - $_CURRENT_PROTO, $_UTIL.simpleChange(${oldValue}, ${newValue})); + ${_names.getCurrentProtoName()}, $_UTIL.simpleChange(${oldValue}, ${newValue})); } '''; } else { @@ -455,7 +451,7 @@ class _CodegenState { return ''' $_CHANGES_LOCAL = $_UTIL.addChange( $_CHANGES_LOCAL, - $_CURRENT_PROTO.bindingRecord.propertyName, + ${_names.getCurrentProtoName()}.bindingRecord.propertyName, $_UTIL.simpleChange($oldValue, $newValue)); '''; } @@ -477,7 +473,7 @@ class _CodegenState { String _genOnInit(ProtoRecord r) { var br = r.bindingRecord; - return 'if (!throwOnChange && !$_ALREADY_CHECKED_ACCESSOR) ' + return 'if (!throwOnChange && !${_names.getAlreadyCheckedName()}) ' '${_names.getDirectiveName(br.directiveRecord.directiveIndex)}.onInit();'; } @@ -504,21 +500,13 @@ class _CodegenState { const PROTO_CHANGE_DETECTOR_FACTORY_METHOD = 'newProtoChangeDetector'; -const _ALREADY_CHECKED_ACCESSOR = '_alreadyChecked'; const _BASE_CLASS = '$_GEN_PREFIX.AbstractChangeDetector'; const _CHANGES_LOCAL = 'changes'; -const _CURRENT_PROTO = 'currentProto'; -const _DIRECTIVES_ACCESSOR = '_directiveRecords'; -const _DISPATCHER_ACCESSOR = 'dispatcher'; const _GEN_PREFIX = '_gen'; const _GEN_RECORDS_METHOD_NAME = '_createRecords'; const _IDENTICAL_CHECK_FN = '$_GEN_PREFIX.looseIdentical'; const _NOT_IDENTICAL_CHECK_FN = '$_GEN_PREFIX.looseNotIdentical'; const _IS_CHANGED_LOCAL = 'isChanged'; -const _LOCALS_ACCESSOR = '_locals'; -const _MODE_ACCESSOR = 'mode'; const _PREGEN_PROTO_CHANGE_DETECTOR_IMPORT = 'package:angular2/src/change_detection/pregen_proto_change_detector.dart'; -const _PIPES_ACCESSOR = '_pipes'; -const _PROTOS_ACCESSOR = '_protos'; const _UTIL = '$_GEN_PREFIX.ChangeDetectionUtil'; diff --git a/modules/angular2/test/transform/integration/two_annotations_files/expected/bar.ng_deps.dart b/modules/angular2/test/transform/integration/two_annotations_files/expected/bar.ng_deps.dart index 7433464203..01c1a3240f 100644 --- a/modules/angular2/test/transform/integration/two_annotations_files/expected/bar.ng_deps.dart +++ b/modules/angular2/test/transform/integration/two_annotations_files/expected/bar.ng_deps.dart @@ -23,19 +23,12 @@ void initReflector() { _gen.preGeneratedProtoDetectors['MyComponent_comp_0'] = _MyComponent_ChangeDetector0.newProtoChangeDetector; } -class _MyComponent_ChangeDetector0 extends _gen.AbstractChangeDetector { - _gen.Pipes _pipes; - final _gen.List<_gen.ProtoRecord> _protos; - final _gen.List<_gen.DirectiveRecord> _directiveRecords; - dynamic _locals = null; - dynamic _alreadyChecked = false; - dynamic currentProto = null; - MyComponent _context; - var _myNum0, _interpolate1; +class _MyComponent_ChangeDetector0 + extends _gen.AbstractChangeDetector { + var myNum0, interpolate1; - _MyComponent_ChangeDetector0( - dynamic dispatcher, this._protos, this._directiveRecords) - : super("MyComponent_comp_0", dispatcher) { + _MyComponent_ChangeDetector0(dispatcher, protos, directiveRecords) + : super("MyComponent_comp_0", dispatcher, protos, directiveRecords) { dehydrateDirectives(false); } @@ -46,48 +39,53 @@ class _MyComponent_ChangeDetector0 extends _gen.AbstractChangeDetector { try { __detectChangesInRecords(throwOnChange); } catch (e, s) { - throwError(currentProto, e, s); + throwError(this.currentProto, e, s); } } void __detectChangesInRecords(throwOnChange) { - var context, c_context, myNum0, c_myNum0, interpolate1, c_interpolate1; - c_context = c_myNum0 = c_interpolate1 = false; + this.currentProto = null; + var l_context = this.context, + l_myNum0, + c_myNum0, + l_interpolate1, + c_interpolate1; + c_myNum0 = c_interpolate1 = false; var isChanged = false; - currentProto = null; var changes = null; - context = _context; - currentProto = _protos[0]; - myNum0 = context.myNum; - if (_gen.looseNotIdentical(myNum0, _myNum0)) { + this.currentProto = this.protos[0]; + l_myNum0 = l_context.myNum; + if (_gen.looseNotIdentical(l_myNum0, this.myNum0)) { c_myNum0 = true; - _myNum0 = myNum0; + this.myNum0 = l_myNum0; } if (c_myNum0) { - currentProto = _protos[1]; - interpolate1 = "Salad: " "${myNum0 == null ? "" : myNum0}" " is awesome"; - if (_gen.looseNotIdentical(interpolate1, _interpolate1)) { + this.currentProto = this.protos[1]; + l_interpolate1 = + "Salad: " "${l_myNum0 == null ? "" : l_myNum0}" " is awesome"; + if (_gen.looseNotIdentical(l_interpolate1, this.interpolate1)) { c_interpolate1 = true; if (throwOnChange) { - _gen.ChangeDetectionUtil.throwOnChange(currentProto, + _gen.ChangeDetectionUtil.throwOnChange(this.currentProto, _gen.ChangeDetectionUtil.simpleChange( - _interpolate1, interpolate1)); + this.interpolate1, l_interpolate1)); } - dispatcher.notifyOnBinding(currentProto.bindingRecord, interpolate1); + this.dispatcher.notifyOnBinding( + this.currentProto.bindingRecord, l_interpolate1); - _interpolate1 = interpolate1; + this.interpolate1 = l_interpolate1; } } else { - interpolate1 = _interpolate1; + l_interpolate1 = this.interpolate1; } changes = null; isChanged = false; - _alreadyChecked = true; + this.alreadyChecked = true; } void checkNoChanges() { @@ -95,30 +93,30 @@ class _MyComponent_ChangeDetector0 extends _gen.AbstractChangeDetector { } void callOnAllChangesDone() { - dispatcher.notifyOnAllChangesDone(); + this.dispatcher.notifyOnAllChangesDone(); } void hydrate(MyComponent context, locals, directives, pipes) { - mode = 'ALWAYS_CHECK'; - _context = context; - _locals = locals; + this.mode = 'ALWAYS_CHECK'; + this.context = context; + this.locals = locals; hydrateDirectives(directives); - _alreadyChecked = false; - _pipes = pipes; + this.alreadyChecked = false; + this.pipes = pipes; } void dehydrate() { dehydrateDirectives(true); - _locals = null; - _pipes = null; + this.locals = null; + this.pipes = null; } void dehydrateDirectives(destroyPipes) { - _context = null; - _myNum0 = _interpolate1 = _gen.ChangeDetectionUtil.uninitialized; + this.context = null; + this.myNum0 = this.interpolate1 = _gen.ChangeDetectionUtil.uninitialized; } - hydrated() => _context != null; + hydrated() => this.context != null; static _gen.ProtoChangeDetector newProtoChangeDetector( _gen.ChangeDetectorDefinition def) { diff --git a/modules/benchmarks/src/change_detection/change_detection_benchmark.ts b/modules/benchmarks/src/change_detection/change_detection_benchmark.ts index 357dae7768..f3472712f4 100644 --- a/modules/benchmarks/src/change_detection/change_detection_benchmark.ts +++ b/modules/benchmarks/src/change_detection/change_detection_benchmark.ts @@ -8,6 +8,7 @@ import { Parser, ChangeDispatcher, ChangeDetection, + DebugContext, DynamicChangeDetection, JitChangeDetection, ChangeDetectorDefinition, @@ -380,6 +381,9 @@ class FakeDirectives { } class DummyDispatcher implements ChangeDispatcher { + getDebugContext(elementIndex: number, directiveIndex: DirectiveIndex): DebugContext { + throw "getDebugContext not implemented."; + } notifyOnBinding(bindingRecord, newValue) { throw "Should not be used"; } notifyOnAllChangesDone() {} }