From d2d0715568421b169dd0ecc4ffa5876849d4da9e Mon Sep 17 00:00:00 2001 From: vsavkin Date: Wed, 19 Aug 2015 11:26:45 -0700 Subject: [PATCH] feat(change_detection): do not reparse AST when using generated detectors --- .../abstract_change_detector.ts | 60 ++++------ .../src/change_detection/binding_record.ts | 113 +++++++++++------- .../src/change_detection/change_detection.ts | 17 ++- .../change_detection_jit_generator.ts | 49 +++++--- .../change_detection/change_detection_util.ts | 11 ++ .../angular2/src/change_detection/coalesce.ts | 10 +- .../change_detection/codegen_logic_util.ts | 20 +++- .../src/change_detection/codegen_name_util.ts | 8 +- .../dynamic_change_detector.ts | 25 ++-- .../src/change_detection/exceptions.ts | 14 +-- .../src/change_detection/interfaces.ts | 10 +- .../jit_proto_change_detector.ts | 7 +- .../pregen_proto_change_detector.dart | 26 +--- .../change_detection/proto_change_detector.ts | 49 ++++---- .../src/change_detection/proto_record.ts | 6 +- .../src/core/compiler/proto_view_factory.ts | 53 ++++++-- modules/angular2/src/core/compiler/view.ts | 14 +-- modules/angular2/src/facade/collection.dart | 6 + modules/angular2/src/facade/collection.ts | 5 + .../change_detector_codegen.dart | 49 ++++++-- .../reflection/reflection_capabilities.dart | 2 +- .../change_detection/change_detection_spec.ts | 4 +- .../change_detection/change_detector_spec.ts | 4 +- .../test/change_detection/coalesce_spec.ts | 5 +- .../proto_record_builder_spec.ts | 4 +- .../change_detection/proto_record_spec.ts | 4 +- .../core/compiler/element_injector_spec.ts | 4 +- .../core/compiler/proto_view_factory_spec.ts | 69 ++++++----- .../angular2/test/facade/collection_spec.ts | 12 ++ .../expected/bar.ng_deps.dart | 35 ++++-- .../change_detection_benchmark.ts | 7 +- 31 files changed, 423 insertions(+), 279 deletions(-) diff --git a/modules/angular2/src/change_detection/abstract_change_detector.ts b/modules/angular2/src/change_detection/abstract_change_detector.ts index 82efc68b46..98abbbe6e9 100644 --- a/modules/angular2/src/change_detection/abstract_change_detector.ts +++ b/modules/angular2/src/change_detection/abstract_change_detector.ts @@ -2,7 +2,7 @@ import {isPresent, isBlank, BaseException, StringWrapper} from 'angular2/src/fac import {List, ListWrapper} from 'angular2/src/facade/collection'; import {ChangeDetectionUtil} from './change_detection_util'; import {ChangeDetectorRef} from './change_detector_ref'; -import {DirectiveRecord} from './directive_record'; +import {DirectiveIndex} from './directive_record'; import {ChangeDetector, ChangeDispatcher} from './interfaces'; import {Pipes} from './pipes'; import { @@ -10,8 +10,7 @@ import { ExpressionChangedAfterItHasBeenCheckedException, DehydratedException } from './exceptions'; -import {ProtoRecord} from './proto_record'; -import {BindingRecord} from './binding_record'; +import {BindingTarget} from './binding_record'; import {Locals} from './parser/locals'; import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from './constants'; import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile'; @@ -20,9 +19,8 @@ import {isObservable} from './observable_facade'; var _scope_check: WtfScopeFn = wtfCreateScope(`ChangeDetector#check(ascii id, bool throwOnChange)`); class _Context { - constructor(public element: any, public componentElement: any, public instance: any, - public context: any, public locals: any, public injector: any, - public expression: any) {} + constructor(public element: any, public componentElement: any, public context: any, + public locals: any, public injector: any, public expression: any) {} } export class AbstractChangeDetector implements ChangeDetector { @@ -35,24 +33,19 @@ export class AbstractChangeDetector implements ChangeDetector { // change detection will fail. alreadyChecked: any = false; context: T; - directiveRecords: List; - dispatcher: ChangeDispatcher; locals: Locals = null; mode: string = null; pipes: Pipes = null; - firstProtoInCurrentBinding: number; - protos: List; + propertyBindingIndex: number; // This is an experimental feature. Works only in Dart. subscriptions: any[]; streams: any[]; - constructor(public id: string, dispatcher: ChangeDispatcher, protos: List, - directiveRecords: List, public modeOnHydrate: string) { + constructor(public id: string, public dispatcher: ChangeDispatcher, + public numberOfPropertyProtoRecords: number, public bindingTargets: BindingTarget[], + public directiveIndices: DirectiveIndex[], public modeOnHydrate: string) { this.ref = new ChangeDetectorRef(this); - this.directiveRecords = directiveRecords; - this.dispatcher = dispatcher; - this.protos = protos; } addChild(cd: ChangeDetector): void { @@ -99,7 +92,7 @@ export class AbstractChangeDetector implements ChangeDetector { // implementation of `detectChangesInRecordsInternal` which does the work of detecting changes // and which this method will call. // This method expects that `detectChangesInRecordsInternal` will set the property - // `this.firstProtoInCurrentBinding` to the selfIndex of the first proto record. This is to + // `this.propertyBindingIndex` to the propertyBindingIndex of the first proto record. This is to // facilitate error reporting. detectChangesInRecords(throwOnChange: boolean): void { if (!this.hydrated()) { @@ -115,9 +108,9 @@ export class AbstractChangeDetector implements ChangeDetector { // Subclasses should override this method to perform any work necessary to detect and report // changes. For example, changes should be reported via `ChangeDetectionUtil.addChange`, lifecycle // methods should be called, etc. - // This implementation should also set `this.firstProtoInCurrentBinding` to the selfIndex of the - // first proto record - // to facilitate error reporting. See {@link #detectChangesInRecords}. + // This implementation should also set `this.propertyBindingIndex` to the propertyBindingIndex of + // the + // first proto record to facilitate error reporting. See {@link #detectChangesInRecords}. detectChangesInRecordsInternal(throwOnChange: boolean): void {} // This method is not intended to be overridden. Subclasses should instead provide an @@ -195,8 +188,8 @@ export class AbstractChangeDetector implements ChangeDetector { protected observe(value: any, index: number): any { if (isObservable(value)) { if (isBlank(this.subscriptions)) { - this.subscriptions = ListWrapper.createFixedSize(this.protos.length + 1); - this.streams = ListWrapper.createFixedSize(this.protos.length + 1); + this.subscriptions = ListWrapper.createFixedSize(this.numberOfPropertyProtoRecords + 1); + this.streams = ListWrapper.createFixedSize(this.numberOfPropertyProtoRecords + 1); } if (isBlank(this.subscriptions[index])) { this.streams[index] = value.changes; @@ -211,7 +204,7 @@ export class AbstractChangeDetector implements ChangeDetector { } protected getDetectorFor(directives: any, index: number): ChangeDetector { - return directives.getDetectorFor(this.directiveRecords[index].directiveIndex); + return directives.getDetectorFor(this.directiveIndices[index]); } protected notifyDispatcher(value: any): void { @@ -223,31 +216,26 @@ export class AbstractChangeDetector implements ChangeDetector { if (isBlank(changes)) { changes = {}; } - changes[this._currentBinding().propertyName] = - ChangeDetectionUtil.simpleChange(oldValue, newValue); + changes[this._currentBinding().name] = ChangeDetectionUtil.simpleChange(oldValue, newValue); return changes; } private _throwError(exception: any, stack: any): void { - var proto = this._currentBindingProto(); - var c = this.dispatcher.getDebugContext(proto.bindingRecord.elementIndex, proto.directiveIndex); - var context = isPresent(c) ? new _Context(c.element, c.componentElement, c.directive, c.context, - c.locals, c.injector, proto.expressionAsString) : + var c = this.dispatcher.getDebugContext(this._currentBinding().elementIndex, null); + var context = isPresent(c) ? new _Context(c.element, c.componentElement, c.context, c.locals, + c.injector, this._currentBinding().debug) : null; - throw new ChangeDetectionError(proto, exception, stack, context); + throw new ChangeDetectionError(this._currentBinding().debug, exception, stack, context); } protected throwOnChangeError(oldValue: any, newValue: any): void { - var change = ChangeDetectionUtil.simpleChange(oldValue, newValue); - throw new ExpressionChangedAfterItHasBeenCheckedException(this._currentBindingProto(), change, - null); + throw new ExpressionChangedAfterItHasBeenCheckedException(this._currentBinding().debug, + oldValue, newValue, null); } protected throwDehydratedError(): void { throw new DehydratedException(); } - private _currentBinding(): BindingRecord { return this._currentBindingProto().bindingRecord; } - - private _currentBindingProto(): ProtoRecord { - return ChangeDetectionUtil.protoByIndex(this.protos, this.firstProtoInCurrentBinding); + private _currentBinding(): BindingTarget { + return this.bindingTargets[this.propertyBindingIndex]; } } diff --git a/modules/angular2/src/change_detection/binding_record.ts b/modules/angular2/src/change_detection/binding_record.ts index b92fd4312e..272ab345b0 100644 --- a/modules/angular2/src/change_detection/binding_record.ts +++ b/modules/angular2/src/change_detection/binding_record.ts @@ -3,8 +3,10 @@ import {SetterFn} from 'angular2/src/reflection/types'; import {AST} from './parser/ast'; import {DirectiveIndex, DirectiveRecord} from './directive_record'; -const DIRECTIVE = "directive"; const DIRECTIVE_LIFECYCLE = "directiveLifecycle"; +const BINDING = "native"; + +const DIRECTIVE = "directive"; const ELEMENT_PROPERTY = "elementProperty"; const ELEMENT_ATTRIBUTE = "elementAttribute"; const ELEMENT_CLASS = "elementClass"; @@ -13,24 +15,12 @@ const TEXT_NODE = "textNode"; const EVENT = "event"; const HOST_EVENT = "hostEvent"; -export class BindingRecord { - constructor(public mode: string, public implicitReceiver: any, public ast: AST, - public elementIndex: number, public propertyName: string, public propertyUnit: string, - public eventName: string, public setter: SetterFn, public lifecycleEvent: string, - public directiveRecord: DirectiveRecord) {} - - callOnChange(): boolean { - return isPresent(this.directiveRecord) && this.directiveRecord.callOnChange; - } - - isDefaultChangeDetection(): boolean { - return isBlank(this.directiveRecord) || this.directiveRecord.isDefaultChangeDetection(); - } +export class BindingTarget { + constructor(public mode: string, public elementIndex: number, public name: string, + public unit: string, public debug: string) {} isDirective(): boolean { return this.mode === DIRECTIVE; } - isDirectiveLifecycle(): boolean { return this.mode === DIRECTIVE_LIFECYCLE; } - isElementProperty(): boolean { return this.mode === ELEMENT_PROPERTY; } isElementAttribute(): boolean { return this.mode === ELEMENT_ATTRIBUTE; } @@ -40,87 +30,118 @@ export class BindingRecord { isElementStyle(): boolean { return this.mode === ELEMENT_STYLE; } isTextNode(): boolean { return this.mode === TEXT_NODE; } +} - static createForDirective(ast: AST, propertyName: string, setter: SetterFn, - directiveRecord: DirectiveRecord): BindingRecord { - return new BindingRecord(DIRECTIVE, 0, ast, 0, propertyName, null, null, setter, null, - directiveRecord); +export class BindingRecord { + constructor(public mode: string, public target: BindingTarget, public implicitReceiver: any, + public ast: AST, public setter: SetterFn, public lifecycleEvent: string, + public directiveRecord: DirectiveRecord) {} + + isDirectiveLifecycle(): boolean { return this.mode === DIRECTIVE_LIFECYCLE; } + + callOnChange(): boolean { + return isPresent(this.directiveRecord) && this.directiveRecord.callOnChange; } + isDefaultChangeDetection(): boolean { + return isBlank(this.directiveRecord) || this.directiveRecord.isDefaultChangeDetection(); + } + + static createDirectiveOnCheck(directiveRecord: DirectiveRecord): BindingRecord { - return new BindingRecord(DIRECTIVE_LIFECYCLE, 0, null, 0, null, null, null, null, "onCheck", - directiveRecord); + return new BindingRecord(DIRECTIVE_LIFECYCLE, null, 0, null, null, "onCheck", directiveRecord); } static createDirectiveOnInit(directiveRecord: DirectiveRecord): BindingRecord { - return new BindingRecord(DIRECTIVE_LIFECYCLE, 0, null, 0, null, null, null, null, "onInit", - directiveRecord); + return new BindingRecord(DIRECTIVE_LIFECYCLE, null, 0, null, null, "onInit", directiveRecord); } static createDirectiveOnChange(directiveRecord: DirectiveRecord): BindingRecord { - return new BindingRecord(DIRECTIVE_LIFECYCLE, 0, null, 0, null, null, null, null, "onChange", - directiveRecord); + return new BindingRecord(DIRECTIVE_LIFECYCLE, null, 0, null, null, "onChange", directiveRecord); } + + + static createForDirective(ast: AST, propertyName: string, setter: SetterFn, + directiveRecord: DirectiveRecord): BindingRecord { + var t = new BindingTarget(DIRECTIVE, null, propertyName, null, ast.toString()); + return new BindingRecord(DIRECTIVE, t, 0, ast, setter, null, directiveRecord); + } + + + static createForElementProperty(ast: AST, elementIndex: number, propertyName: string): BindingRecord { - return new BindingRecord(ELEMENT_PROPERTY, 0, ast, elementIndex, propertyName, null, null, null, - null, null); + var t = new BindingTarget(ELEMENT_PROPERTY, elementIndex, propertyName, null, ast.toString()); + return new BindingRecord(BINDING, t, 0, ast, null, null, null); } static createForElementAttribute(ast: AST, elementIndex: number, attributeName: string): BindingRecord { - return new BindingRecord(ELEMENT_ATTRIBUTE, 0, ast, elementIndex, attributeName, null, null, - null, null, null); + var t = new BindingTarget(ELEMENT_ATTRIBUTE, elementIndex, attributeName, null, ast.toString()); + return new BindingRecord(BINDING, t, 0, ast, null, null, null); } static createForElementClass(ast: AST, elementIndex: number, className: string): BindingRecord { - return new BindingRecord(ELEMENT_CLASS, 0, ast, elementIndex, className, null, null, null, null, - null); + var t = new BindingTarget(ELEMENT_CLASS, elementIndex, className, null, ast.toString()); + return new BindingRecord(BINDING, t, 0, ast, null, null, null); } static createForElementStyle(ast: AST, elementIndex: number, styleName: string, unit: string): BindingRecord { - return new BindingRecord(ELEMENT_STYLE, 0, ast, elementIndex, styleName, unit, null, null, null, - null); + var t = new BindingTarget(ELEMENT_STYLE, elementIndex, styleName, unit, ast.toString()); + return new BindingRecord(BINDING, t, 0, ast, null, null, null); } + + static createForHostProperty(directiveIndex: DirectiveIndex, ast: AST, propertyName: string): BindingRecord { - return new BindingRecord(ELEMENT_PROPERTY, directiveIndex, ast, directiveIndex.elementIndex, - propertyName, null, null, null, null, null); + var t = new BindingTarget(ELEMENT_PROPERTY, directiveIndex.elementIndex, propertyName, null, + ast.toString()); + return new BindingRecord(BINDING, t, directiveIndex, ast, null, null, null); } static createForHostAttribute(directiveIndex: DirectiveIndex, ast: AST, attributeName: string): BindingRecord { - return new BindingRecord(ELEMENT_ATTRIBUTE, directiveIndex, ast, directiveIndex.elementIndex, - attributeName, null, null, null, null, null); + var t = new BindingTarget(ELEMENT_ATTRIBUTE, directiveIndex.elementIndex, attributeName, null, + ast.toString()); + return new BindingRecord(BINDING, t, directiveIndex, ast, null, null, null); } static createForHostClass(directiveIndex: DirectiveIndex, ast: AST, className: string): BindingRecord { - return new BindingRecord(ELEMENT_CLASS, directiveIndex, ast, directiveIndex.elementIndex, - className, null, null, null, null, null); + var t = new BindingTarget(ELEMENT_CLASS, directiveIndex.elementIndex, className, null, + ast.toString()); + return new BindingRecord(BINDING, t, directiveIndex, ast, null, null, null); } static createForHostStyle(directiveIndex: DirectiveIndex, ast: AST, styleName: string, unit: string): BindingRecord { - return new BindingRecord(ELEMENT_STYLE, directiveIndex, ast, directiveIndex.elementIndex, - styleName, unit, null, null, null, null); + var t = new BindingTarget(ELEMENT_STYLE, directiveIndex.elementIndex, styleName, unit, + ast.toString()); + return new BindingRecord(BINDING, t, directiveIndex, ast, null, null, null); } + + static createForTextNode(ast: AST, elementIndex: number): BindingRecord { - return new BindingRecord(TEXT_NODE, 0, ast, elementIndex, null, null, null, null, null, null); + var t = new BindingTarget(TEXT_NODE, elementIndex, null, null, ast.toString()); + return new BindingRecord(BINDING, t, 0, ast, null, null, null); } + + static createForEvent(ast: AST, eventName: string, elementIndex: number): BindingRecord { - return new BindingRecord(EVENT, 0, ast, elementIndex, null, null, eventName, null, null, null); + var t = new BindingTarget(EVENT, elementIndex, eventName, null, ast.toString()); + return new BindingRecord(EVENT, t, 0, ast, null, null, null); } static createForHostEvent(ast: AST, eventName: string, directiveRecord: DirectiveRecord): BindingRecord { var directiveIndex = directiveRecord.directiveIndex; - return new BindingRecord(EVENT, directiveIndex, ast, directiveIndex.elementIndex, null, null, - eventName, null, null, directiveRecord); + var t = + new BindingTarget(HOST_EVENT, directiveIndex.elementIndex, eventName, null, ast.toString()); + return new BindingRecord(HOST_EVENT, t, directiveIndex, ast, null, null, directiveRecord); } } diff --git a/modules/angular2/src/change_detection/change_detection.ts b/modules/angular2/src/change_detection/change_detection.ts index e189844ba0..68bd95d9f4 100644 --- a/modules/angular2/src/change_detection/change_detection.ts +++ b/modules/angular2/src/change_detection/change_detection.ts @@ -38,7 +38,7 @@ export { } from './interfaces'; export {CHECK_ONCE, CHECK_ALWAYS, DETACHED, CHECKED, ON_PUSH, DEFAULT} from './constants'; export {DynamicProtoChangeDetector} from './proto_change_detector'; -export {BindingRecord} from './binding_record'; +export {BindingRecord, BindingTarget} from './binding_record'; export {DirectiveIndex, DirectiveRecord} from './directive_record'; export {DynamicChangeDetector} from './dynamic_change_detector'; export {ChangeDetectorRef} from './change_detector_ref'; @@ -93,13 +93,14 @@ export class PreGeneratedChangeDetection extends ChangeDetection { static isSupported(): boolean { return PregenProtoChangeDetector.isSupported(); } - createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector { - var id = definition.id; + getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector { if (StringMapWrapper.contains(this._protoChangeDetectorFactories, id)) { return StringMapWrapper.get(this._protoChangeDetectorFactories, id)(definition); } - return this._dynamicChangeDetection.createProtoChangeDetector(definition); + return this._dynamicChangeDetection.getProtoChangeDetector(id, definition); } + + get generateDetectors(): boolean { return true; } } @@ -110,9 +111,11 @@ export class PreGeneratedChangeDetection extends ChangeDetection { */ @Injectable() export class DynamicChangeDetection extends ChangeDetection { - createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector { + getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector { return new DynamicProtoChangeDetector(definition); } + + get generateDetectors(): boolean { return true; } } /** @@ -126,7 +129,9 @@ export class DynamicChangeDetection extends ChangeDetection { export class JitChangeDetection extends ChangeDetection { static isSupported(): boolean { return JitProtoChangeDetector.isSupported(); } - createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector { + getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector { return new JitProtoChangeDetector(definition); } + + get generateDetectors(): boolean { return true; } } 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 78609c0081..3ba1564d5f 100644 --- a/modules/angular2/src/change_detection/change_detection_jit_generator.ts +++ b/modules/angular2/src/change_detection/change_detection_jit_generator.ts @@ -9,6 +9,7 @@ import {ProtoRecord, RecordType} from './proto_record'; import {CodegenNameUtil, sanitizeName} from './codegen_name_util'; import {CodegenLogicUtil} from './codegen_logic_util'; import {EventBinding} from './event_binding'; +import {BindingTarget} from './binding_record'; /** @@ -30,9 +31,10 @@ export class ChangeDetectorJITGenerator { _names: CodegenNameUtil; _typeName: string; - constructor(public id: string, private changeDetectionStrategy: string, - public records: List, public eventBindings: EventBinding[], - public directiveRecords: List, private generateCheckNoChanges: boolean) { + constructor(private id: string, private changeDetectionStrategy: string, + private records: List, private propertyBindingTargets: BindingTarget[], + private eventBindings: EventBinding[], private directiveRecords: List, + private devMode: boolean) { this._names = new CodegenNameUtil(this.records, this.eventBindings, this.directiveRecords, UTIL); this._logic = new CodegenLogicUtil(this._names, UTIL, changeDetectionStrategy); @@ -41,9 +43,10 @@ export class ChangeDetectorJITGenerator { generate(): Function { var classDefinition = ` - var ${this._typeName} = function ${this._typeName}(dispatcher, protos, directiveRecords) { + var ${this._typeName} = function ${this._typeName}(dispatcher) { ${ABSTRACT_CHANGE_DETECTOR}.call( - this, ${JSON.stringify(this.id)}, dispatcher, protos, directiveRecords, + this, ${JSON.stringify(this.id)}, dispatcher, ${this.records.length}, + ${this._typeName}.gen_propertyBindingTargets, ${this._typeName}.gen_directiveIndices, "${ChangeDetectionUtil.changeDetectionMode(this.changeDetectionStrategy)}"); this.dehydrateDirectives(false); } @@ -70,13 +73,27 @@ export class ChangeDetectorJITGenerator { ${this._maybeGenDehydrateDirectives()} + ${this._genPropertyBindingTargets()}; + + ${this._genDirectiveIndices()}; + return function(dispatcher) { - return new ${this._typeName}(dispatcher, protos, directiveRecords); + return new ${this._typeName}(dispatcher); } `; - return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos', - 'directiveRecords', classDefinition)( - AbstractChangeDetector, ChangeDetectionUtil, this.records, this.directiveRecords); + + return new Function(ABSTRACT_CHANGE_DETECTOR, UTIL, classDefinition)(AbstractChangeDetector, + ChangeDetectionUtil); + } + + _genPropertyBindingTargets(): string { + var targets = this._logic.genPropertyBindingTargets(this.propertyBindingTargets, true); + return `${this._typeName}.gen_propertyBindingTargets = ${targets};`; + } + + _genDirectiveIndices(): string { + var indices = this._logic.genDirectiveIndices(this.directiveRecords); + return `${this._typeName}.gen_directiveIndices = ${indices};`; } _maybeGenHandleEventInternal(): string { @@ -156,7 +173,7 @@ export class ChangeDetectorJITGenerator { var lines = ListWrapper.createFixedSize(directiveFieldNames.length); for (var i = 0, iLen = directiveFieldNames.length; i < iLen; ++i) { lines[i] = `${directiveFieldNames[i]} = directives.getDirectiveFor( - ${this._names.getDirectivesAccessorName()}[${i}].directiveIndex);`; + ${this._names.getDirectivesAccessorName()}[${i}]);`; } return lines.join('\n'); } @@ -284,9 +301,9 @@ export class ChangeDetectorJITGenerator { var oldValue = this._names.getFieldName(r.selfIndex); var br = r.bindingRecord; - if (br.isDirective()) { + if (br.target.isDirective()) { var directiveProperty = - `${this._names.getDirectiveName(br.directiveRecord.directiveIndex)}.${br.propertyName}`; + `${this._names.getDirectiveName(br.directiveRecord.directiveIndex)}.${br.target.name}`; return ` ${this._genThrowOnChangeCheck(oldValue, newValue)} ${directiveProperty} = ${newValue}; @@ -301,7 +318,7 @@ export class ChangeDetectorJITGenerator { } _genThrowOnChangeCheck(oldValue: string, newValue: string): string { - if (this.generateCheckNoChanges) { + if (this.devMode) { return ` if(throwOnChange) { this.throwOnChangeError(${oldValue}, ${newValue}); @@ -313,7 +330,7 @@ export class ChangeDetectorJITGenerator { } _genCheckNoChanges(): string { - if (this.generateCheckNoChanges) { + if (this.devMode) { return `${this._typeName}.prototype.checkNoChanges = function() { this.runDetectChanges(true); }`; } else { return ''; @@ -330,7 +347,9 @@ export class ChangeDetectorJITGenerator { _maybeFirstInBinding(r: ProtoRecord): string { var prev = ChangeDetectionUtil.protoByIndex(this.records, r.selfIndex - 1); var firstInBindng = isBlank(prev) || prev.bindingRecord !== r.bindingRecord; - return firstInBindng ? `${this._names.getFirstProtoInCurrentBinding()} = ${r.selfIndex};` : ''; + return firstInBindng && !r.bindingRecord.isDirectiveLifecycle() ? + `${this._names.getPropertyBindingIndex()} = ${r.propertyBindingIndex};` : + ''; } _maybeGenLastInDirective(r: ProtoRecord): string { diff --git a/modules/angular2/src/change_detection/change_detection_util.ts b/modules/angular2/src/change_detection/change_detection_util.ts index f05faa6457..d8784ecb26 100644 --- a/modules/angular2/src/change_detection/change_detection_util.ts +++ b/modules/angular2/src/change_detection/change_detection_util.ts @@ -16,6 +16,8 @@ import { isDefaultChangeDetectionStrategy } from './constants'; import {implementsOnDestroy} from './pipe_lifecycle_reflector'; +import {BindingTarget} from './binding_record'; +import {DirectiveIndex} from './directive_record'; /** @@ -201,4 +203,13 @@ export class ChangeDetectionUtil { pipe.onDestroy(); } } + + static bindingTarget(mode: string, elementIndex: number, name: string, unit: string, + debug: string): BindingTarget { + return new BindingTarget(mode, elementIndex, name, unit, debug); + } + + static directiveIndex(elementIndex: number, directiveIndex: number): DirectiveIndex { + return new DirectiveIndex(elementIndex, directiveIndex); + } } diff --git a/modules/angular2/src/change_detection/coalesce.ts b/modules/angular2/src/change_detection/coalesce.ts index d2d7796437..d5353a1506 100644 --- a/modules/angular2/src/change_detection/coalesce.ts +++ b/modules/angular2/src/change_detection/coalesce.ts @@ -44,8 +44,8 @@ export function coalesce(records: ProtoRecord[]): ProtoRecord[] { function _selfRecord(r: ProtoRecord, contextIndex: number, selfIndex: number): ProtoRecord { return new ProtoRecord(RecordType.SELF, "self", null, [], r.fixedArgs, contextIndex, - r.directiveIndex, selfIndex, r.bindingRecord, r.expressionAsString, - r.lastInBinding, r.lastInDirective, false, false); + r.directiveIndex, selfIndex, r.bindingRecord, r.lastInBinding, + r.lastInDirective, false, false, r.propertyBindingIndex); } function _findMatching(r: ProtoRecord, rs: List) { @@ -70,9 +70,9 @@ function _replaceIndices(r: ProtoRecord, selfIndex: number, indexMap: Map _map(indexMap, a)); var contextIndex = _map(indexMap, r.contextIndex); return new ProtoRecord(r.mode, r.name, r.funcOrValue, args, r.fixedArgs, contextIndex, - r.directiveIndex, selfIndex, r.bindingRecord, r.expressionAsString, - r.lastInBinding, r.lastInDirective, r.argumentToPureFunction, - r.referencedBySelf); + r.directiveIndex, selfIndex, r.bindingRecord, r.lastInBinding, + r.lastInDirective, r.argumentToPureFunction, r.referencedBySelf, + r.propertyBindingIndex); } function _map(indexMap: Map, value: number) { diff --git a/modules/angular2/src/change_detection/codegen_logic_util.ts b/modules/angular2/src/change_detection/codegen_logic_util.ts index 1181c82432..1dd632ed84 100644 --- a/modules/angular2/src/change_detection/codegen_logic_util.ts +++ b/modules/angular2/src/change_detection/codegen_logic_util.ts @@ -1,8 +1,9 @@ import {ListWrapper} from 'angular2/src/facade/collection'; -import {BaseException, Json, StringWrapper} from 'angular2/src/facade/lang'; +import {BaseException, Json, StringWrapper, isPresent, isBlank} from 'angular2/src/facade/lang'; import {CodegenNameUtil} from './codegen_name_util'; import {codify, combineGeneratedStrings, rawString} from './codegen_facade'; import {ProtoRecord, RecordType} from './proto_record'; +import {BindingTarget} from './binding_record'; import {DirectiveRecord} from './directive_record'; /** @@ -123,6 +124,23 @@ export class CodegenLogicUtil { } } + genPropertyBindingTargets(propertyBindingTargets: BindingTarget[], devMode: boolean): string { + var bs = propertyBindingTargets.map(b => { + if (isBlank(b)) return "null"; + + var debug = devMode ? codify(b.debug) : "null"; + return `${this._utilName}.bindingTarget(${codify(b.mode)}, ${b.elementIndex}, ${codify(b.name)}, ${codify(b.unit)}, ${debug})`; + }); + return `[${bs.join(", ")}]`; + } + + genDirectiveIndices(directiveRecords: DirectiveRecord[]): string { + var bs = directiveRecords.map( + b => + `${this._utilName}.directiveIndex(${b.directiveIndex.elementIndex}, ${b.directiveIndex.directiveIndex})`); + return `[${bs.join(", ")}]`; + } + _genInterpolation(protoRec: ProtoRecord): string { var iVals = []; for (var i = 0; i < protoRec.args.length; ++i) { diff --git a/modules/angular2/src/change_detection/codegen_name_util.ts b/modules/angular2/src/change_detection/codegen_name_util.ts index eb5aaced3a..53694a0dc8 100644 --- a/modules/angular2/src/change_detection/codegen_name_util.ts +++ b/modules/angular2/src/change_detection/codegen_name_util.ts @@ -10,8 +10,8 @@ import {EventBinding} from './event_binding'; // detection will fail. const _ALREADY_CHECKED_ACCESSOR = "alreadyChecked"; const _CONTEXT_ACCESSOR = "context"; -const _FIRST_PROTO_IN_CURRENT_BINDING = "firstProtoInCurrentBinding"; -const _DIRECTIVES_ACCESSOR = "directiveRecords"; +const _PROP_BINDING_INDEX = "propertyBindingIndex"; +const _DIRECTIVES_ACCESSOR = "directiveIndices"; const _DISPATCHER_ACCESSOR = "dispatcher"; const _LOCALS_ACCESSOR = "locals"; const _MODE_ACCESSOR = "mode"; @@ -79,9 +79,7 @@ export class CodegenNameUtil { getModeName(): string { return this._addFieldPrefix(_MODE_ACCESSOR); } - getFirstProtoInCurrentBinding(): string { - return this._addFieldPrefix(_FIRST_PROTO_IN_CURRENT_BINDING); - } + getPropertyBindingIndex(): string { return this._addFieldPrefix(_PROP_BINDING_INDEX); } getLocalName(idx: number): string { return `l_${this._sanitizedNames[idx]}`; } diff --git a/modules/angular2/src/change_detection/dynamic_change_detector.ts b/modules/angular2/src/change_detection/dynamic_change_detector.ts index ef6d98d557..2d23b2f126 100644 --- a/modules/angular2/src/change_detection/dynamic_change_detector.ts +++ b/modules/angular2/src/change_detection/dynamic_change_detector.ts @@ -9,11 +9,11 @@ import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/faca import {AbstractChangeDetector} from './abstract_change_detector'; import {EventBinding} from './event_binding'; -import {BindingRecord} from './binding_record'; +import {BindingRecord, BindingTarget} from './binding_record'; +import {DirectiveRecord, DirectiveIndex} from './directive_record'; import {Locals} from './parser/locals'; import {ChangeDetectionUtil, SimpleChange} from './change_detection_util'; - import {ProtoRecord, RecordType} from './proto_record'; export class DynamicChangeDetector extends AbstractChangeDetector { @@ -23,12 +23,13 @@ export class DynamicChangeDetector extends AbstractChangeDetector { prevContexts: List; directives: any = null; - constructor(id: string, changeDetectionStrategy: string, dispatcher: any, - protos: List, public eventBindings: EventBinding[], - directiveRecords: List) { - super(id, dispatcher, protos, directiveRecords, - ChangeDetectionUtil.changeDetectionMode(changeDetectionStrategy)); - var len = protos.length + 1; + constructor(id: string, dispatcher: any, numberOfPropertyProtoRecords: number, + propertyBindingTargets: BindingTarget[], directiveIndices: DirectiveIndex[], + modeOnHydrate: string, private records: ProtoRecord[], + private eventBindings: EventBinding[], private directiveRecords: DirectiveRecord[]) { + super(id, dispatcher, numberOfPropertyProtoRecords, propertyBindingTargets, directiveIndices, + modeOnHydrate); + var len = records.length + 1; this.values = ListWrapper.createFixedSize(len); this.localPipes = ListWrapper.createFixedSize(len); this.prevContexts = ListWrapper.createFixedSize(len); @@ -109,7 +110,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector { checkNoChanges(): void { this.runDetectChanges(true); } detectChangesInRecordsInternal(throwOnChange: boolean) { - var protos = this.protos; + var protos = this.records; var changes = null; var isChanged = false; @@ -119,7 +120,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector { var directiveRecord = bindingRecord.directiveRecord; if (this._firstInBinding(proto)) { - this.firstProtoInCurrentBinding = proto.selfIndex; + this.propertyBindingIndex = proto.propertyBindingIndex; } if (proto.isLifeCycleRecord()) { @@ -154,7 +155,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector { } _firstInBinding(r: ProtoRecord): boolean { - var prev = ChangeDetectionUtil.protoByIndex(this.protos, r.selfIndex - 1); + var prev = ChangeDetectionUtil.protoByIndex(this.records, r.selfIndex - 1); return isBlank(prev) || prev.bindingRecord !== r.bindingRecord; } @@ -171,7 +172,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector { _updateDirectiveOrElement(change, bindingRecord) { if (isBlank(bindingRecord.directiveRecord)) { - this.dispatcher.notifyOnBinding(bindingRecord, change.currentValue); + super.notifyDispatcher(change.currentValue); } else { var directiveIndex = bindingRecord.directiveRecord.directiveIndex; bindingRecord.setter(this._getDirectiveFor(directiveIndex), change.currentValue); diff --git a/modules/angular2/src/change_detection/exceptions.ts b/modules/angular2/src/change_detection/exceptions.ts index 520da217ed..e235d7371f 100644 --- a/modules/angular2/src/change_detection/exceptions.ts +++ b/modules/angular2/src/change_detection/exceptions.ts @@ -1,4 +1,3 @@ -import {ProtoRecord} from './proto_record'; import {BaseException} from "angular2/src/facade/lang"; /** @@ -11,9 +10,9 @@ import {BaseException} from "angular2/src/facade/lang"; * This exception is only thrown in dev mode. */ export class ExpressionChangedAfterItHasBeenCheckedException extends BaseException { - constructor(proto: ProtoRecord, change: any, context: any) { - super(`Expression '${proto.expressionAsString}' has changed after it was checked. ` + - `Previous value: '${change.previousValue}'. Current value: '${change.currentValue}'`); + constructor(exp: string, oldValue: any, currValue: any, context: any) { + super(`Expression '${exp}' has changed after it was checked. ` + + `Previous value: '${oldValue}'. Current value: '${currValue}'`); } } @@ -28,10 +27,9 @@ export class ChangeDetectionError extends BaseException { */ location: string; - constructor(proto: ProtoRecord, originalException: any, originalStack: any, context: any) { - super(`${originalException} in [${proto.expressionAsString}]`, originalException, originalStack, - context); - this.location = proto.expressionAsString; + constructor(exp: string, originalException: any, originalStack: any, context: any) { + super(`${originalException} in [${exp}]`, originalException, originalStack, context); + this.location = exp; } } diff --git a/modules/angular2/src/change_detection/interfaces.ts b/modules/angular2/src/change_detection/interfaces.ts index b5d45ebf3e..08b9ed5ee7 100644 --- a/modules/angular2/src/change_detection/interfaces.ts +++ b/modules/angular2/src/change_detection/interfaces.ts @@ -1,7 +1,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 {BindingTarget, BindingRecord} from './binding_record'; import {DirectiveIndex, DirectiveRecord} from './directive_record'; import {ChangeDetectorRef} from './change_detector_ref'; @@ -32,9 +32,11 @@ import {ChangeDetectorRef} from './change_detector_ref'; */ @CONST() export class ChangeDetection { - createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector { + getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector { return null; } + + get generateDetectors(): boolean { return null; } } export class DebugContext { @@ -44,7 +46,7 @@ export class DebugContext { export interface ChangeDispatcher { getDebugContext(elementIndex: number, directiveIndex: DirectiveIndex): DebugContext; - notifyOnBinding(bindingRecord: BindingRecord, value: any): void; + notifyOnBinding(bindingTarget: BindingTarget, value: any): void; notifyOnAllChangesDone(): void; } @@ -72,5 +74,5 @@ export interface ProtoChangeDetector { instantiate(dispatcher: ChangeDispatcher) export class ChangeDetectorDefinition { constructor(public id: string, public strategy: string, public variableNames: List, public bindingRecords: BindingRecord[], public eventRecords: BindingRecord[], - public directiveRecords: DirectiveRecord[], public generateCheckNoChanges: boolean) {} + public directiveRecords: DirectiveRecord[], public devMode: boolean) {} } diff --git a/modules/angular2/src/change_detection/jit_proto_change_detector.ts b/modules/angular2/src/change_detection/jit_proto_change_detector.ts index df009b5cc4..fe063f4da4 100644 --- a/modules/angular2/src/change_detection/jit_proto_change_detector.ts +++ b/modules/angular2/src/change_detection/jit_proto_change_detector.ts @@ -1,4 +1,5 @@ import {ListWrapper} from 'angular2/src/facade/collection'; +import {isPresent} from 'angular2/src/facade/lang'; import {ProtoChangeDetector, ChangeDetector, ChangeDetectorDefinition} from './interfaces'; import {ChangeDetectorJITGenerator} from './change_detection_jit_generator'; @@ -20,9 +21,11 @@ export class JitProtoChangeDetector implements ProtoChangeDetector { _createFactory(definition: ChangeDetectorDefinition) { var propertyBindingRecords = createPropertyRecords(definition); var eventBindingRecords = createEventRecords(definition); + var propertyBindingTargets = this.definition.bindingRecords.map(b => b.target); + return new ChangeDetectorJITGenerator( - definition.id, definition.strategy, propertyBindingRecords, eventBindingRecords, - this.definition.directiveRecords, this.definition.generateCheckNoChanges) + definition.id, definition.strategy, propertyBindingRecords, propertyBindingTargets, + eventBindingRecords, this.definition.directiveRecords, this.definition.devMode) .generate(); } } diff --git a/modules/angular2/src/change_detection/pregen_proto_change_detector.dart b/modules/angular2/src/change_detection/pregen_proto_change_detector.dart index bdb9a7546d..27ba1d00b0 100644 --- a/modules/angular2/src/change_detection/pregen_proto_change_detector.dart +++ b/modules/angular2/src/change_detection/pregen_proto_change_detector.dart @@ -1,10 +1,6 @@ library angular2.src.change_detection.pregen_proto_change_detector; -import 'package:angular2/src/change_detection/coalesce.dart'; -import 'package:angular2/src/change_detection/directive_record.dart'; import 'package:angular2/src/change_detection/interfaces.dart'; -import 'package:angular2/src/change_detection/proto_change_detector.dart'; -import 'package:angular2/src/change_detection/proto_record.dart'; import 'package:angular2/src/facade/lang.dart' show looseIdentical; export 'dart:core' show List; @@ -26,8 +22,7 @@ export 'package:angular2/src/facade/lang.dart' show looseIdentical; typedef ProtoChangeDetector PregenProtoChangeDetectorFactory( ChangeDetectorDefinition definition); -typedef ChangeDetector InstantiateMethod(dynamic dispatcher, - List protoRecords, List directiveRecords); +typedef ChangeDetector InstantiateMethod(dynamic dispatcher); /// Implementation of [ProtoChangeDetector] for use by pre-generated change /// detectors in Angular 2 Dart. @@ -41,31 +36,18 @@ class PregenProtoChangeDetector extends ProtoChangeDetector { /// Closure used to generate an actual [ChangeDetector]. final InstantiateMethod _instantiateMethod; - // [ChangeDetector] dependencies. - final List _protoRecords; - final List _directiveRecords; - /// Internal ctor. - PregenProtoChangeDetector._(this.id, this._instantiateMethod, - this._protoRecords, this._directiveRecords); + PregenProtoChangeDetector._(this.id, this._instantiateMethod); static bool isSupported() => true; factory PregenProtoChangeDetector( InstantiateMethod instantiateMethod, ChangeDetectorDefinition def) { - // TODO(kegluneq): Pre-generate these (#2067). - var recordBuilder = new ProtoRecordBuilder(); - def.bindingRecords.forEach((b) { - recordBuilder.add(b, def.variableNames); - }); - var protoRecords = coalesce(recordBuilder.records); - return new PregenProtoChangeDetector._( - def.id, instantiateMethod, protoRecords, def.directiveRecords); + return new PregenProtoChangeDetector._(def.id, instantiateMethod); } @override - instantiate(dynamic dispatcher) => - _instantiateMethod(dispatcher, _protoRecords, _directiveRecords); + instantiate(dynamic dispatcher) => _instantiateMethod(dispatcher); } /// Provided as an optimization to cut down on '!' characters in generated diff --git a/modules/angular2/src/change_detection/proto_change_detector.ts b/modules/angular2/src/change_detection/proto_change_detector.ts index 28e8c246c3..f018c728a6 100644 --- a/modules/angular2/src/change_detection/proto_change_detector.ts +++ b/modules/angular2/src/change_detection/proto_change_detector.ts @@ -29,7 +29,7 @@ import { import {ChangeDetector, ProtoChangeDetector, ChangeDetectorDefinition} from './interfaces'; import {ChangeDetectionUtil} from './change_detection_util'; import {DynamicChangeDetector} from './dynamic_change_detector'; -import {BindingRecord} from './binding_record'; +import {BindingRecord, BindingTarget} from './binding_record'; import {DirectiveRecord, DirectiveIndex} from './directive_record'; import {EventBinding} from './event_binding'; @@ -38,24 +38,30 @@ import {ProtoRecord, RecordType} from './proto_record'; export class DynamicProtoChangeDetector implements ProtoChangeDetector { _propertyBindingRecords: ProtoRecord[]; + _propertyBindingTargets: BindingTarget[]; _eventBindingRecords: EventBinding[]; + _directiveIndices: DirectiveIndex[]; constructor(private definition: ChangeDetectorDefinition) { this._propertyBindingRecords = createPropertyRecords(definition); this._eventBindingRecords = createEventRecords(definition); + this._propertyBindingTargets = this.definition.bindingRecords.map(b => b.target); + this._directiveIndices = this.definition.directiveRecords.map(d => d.directiveIndex); } instantiate(dispatcher: any): ChangeDetector { - return new DynamicChangeDetector(this.definition.id, this.definition.strategy, dispatcher, - this._propertyBindingRecords, this._eventBindingRecords, - this.definition.directiveRecords); + return new DynamicChangeDetector( + this.definition.id, dispatcher, this._propertyBindingRecords.length, + this._propertyBindingTargets, this._directiveIndices, + ChangeDetectionUtil.changeDetectionMode(this.definition.strategy), + this._propertyBindingRecords, this._eventBindingRecords, this.definition.directiveRecords); } } export function createPropertyRecords(definition: ChangeDetectorDefinition): ProtoRecord[] { var recordBuilder = new ProtoRecordBuilder(); - ListWrapper.forEach(definition.bindingRecords, - (b) => { recordBuilder.add(b, definition.variableNames); }); + ListWrapper.forEachWithIndex(definition.bindingRecords, + (b, index) => recordBuilder.add(b, definition.variableNames, index)); return coalesce(recordBuilder.records); } @@ -65,7 +71,7 @@ export function createEventRecords(definition: ChangeDetectorDefinition): EventB return definition.eventRecords.map(er => { var records = _ConvertAstIntoProtoRecords.create(er, varNames); var dirIndex = er.implicitReceiver instanceof DirectiveIndex ? er.implicitReceiver : null; - return new EventBinding(er.eventName, er.elementIndex, dirIndex, records); + return new EventBinding(er.target.name, er.target.elementIndex, dirIndex, records); }); } @@ -74,13 +80,13 @@ export class ProtoRecordBuilder { constructor() { this.records = []; } - add(b: BindingRecord, variableNames: List = null) { + add(b: BindingRecord, variableNames: string[], bindingIndex: number) { var oldLast = ListWrapper.last(this.records); if (isPresent(oldLast) && oldLast.bindingRecord.directiveRecord == b.directiveRecord) { oldLast.lastInDirective = false; } var numberOfRecordsBefore = this.records.length; - this._appendRecords(b, variableNames); + this._appendRecords(b, variableNames, bindingIndex); var newLast = ListWrapper.last(this.records); if (isPresent(newLast) && newLast !== oldLast) { newLast.lastInBinding = true; @@ -99,29 +105,30 @@ export class ProtoRecordBuilder { } } - _appendRecords(b: BindingRecord, variableNames: List) { + _appendRecords(b: BindingRecord, variableNames: string[], bindingIndex: number) { if (b.isDirectiveLifecycle()) { this.records.push(new ProtoRecord(RecordType.DIRECTIVE_LIFECYCLE, b.lifecycleEvent, null, [], - [], -1, null, this.records.length + 1, b, null, false, - false, false, false)); + [], -1, null, this.records.length + 1, b, false, false, + false, false, null)); } else { - _ConvertAstIntoProtoRecords.append(this.records, b, variableNames); + _ConvertAstIntoProtoRecords.append(this.records, b, variableNames, bindingIndex); } } } class _ConvertAstIntoProtoRecords implements AstVisitor { constructor(private _records: List, private _bindingRecord: BindingRecord, - private _expressionAsString: string, private _variableNames: List) {} + private _variableNames: string[], private _bindingIndex: number) {} - static append(records: List, b: BindingRecord, variableNames: List) { - var c = new _ConvertAstIntoProtoRecords(records, b, b.ast.toString(), variableNames); + static append(records: List, b: BindingRecord, variableNames: string[], + bindingIndex: number) { + var c = new _ConvertAstIntoProtoRecords(records, b, variableNames, bindingIndex); b.ast.visit(c); } static create(b: BindingRecord, variableNames: List): ProtoRecord[] { var rec = []; - _ConvertAstIntoProtoRecords.append(rec, b, variableNames); + _ConvertAstIntoProtoRecords.append(rec, b, variableNames, null); rec[rec.length - 1].lastInBinding = true; return rec; } @@ -261,12 +268,12 @@ class _ConvertAstIntoProtoRecords implements AstVisitor { var selfIndex = this._records.length + 1; if (context instanceof DirectiveIndex) { this._records.push(new ProtoRecord(type, name, funcOrValue, args, fixedArgs, -1, context, - selfIndex, this._bindingRecord, this._expressionAsString, - false, false, false, false)); + selfIndex, this._bindingRecord, false, false, false, false, + this._bindingIndex)); } else { this._records.push(new ProtoRecord(type, name, funcOrValue, args, fixedArgs, context, null, - selfIndex, this._bindingRecord, this._expressionAsString, - false, false, false, false)); + selfIndex, this._bindingRecord, false, false, false, false, + this._bindingIndex)); } return selfIndex; } diff --git a/modules/angular2/src/change_detection/proto_record.ts b/modules/angular2/src/change_detection/proto_record.ts index 4f53cb3852..701d709fdd 100644 --- a/modules/angular2/src/change_detection/proto_record.ts +++ b/modules/angular2/src/change_detection/proto_record.ts @@ -26,9 +26,9 @@ export class ProtoRecord { constructor(public mode: RecordType, public name: string, public funcOrValue, public args: List, public fixedArgs: List, public contextIndex: number, public directiveIndex: DirectiveIndex, public selfIndex: number, - public bindingRecord: BindingRecord, public expressionAsString: string, - public lastInBinding: boolean, public lastInDirective: boolean, - public argumentToPureFunction: boolean, public referencedBySelf: boolean) {} + public bindingRecord: BindingRecord, public lastInBinding: boolean, + public lastInDirective: boolean, public argumentToPureFunction: boolean, + public referencedBySelf: boolean, public propertyBindingIndex: number) {} isPureFunction(): boolean { return this.mode === RecordType.INTERPOLATE || this.mode === RecordType.COLLECTION_LITERAL; diff --git a/modules/angular2/src/core/compiler/proto_view_factory.ts b/modules/angular2/src/core/compiler/proto_view_factory.ts index fef99ed9ee..716e79e20d 100644 --- a/modules/angular2/src/core/compiler/proto_view_factory.ts +++ b/modules/angular2/src/core/compiler/proto_view_factory.ts @@ -211,12 +211,10 @@ export class ProtoViewFactory { var nestedPvVariableBindings = _collectNestedProtoViewsVariableBindings(nestedPvsWithIndex); var nestedPvVariableNames = _collectNestedProtoViewsVariableNames(nestedPvsWithIndex); - var changeDetectorDefs = - _getChangeDetectorDefinitions(hostComponentBinding.metadata, nestedPvsWithIndex, + var protoChangeDetectors = + this._getProtoChangeDetectors(hostComponentBinding, nestedPvsWithIndex, nestedPvVariableNames, allRenderDirectiveMetadata); - var protoChangeDetectors = ListWrapper.map( - changeDetectorDefs, - changeDetectorDef => this._changeDetection.createProtoChangeDetector(changeDetectorDef)); + var appProtoViews = ListWrapper.createFixedSize(nestedPvsWithIndex.length); ListWrapper.forEach(nestedPvsWithIndex, (pvWithIndex: RenderProtoViewWithIndex) => { var appProtoView = @@ -230,6 +228,24 @@ export class ProtoViewFactory { }); return appProtoViews; } + + private _getProtoChangeDetectors(hostComponentBinding: DirectiveBinding, + nestedPvsWithIndex: RenderProtoViewWithIndex[], + nestedPvVariableNames: string[][], + allRenderDirectiveMetadata: any[]): ProtoChangeDetector[] { + if (this._changeDetection.generateDetectors) { + var changeDetectorDefs = + _getChangeDetectorDefinitions(hostComponentBinding.metadata, nestedPvsWithIndex, + nestedPvVariableNames, allRenderDirectiveMetadata); + return changeDetectorDefs.map(changeDetectorDef => + this._changeDetection.getProtoChangeDetector( + changeDetectorDef.id, changeDetectorDef)); + } else { + var changeDetectorIds = + _getChangeDetectorDefinitionIds(hostComponentBinding.metadata, nestedPvsWithIndex); + return changeDetectorIds.map(id => this._changeDetection.getProtoChangeDetector(id, null)); + } + } } /** @@ -280,22 +296,35 @@ function _getChangeDetectorDefinitions( var directiveRecords = bindingRecordsCreator.getDirectiveRecords(elementBinders, allRenderDirectiveMetadata); var strategyName = DEFAULT; - var typeString; if (pvWithIndex.renderProtoView.type === ViewType.COMPONENT) { strategyName = hostComponentMetadata.changeDetection; - typeString = 'comp'; - } else if (pvWithIndex.renderProtoView.type === ViewType.HOST) { - typeString = 'host'; - } else { - typeString = 'embedded'; } - var id = `${hostComponentMetadata.id}_${typeString}_${pvWithIndex.index}`; + var id = _protoViewId(hostComponentMetadata, pvWithIndex); var variableNames = nestedPvVariableNames[pvWithIndex.index]; return new ChangeDetectorDefinition(id, strategyName, variableNames, propBindingRecords, eventBindingRecords, directiveRecords, assertionsEnabled()); }); } +function _getChangeDetectorDefinitionIds(hostComponentMetadata: RenderDirectiveMetadata, + nestedPvsWithIndex: List): + string[] { + return nestedPvsWithIndex.map(pvWithIndex => _protoViewId(hostComponentMetadata, pvWithIndex)); +} + +function _protoViewId(hostComponentMetadata: RenderDirectiveMetadata, + pvWithIndex: RenderProtoViewWithIndex): string { + var typeString; + if (pvWithIndex.renderProtoView.type === ViewType.COMPONENT) { + typeString = 'comp'; + } else if (pvWithIndex.renderProtoView.type === ViewType.HOST) { + typeString = 'host'; + } else { + typeString = 'embedded'; + } + return `${hostComponentMetadata.id}_${typeString}_${pvWithIndex.index}`; +} + function _createAppProtoView( renderProtoView: ProtoViewDto, protoChangeDetector: ProtoChangeDetector, variableBindings: Map, allDirectives: List, diff --git a/modules/angular2/src/core/compiler/view.ts b/modules/angular2/src/core/compiler/view.ts index 5c2a7c04cf..1efcb35a2a 100644 --- a/modules/angular2/src/core/compiler/view.ts +++ b/modules/angular2/src/core/compiler/view.ts @@ -8,12 +8,12 @@ import { } from 'angular2/src/facade/collection'; import { AST, - BindingRecord, ChangeDetector, ChangeDetectorRef, ChangeDispatcher, DirectiveIndex, DirectiveRecord, + BindingTarget, Locals, ProtoChangeDetector } from 'angular2/src/change_detection/change_detection'; @@ -171,7 +171,7 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher { } // dispatch to element injector or text nodes based on context - notifyOnBinding(b: BindingRecord, currentValue: any): void { + notifyOnBinding(b: BindingTarget, currentValue: any): void { if (b.isTextNode()) { this.renderer.setText( this.render, this.mainMergeMapping.renderTextIndices[b.elementIndex + this.textOffset], @@ -179,14 +179,14 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher { } else { var elementRef = this.elementRefs[this.elementOffset + b.elementIndex]; if (b.isElementProperty()) { - this.renderer.setElementProperty(elementRef, b.propertyName, currentValue); + this.renderer.setElementProperty(elementRef, b.name, currentValue); } else if (b.isElementAttribute()) { - this.renderer.setElementAttribute(elementRef, b.propertyName, currentValue); + this.renderer.setElementAttribute(elementRef, b.name, currentValue); } else if (b.isElementClass()) { - this.renderer.setElementClass(elementRef, b.propertyName, currentValue); + this.renderer.setElementClass(elementRef, b.name, currentValue); } else if (b.isElementStyle()) { - var unit = isPresent(b.propertyUnit) ? b.propertyUnit : ''; - this.renderer.setElementStyle(elementRef, b.propertyName, `${currentValue}${unit}`); + var unit = isPresent(b.unit) ? b.unit : ''; + this.renderer.setElementStyle(elementRef, b.name, `${currentValue}${unit}`); } else { throw new BaseException('Unsupported directive record'); } diff --git a/modules/angular2/src/facade/collection.dart b/modules/angular2/src/facade/collection.dart index c2766d509b..7c4e7d8e87 100644 --- a/modules/angular2/src/facade/collection.dart +++ b/modules/angular2/src/facade/collection.dart @@ -134,6 +134,12 @@ class ListWrapper { list.forEach(fn); } + static void forEachWithIndex(List list, fn(item, index)) { + for (var i = 0; i < list.length; ++i) { + fn(list[i], i); + } + } + static reduce(List list, fn(a, b), init) { return list.fold(init, fn); } diff --git a/modules/angular2/src/facade/collection.ts b/modules/angular2/src/facade/collection.ts index b09e4f9ecd..6602a8cc87 100644 --- a/modules/angular2/src/facade/collection.ts +++ b/modules/angular2/src/facade/collection.ts @@ -182,6 +182,11 @@ export class ListWrapper { fn(array[i]); } } + static forEachWithIndex(array: List, fn: (T, number) => void) { + for (var i = 0; i < array.length; i++) { + fn(array[i], i); + } + } static first(array: List): T { if (!array) return null; return array[0]; 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 81a6e19e1b..a7f20a9080 100644 --- a/modules/angular2/src/transform/template_compiler/change_detector_codegen.dart +++ b/modules/angular2/src/transform/template_compiler/change_detector_codegen.dart @@ -9,6 +9,7 @@ import 'package:angular2/src/change_detection/interfaces.dart'; import 'package:angular2/src/change_detection/proto_change_detector.dart'; import 'package:angular2/src/change_detection/proto_record.dart'; import 'package:angular2/src/change_detection/event_binding.dart'; +import 'package:angular2/src/change_detection/binding_record.dart'; import 'package:angular2/src/facade/lang.dart' show BaseException; /// Responsible for generating change detector classes for Angular 2. @@ -79,7 +80,8 @@ class _CodegenState { final List _eventBindings; final CodegenLogicUtil _logic; final CodegenNameUtil _names; - final bool _generateCheckNoChanges; + final bool _devMode; + final List _propertyBindingTargets; _CodegenState._( this._changeDetectorDefId, @@ -87,11 +89,12 @@ class _CodegenState { this._changeDetectorTypeName, String changeDetectionStrategy, this._records, + this._propertyBindingTargets, this._eventBindings, this._directiveRecords, this._logic, this._names, - this._generateCheckNoChanges) + this._devMode) : _changeDetectionMode = ChangeDetectionUtil.changeDetectionMode(changeDetectionStrategy); @@ -99,6 +102,8 @@ class _CodegenState { ChangeDetectorDefinition def) { var protoRecords = createPropertyRecords(def); var eventBindings = createEventRecords(def); + var propertyBindingTargets = def.bindingRecords.map((b) => b.target).toList(); + var names = new CodegenNameUtil(protoRecords, eventBindings, def.directiveRecords, _UTIL); var logic = new CodegenLogicUtil(names, _UTIL, def.strategy); return new _CodegenState._( @@ -107,11 +112,12 @@ class _CodegenState { changeDetectorTypeName, def.strategy, protoRecords, + propertyBindingTargets, eventBindings, def.directiveRecords, logic, names, - def.generateCheckNoChanges); + def.devMode); } void _writeToBuf(StringBuffer buf) { @@ -119,9 +125,12 @@ class _CodegenState { class $_changeDetectorTypeName extends $_BASE_CLASS<$_contextTypeName> { ${_genDeclareFields()} - $_changeDetectorTypeName(dispatcher, protos, directiveRecords) + $_changeDetectorTypeName(dispatcher) : super(${codify(_changeDetectorDefId)}, - dispatcher, protos, directiveRecords, '$_changeDetectionMode') { + dispatcher, ${_records.length}, + ${_changeDetectorTypeName}.gen_propertyBindingTargets, + ${_changeDetectorTypeName}.gen_directiveIndices, + '$_changeDetectionMode') { dehydrateDirectives(false); } @@ -145,17 +154,31 @@ class _CodegenState { ${_maybeGenDehydrateDirectives()} + ${_genPropertyBindingTargets()}; + + ${_genDirectiveIndices()}; + static $_GEN_PREFIX.ProtoChangeDetector $PROTO_CHANGE_DETECTOR_FACTORY_METHOD( $_GEN_PREFIX.ChangeDetectorDefinition def) { return new $_GEN_PREFIX.PregenProtoChangeDetector( - (a, b, c) => new $_changeDetectorTypeName(a, b, c), + (a) => new $_changeDetectorTypeName(a), def); } } '''); } + String _genPropertyBindingTargets() { + var targets = _logic.genPropertyBindingTargets(_propertyBindingTargets, this._devMode); + return "static var gen_propertyBindingTargets = ${targets}"; + } + + String _genDirectiveIndices() { + var indices = _logic.genDirectiveIndices(_directiveRecords); + return "static var gen_directiveIndices = ${indices}"; + } + String _maybeGenHandleEventInternal() { if (_eventBindings.length > 0) { var handlers = _eventBindings.map((eb) => _genEventBinding(eb)).join("\n"); @@ -239,7 +262,7 @@ class _CodegenState { var directiveFieldNames = _names.getAllDirectiveNames(); for (var i = 0; i < directiveFieldNames.length; ++i) { buf.writeln('${directiveFieldNames[i]} = directives.getDirectiveFor(' - '${_names.getDirectivesAccessorName()}[$i].directiveIndex);'); + '${_names.getDirectivesAccessorName()}[$i]);'); } return '$buf'; } @@ -378,9 +401,9 @@ class _CodegenState { var oldValue = _names.getFieldName(r.selfIndex); var br = r.bindingRecord; - if (br.isDirective()) { + if (br.target.isDirective()) { var directiveProperty = - '${_names.getDirectiveName(br.directiveRecord.directiveIndex)}.${br.propertyName}'; + '${_names.getDirectiveName(br.directiveRecord.directiveIndex)}.${br.target.name}'; return ''' ${_genThrowOnChangeCheck(oldValue, newValue)} $directiveProperty = $newValue; @@ -395,7 +418,7 @@ class _CodegenState { } String _genThrowOnChangeCheck(String oldValue, String newValue) { - if (this._generateCheckNoChanges) { + if (this._devMode) { return ''' if(throwOnChange) { this.throwOnChangeError(${oldValue}, ${newValue}); @@ -407,7 +430,7 @@ class _CodegenState { } String _genCheckNoChanges() { - if (this._generateCheckNoChanges) { + if (this._devMode) { return 'void checkNoChanges() { runDetectChanges(true); }'; } else { return ''; @@ -417,8 +440,8 @@ class _CodegenState { String _maybeFirstInBinding(ProtoRecord r) { var prev = ChangeDetectionUtil.protoByIndex(_records, r.selfIndex - 1); var firstInBindng = prev == null || prev.bindingRecord != r.bindingRecord; - return firstInBindng - ? "${_names.getFirstProtoInCurrentBinding()} = ${r.selfIndex};" + return firstInBindng && !r.bindingRecord.isDirectiveLifecycle() + ? "${_names.getPropertyBindingIndex()} = ${r.propertyBindingIndex};" : ''; } diff --git a/modules/angular2/src/transform/template_compiler/reflection/reflection_capabilities.dart b/modules/angular2/src/transform/template_compiler/reflection/reflection_capabilities.dart index 65734853ca..50fcc118ba 100644 --- a/modules/angular2/src/transform/template_compiler/reflection/reflection_capabilities.dart +++ b/modules/angular2/src/transform/template_compiler/reflection/reflection_capabilities.dart @@ -14,7 +14,7 @@ class NullReflectionCapabilities implements ReflectionCapabilities { return false; } - Function factory(Type type) => _notImplemented('factory'); + Function factory(Type type) => _notImplemented("factory"); List parameters(typeOrFunc) => _notImplemented('parameters'); diff --git a/modules/angular2/test/change_detection/change_detection_spec.ts b/modules/angular2/test/change_detection/change_detection_spec.ts index d08803aae6..24eb8dfdbd 100644 --- a/modules/angular2/test/change_detection/change_detection_spec.ts +++ b/modules/angular2/test/change_detection/change_detection_spec.ts @@ -30,12 +30,12 @@ export function main() { var map = {'id': (def) => proto}; var cd = new PreGeneratedChangeDetection(map); - expect(cd.createProtoChangeDetector(def)).toBe(proto) + expect(cd.getProtoChangeDetector('id', def)).toBe(proto) }); it("should delegate to dynamic change detection otherwise", () => { var cd = new PreGeneratedChangeDetection({}); - expect(cd.createProtoChangeDetector(def)).toBeAnInstanceOf(DynamicProtoChangeDetector); + expect(cd.getProtoChangeDetector('id', def)).toBeAnInstanceOf(DynamicProtoChangeDetector); }); }); } diff --git a/modules/angular2/test/change_detection/change_detector_spec.ts b/modules/angular2/test/change_detection/change_detector_spec.ts index e814617cd9..1c8893f89e 100644 --- a/modules/angular2/test/change_detection/change_detector_spec.ts +++ b/modules/angular2/test/change_detection/change_detector_spec.ts @@ -1135,8 +1135,8 @@ class TestDispatcher implements ChangeDispatcher { this.onAllChangesDoneCalled = true; } - notifyOnBinding(binding, value) { - this.log.push(`${binding.propertyName}=${this._asString(value)}`); + notifyOnBinding(target, value) { + this.log.push(`${target.name}=${this._asString(value)}`); this.loggedValues.push(value); } diff --git a/modules/angular2/test/change_detection/coalesce_spec.ts b/modules/angular2/test/change_detection/coalesce_spec.ts index e147a66f67..ccb8159083 100644 --- a/modules/angular2/test/change_detection/coalesce_spec.ts +++ b/modules/angular2/test/change_detection/coalesce_spec.ts @@ -21,8 +21,7 @@ export function main() { if (isBlank(argumentToPureFunction)) argumentToPureFunction = false; return new ProtoRecord(mode, name, funcOrValue, args, null, contextIndex, directiveIndex, - selfIndex, null, null, lastInBinding, false, argumentToPureFunction, - false); + selfIndex, null, lastInBinding, false, argumentToPureFunction, false, 0); } describe("change detection - coalesce", () => { @@ -64,7 +63,7 @@ export function main() { [r("user", [], 0, 1, {lastInBinding: true}), r("user", [], 0, 2, {lastInBinding: true})]); expect(rs[1]).toEqual(new ProtoRecord(RecordType.SELF, "self", null, [], null, 1, null, 2, - null, null, true, false, false, false)); + null, true, false, false, false, 0)); }); it("should set referencedBySelf", () => { diff --git a/modules/angular2/test/change_detection/proto_record_builder_spec.ts b/modules/angular2/test/change_detection/proto_record_builder_spec.ts index 06c27eadea..a6c53fe93e 100644 --- a/modules/angular2/test/change_detection/proto_record_builder_spec.ts +++ b/modules/angular2/test/change_detection/proto_record_builder_spec.ts @@ -19,7 +19,7 @@ export function main() { it('should set argumentToPureFunction flag', inject([Parser], (p: Parser) => { var builder = new ProtoRecordBuilder(); var ast = p.parseBinding("[1,2]", "location"); // collection literal is a pure function - builder.add(BindingRecord.createForElementProperty(ast, 0, "property"), []); + builder.add(BindingRecord.createForElementProperty(ast, 0, "property"), [], 0); var isPureFunc = builder.records.map(r => r.argumentToPureFunction); expect(isPureFunc).toEqual([true, true, false]); @@ -29,7 +29,7 @@ export function main() { inject([Parser], (p: Parser) => { var builder = new ProtoRecordBuilder(); var ast = p.parseBinding("f(1,2)", "location"); - builder.add(BindingRecord.createForElementProperty(ast, 0, "property"), []); + builder.add(BindingRecord.createForElementProperty(ast, 0, "property"), [], 0); var isPureFunc = builder.records.map(r => r.argumentToPureFunction); expect(isPureFunc).toEqual([false, false, false]); diff --git a/modules/angular2/test/change_detection/proto_record_spec.ts b/modules/angular2/test/change_detection/proto_record_spec.ts index c7071c09fc..fb62af7ff7 100644 --- a/modules/angular2/test/change_detection/proto_record_spec.ts +++ b/modules/angular2/test/change_detection/proto_record_spec.ts @@ -20,8 +20,8 @@ export function main() { if (isBlank(argumentToPureFunction)) argumentToPureFunction = false; if (isBlank(referencedBySelf)) referencedBySelf = false; - return new ProtoRecord(mode, name, null, [], null, 0, directiveIndex, 0, null, null, - lastInBinding, false, argumentToPureFunction, referencedBySelf); + return new ProtoRecord(mode, name, null, [], null, 0, directiveIndex, 0, null, lastInBinding, + false, argumentToPureFunction, referencedBySelf, 0); } describe("ProtoRecord", () => { diff --git a/modules/angular2/test/core/compiler/element_injector_spec.ts b/modules/angular2/test/core/compiler/element_injector_spec.ts index 558e65baeb..09d051ec5b 100644 --- a/modules/angular2/test/core/compiler/element_injector_spec.ts +++ b/modules/angular2/test/core/compiler/element_injector_spec.ts @@ -980,7 +980,7 @@ export function main() { }); it("should inject ChangeDetectorRef of the component's view into the component", () => { - var cd = new DynamicChangeDetector(null, null, null, [], [], []); + var cd = new DynamicChangeDetector(null, null, 0, [], [], null, [], [], []); var view = new DummyView(); var childView = new DummyView(); childView.changeDetector = cd; @@ -993,7 +993,7 @@ export function main() { }); it("should inject ChangeDetectorRef of the containing component into directives", () => { - var cd = new DynamicChangeDetector(null, null, null, [], [], []); + var cd = new DynamicChangeDetector(null, null, 0, [], [], null, [], [], []); var view = new DummyView(); view.changeDetector =cd; var binding = DirectiveBinding.createFromType(DirectiveNeedsChangeDetectorRef, new DirectiveMetadata()); diff --git a/modules/angular2/test/core/compiler/proto_view_factory_spec.ts b/modules/angular2/test/core/compiler/proto_view_factory_spec.ts index b302dfb8b0..a69e57cd33 100644 --- a/modules/angular2/test/core/compiler/proto_view_factory_spec.ts +++ b/modules/angular2/test/core/compiler/proto_view_factory_spec.ts @@ -20,7 +20,8 @@ import { ChangeDetection, ChangeDetectorDefinition, BindingRecord, - DirectiveIndex + DirectiveIndex, + Parser } from 'angular2/src/change_detection/change_detection'; import { BindingRecordsCreator, @@ -177,39 +178,44 @@ export function main() { beforeEach(() => { creator = new BindingRecordsCreator(); }); describe('getEventBindingRecords', () => { - it("should return template event records", () => { - var rec = creator.getEventBindingRecords( - [ - new RenderElementBinder( - {eventBindings: [new EventBinding("a", null)], directives: []}), - new RenderElementBinder( - {eventBindings: [new EventBinding("b", null)], directives: []}) - ], - []); + it("should return template event records", inject([Parser], (p: Parser) => { + var ast1 = p.parseAction("1", null); + var ast2 = p.parseAction("2", null); - expect(rec).toEqual([ - BindingRecord.createForEvent(null, "a", 0), - BindingRecord.createForEvent(null, "b", 1) - ]); - }); + var rec = creator.getEventBindingRecords( + [ + new RenderElementBinder( + {eventBindings: [new EventBinding("a", ast1)], directives: []}), + new RenderElementBinder( + {eventBindings: [new EventBinding("b", ast2)], directives: []}) + ], + []); - it('should return host event records', () => { - var rec = creator.getEventBindingRecords( - [ - new RenderElementBinder({ - eventBindings: [], - directives: [ - new DirectiveBinder( - {directiveIndex: 0, eventBindings: [new EventBinding("a", null)]}) - ] - }) - ], - [RenderDirectiveMetadata.create({id: 'some-id'})]); + expect(rec).toEqual([ + BindingRecord.createForEvent(ast1, "a", 0), + BindingRecord.createForEvent(ast2, "b", 1) + ]); + })); - expect(rec.length).toEqual(1); - expect(rec[0].eventName).toEqual("a"); - expect(rec[0].implicitReceiver).toBeAnInstanceOf(DirectiveIndex); - }); + it('should return host event records', inject([Parser], (p: Parser) => { + var ast1 = p.parseAction("1", null); + + var rec = creator.getEventBindingRecords( + [ + new RenderElementBinder({ + eventBindings: [], + directives: [ + new DirectiveBinder( + {directiveIndex: 0, eventBindings: [new EventBinding("a", ast1)]}) + ] + }) + ], + [RenderDirectiveMetadata.create({id: 'some-id'})]); + + expect(rec.length).toEqual(1); + expect(rec[0].target.name).toEqual("a"); + expect(rec[0].implicitReceiver).toBeAnInstanceOf(DirectiveIndex); + })); }); }); }); @@ -253,6 +259,7 @@ function createRenderViewportElementBinder(nestedProtoView) { class ChangeDetectionSpy extends SpyObject { constructor() { super(ChangeDetection); } noSuchMethod(m) { return super.noSuchMethod(m) } + get generateDetectors() { return true; } } @Component({selector: 'main-comp'}) diff --git a/modules/angular2/test/facade/collection_spec.ts b/modules/angular2/test/facade/collection_spec.ts index 161a1259cf..f8542b7a53 100644 --- a/modules/angular2/test/facade/collection_spec.ts +++ b/modules/angular2/test/facade/collection_spec.ts @@ -90,6 +90,18 @@ export function main() { it('should return null for an empty list', () => { expect(ListWrapper.maximum([], x => x)).toEqual(null); }); }); + + describe('forEachWithIndex', () => { + var l; + + beforeEach(() => { l = ["a", "b"]; }); + + it('should iterate over an array passing values and indices', () => { + var record = []; + ListWrapper.forEachWithIndex(l, (value, index) => record.push([value, index])); + expect(record).toEqual([["a", 0], ["b", 1]]); + }); + }); }); describe('StringMapWrapper', () => { 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 09dff325f1..68a9b41211 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 @@ -13,12 +13,10 @@ void initReflector() { if (_visited) return; _visited = true; _ngRef.reflector - ..registerType( - MyComponent, - new _ngRef.ReflectionInfo(const [ - const Component(selector: '[soup]'), - const View(template: 'Salad: {{myNum}} is awesome') - ], const [], () => new MyComponent())) + ..registerType(MyComponent, new _ngRef.ReflectionInfo(const [ + const Component(selector: '[soup]'), + const View(template: 'Salad: {{myNum}} is awesome') + ], const [], () => new MyComponent())) ..registerGetters({'myNum': (o) => o.myNum}); _gen.preGeneratedProtoDetectors['MyComponent_comp_0'] = _MyComponent_ChangeDetector0.newProtoChangeDetector; @@ -28,19 +26,23 @@ class _MyComponent_ChangeDetector0 extends _gen.AbstractChangeDetector { var myNum0, interpolate1; - _MyComponent_ChangeDetector0(dispatcher, protos, directiveRecords) - : super("MyComponent_comp_0", dispatcher, protos, directiveRecords, - 'ALWAYS_CHECK') { + _MyComponent_ChangeDetector0(dispatcher) : super( + "MyComponent_comp_0", dispatcher, 2, + _MyComponent_ChangeDetector0.gen_propertyBindingTargets, + _MyComponent_ChangeDetector0.gen_directiveIndices, 'ALWAYS_CHECK') { dehydrateDirectives(false); } void detectChangesInRecordsInternal(throwOnChange) { - var l_context = this.context, l_myNum0, c_myNum0, l_interpolate1; + var l_context = this.context, + l_myNum0, + c_myNum0, + l_interpolate1; c_myNum0 = false; var isChanged = false; var changes = null; - this.firstProtoInCurrentBinding = 1; + this.propertyBindingIndex = 0; l_myNum0 = l_context.myNum; if (_gen.looseNotIdentical(l_myNum0, this.myNum0)) { c_myNum0 = true; @@ -75,9 +77,16 @@ class _MyComponent_ChangeDetector0 this.myNum0 = this.interpolate1 = _gen.ChangeDetectionUtil.uninitialized; } + static var gen_propertyBindingTargets = [ + _gen.ChangeDetectionUtil.bindingTarget("textNode", 0, null, null, + "Salad: {{myNum}} is awesome in MyComponent: