feat(change_detection): do not reparse AST when using generated detectors

This commit is contained in:
vsavkin 2015-08-19 11:26:45 -07:00
parent b986c54079
commit d2d0715568
31 changed files with 423 additions and 279 deletions

View File

@ -2,7 +2,7 @@ import {isPresent, isBlank, BaseException, StringWrapper} from 'angular2/src/fac
import {List, ListWrapper} from 'angular2/src/facade/collection'; import {List, ListWrapper} from 'angular2/src/facade/collection';
import {ChangeDetectionUtil} from './change_detection_util'; import {ChangeDetectionUtil} from './change_detection_util';
import {ChangeDetectorRef} from './change_detector_ref'; import {ChangeDetectorRef} from './change_detector_ref';
import {DirectiveRecord} from './directive_record'; import {DirectiveIndex} from './directive_record';
import {ChangeDetector, ChangeDispatcher} from './interfaces'; import {ChangeDetector, ChangeDispatcher} from './interfaces';
import {Pipes} from './pipes'; import {Pipes} from './pipes';
import { import {
@ -10,8 +10,7 @@ import {
ExpressionChangedAfterItHasBeenCheckedException, ExpressionChangedAfterItHasBeenCheckedException,
DehydratedException DehydratedException
} from './exceptions'; } from './exceptions';
import {ProtoRecord} from './proto_record'; import {BindingTarget} from './binding_record';
import {BindingRecord} from './binding_record';
import {Locals} from './parser/locals'; import {Locals} from './parser/locals';
import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from './constants'; import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from './constants';
import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile'; 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)`); var _scope_check: WtfScopeFn = wtfCreateScope(`ChangeDetector#check(ascii id, bool throwOnChange)`);
class _Context { class _Context {
constructor(public element: any, public componentElement: any, public instance: any, constructor(public element: any, public componentElement: any, public context: any,
public context: any, public locals: any, public injector: any, public locals: any, public injector: any, public expression: any) {}
public expression: any) {}
} }
export class AbstractChangeDetector<T> implements ChangeDetector { export class AbstractChangeDetector<T> implements ChangeDetector {
@ -35,24 +33,19 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
// change detection will fail. // change detection will fail.
alreadyChecked: any = false; alreadyChecked: any = false;
context: T; context: T;
directiveRecords: List<DirectiveRecord>;
dispatcher: ChangeDispatcher;
locals: Locals = null; locals: Locals = null;
mode: string = null; mode: string = null;
pipes: Pipes = null; pipes: Pipes = null;
firstProtoInCurrentBinding: number; propertyBindingIndex: number;
protos: List<ProtoRecord>;
// This is an experimental feature. Works only in Dart. // This is an experimental feature. Works only in Dart.
subscriptions: any[]; subscriptions: any[];
streams: any[]; streams: any[];
constructor(public id: string, dispatcher: ChangeDispatcher, protos: List<ProtoRecord>, constructor(public id: string, public dispatcher: ChangeDispatcher,
directiveRecords: List<DirectiveRecord>, public modeOnHydrate: string) { public numberOfPropertyProtoRecords: number, public bindingTargets: BindingTarget[],
public directiveIndices: DirectiveIndex[], public modeOnHydrate: string) {
this.ref = new ChangeDetectorRef(this); this.ref = new ChangeDetectorRef(this);
this.directiveRecords = directiveRecords;
this.dispatcher = dispatcher;
this.protos = protos;
} }
addChild(cd: ChangeDetector): void { addChild(cd: ChangeDetector): void {
@ -99,7 +92,7 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
// implementation of `detectChangesInRecordsInternal` which does the work of detecting changes // implementation of `detectChangesInRecordsInternal` which does the work of detecting changes
// and which this method will call. // and which this method will call.
// This method expects that `detectChangesInRecordsInternal` will set the property // 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. // facilitate error reporting.
detectChangesInRecords(throwOnChange: boolean): void { detectChangesInRecords(throwOnChange: boolean): void {
if (!this.hydrated()) { if (!this.hydrated()) {
@ -115,9 +108,9 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
// Subclasses should override this method to perform any work necessary to detect and report // 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 // changes. For example, changes should be reported via `ChangeDetectionUtil.addChange`, lifecycle
// methods should be called, etc. // methods should be called, etc.
// This implementation should also set `this.firstProtoInCurrentBinding` to the selfIndex of the // This implementation should also set `this.propertyBindingIndex` to the propertyBindingIndex of
// first proto record // the
// to facilitate error reporting. See {@link #detectChangesInRecords}. // first proto record to facilitate error reporting. See {@link #detectChangesInRecords}.
detectChangesInRecordsInternal(throwOnChange: boolean): void {} detectChangesInRecordsInternal(throwOnChange: boolean): void {}
// This method is not intended to be overridden. Subclasses should instead provide an // This method is not intended to be overridden. Subclasses should instead provide an
@ -195,8 +188,8 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
protected observe(value: any, index: number): any { protected observe(value: any, index: number): any {
if (isObservable(value)) { if (isObservable(value)) {
if (isBlank(this.subscriptions)) { if (isBlank(this.subscriptions)) {
this.subscriptions = ListWrapper.createFixedSize(this.protos.length + 1); this.subscriptions = ListWrapper.createFixedSize(this.numberOfPropertyProtoRecords + 1);
this.streams = ListWrapper.createFixedSize(this.protos.length + 1); this.streams = ListWrapper.createFixedSize(this.numberOfPropertyProtoRecords + 1);
} }
if (isBlank(this.subscriptions[index])) { if (isBlank(this.subscriptions[index])) {
this.streams[index] = value.changes; this.streams[index] = value.changes;
@ -211,7 +204,7 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
} }
protected getDetectorFor(directives: any, index: number): 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 { protected notifyDispatcher(value: any): void {
@ -223,31 +216,26 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
if (isBlank(changes)) { if (isBlank(changes)) {
changes = {}; changes = {};
} }
changes[this._currentBinding().propertyName] = changes[this._currentBinding().name] = ChangeDetectionUtil.simpleChange(oldValue, newValue);
ChangeDetectionUtil.simpleChange(oldValue, newValue);
return changes; return changes;
} }
private _throwError(exception: any, stack: any): void { private _throwError(exception: any, stack: any): void {
var proto = this._currentBindingProto(); var c = this.dispatcher.getDebugContext(this._currentBinding().elementIndex, null);
var c = this.dispatcher.getDebugContext(proto.bindingRecord.elementIndex, proto.directiveIndex); var context = isPresent(c) ? new _Context(c.element, c.componentElement, c.context, c.locals,
var context = isPresent(c) ? new _Context(c.element, c.componentElement, c.directive, c.context, c.injector, this._currentBinding().debug) :
c.locals, c.injector, proto.expressionAsString) :
null; null;
throw new ChangeDetectionError(proto, exception, stack, context); throw new ChangeDetectionError(this._currentBinding().debug, exception, stack, context);
} }
protected throwOnChangeError(oldValue: any, newValue: any): void { protected throwOnChangeError(oldValue: any, newValue: any): void {
var change = ChangeDetectionUtil.simpleChange(oldValue, newValue); throw new ExpressionChangedAfterItHasBeenCheckedException(this._currentBinding().debug,
throw new ExpressionChangedAfterItHasBeenCheckedException(this._currentBindingProto(), change, oldValue, newValue, null);
null);
} }
protected throwDehydratedError(): void { throw new DehydratedException(); } protected throwDehydratedError(): void { throw new DehydratedException(); }
private _currentBinding(): BindingRecord { return this._currentBindingProto().bindingRecord; } private _currentBinding(): BindingTarget {
return this.bindingTargets[this.propertyBindingIndex];
private _currentBindingProto(): ProtoRecord {
return ChangeDetectionUtil.protoByIndex(this.protos, this.firstProtoInCurrentBinding);
} }
} }

View File

@ -3,8 +3,10 @@ import {SetterFn} from 'angular2/src/reflection/types';
import {AST} from './parser/ast'; import {AST} from './parser/ast';
import {DirectiveIndex, DirectiveRecord} from './directive_record'; import {DirectiveIndex, DirectiveRecord} from './directive_record';
const DIRECTIVE = "directive";
const DIRECTIVE_LIFECYCLE = "directiveLifecycle"; const DIRECTIVE_LIFECYCLE = "directiveLifecycle";
const BINDING = "native";
const DIRECTIVE = "directive";
const ELEMENT_PROPERTY = "elementProperty"; const ELEMENT_PROPERTY = "elementProperty";
const ELEMENT_ATTRIBUTE = "elementAttribute"; const ELEMENT_ATTRIBUTE = "elementAttribute";
const ELEMENT_CLASS = "elementClass"; const ELEMENT_CLASS = "elementClass";
@ -13,24 +15,12 @@ const TEXT_NODE = "textNode";
const EVENT = "event"; const EVENT = "event";
const HOST_EVENT = "hostEvent"; const HOST_EVENT = "hostEvent";
export class BindingRecord { export class BindingTarget {
constructor(public mode: string, public implicitReceiver: any, public ast: AST, constructor(public mode: string, public elementIndex: number, public name: string,
public elementIndex: number, public propertyName: string, public propertyUnit: string, public unit: string, public debug: 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();
}
isDirective(): boolean { return this.mode === DIRECTIVE; } isDirective(): boolean { return this.mode === DIRECTIVE; }
isDirectiveLifecycle(): boolean { return this.mode === DIRECTIVE_LIFECYCLE; }
isElementProperty(): boolean { return this.mode === ELEMENT_PROPERTY; } isElementProperty(): boolean { return this.mode === ELEMENT_PROPERTY; }
isElementAttribute(): boolean { return this.mode === ELEMENT_ATTRIBUTE; } isElementAttribute(): boolean { return this.mode === ELEMENT_ATTRIBUTE; }
@ -40,87 +30,118 @@ export class BindingRecord {
isElementStyle(): boolean { return this.mode === ELEMENT_STYLE; } isElementStyle(): boolean { return this.mode === ELEMENT_STYLE; }
isTextNode(): boolean { return this.mode === TEXT_NODE; } 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 { static createDirectiveOnCheck(directiveRecord: DirectiveRecord): BindingRecord {
return new BindingRecord(DIRECTIVE_LIFECYCLE, 0, null, 0, null, null, null, null, "onCheck", return new BindingRecord(DIRECTIVE_LIFECYCLE, null, 0, null, null, "onCheck", directiveRecord);
directiveRecord);
} }
static createDirectiveOnInit(directiveRecord: DirectiveRecord): BindingRecord { static createDirectiveOnInit(directiveRecord: DirectiveRecord): BindingRecord {
return new BindingRecord(DIRECTIVE_LIFECYCLE, 0, null, 0, null, null, null, null, "onInit", return new BindingRecord(DIRECTIVE_LIFECYCLE, null, 0, null, null, "onInit", directiveRecord);
directiveRecord);
} }
static createDirectiveOnChange(directiveRecord: DirectiveRecord): BindingRecord { static createDirectiveOnChange(directiveRecord: DirectiveRecord): BindingRecord {
return new BindingRecord(DIRECTIVE_LIFECYCLE, 0, null, 0, null, null, null, null, "onChange", return new BindingRecord(DIRECTIVE_LIFECYCLE, null, 0, null, null, "onChange", directiveRecord);
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, static createForElementProperty(ast: AST, elementIndex: number,
propertyName: string): BindingRecord { propertyName: string): BindingRecord {
return new BindingRecord(ELEMENT_PROPERTY, 0, ast, elementIndex, propertyName, null, null, null, var t = new BindingTarget(ELEMENT_PROPERTY, elementIndex, propertyName, null, ast.toString());
null, null); return new BindingRecord(BINDING, t, 0, ast, null, null, null);
} }
static createForElementAttribute(ast: AST, elementIndex: number, static createForElementAttribute(ast: AST, elementIndex: number,
attributeName: string): BindingRecord { attributeName: string): BindingRecord {
return new BindingRecord(ELEMENT_ATTRIBUTE, 0, ast, elementIndex, attributeName, null, null, var t = new BindingTarget(ELEMENT_ATTRIBUTE, elementIndex, attributeName, null, ast.toString());
null, null, null); return new BindingRecord(BINDING, t, 0, ast, null, null, null);
} }
static createForElementClass(ast: AST, elementIndex: number, className: string): BindingRecord { static createForElementClass(ast: AST, elementIndex: number, className: string): BindingRecord {
return new BindingRecord(ELEMENT_CLASS, 0, ast, elementIndex, className, null, null, null, null, var t = new BindingTarget(ELEMENT_CLASS, elementIndex, className, null, ast.toString());
null); return new BindingRecord(BINDING, t, 0, ast, null, null, null);
} }
static createForElementStyle(ast: AST, elementIndex: number, styleName: string, static createForElementStyle(ast: AST, elementIndex: number, styleName: string,
unit: string): BindingRecord { unit: string): BindingRecord {
return new BindingRecord(ELEMENT_STYLE, 0, ast, elementIndex, styleName, unit, null, null, null, var t = new BindingTarget(ELEMENT_STYLE, elementIndex, styleName, unit, ast.toString());
null); return new BindingRecord(BINDING, t, 0, ast, null, null, null);
} }
static createForHostProperty(directiveIndex: DirectiveIndex, ast: AST, static createForHostProperty(directiveIndex: DirectiveIndex, ast: AST,
propertyName: string): BindingRecord { propertyName: string): BindingRecord {
return new BindingRecord(ELEMENT_PROPERTY, directiveIndex, ast, directiveIndex.elementIndex, var t = new BindingTarget(ELEMENT_PROPERTY, directiveIndex.elementIndex, propertyName, null,
propertyName, null, null, null, null, null); ast.toString());
return new BindingRecord(BINDING, t, directiveIndex, ast, null, null, null);
} }
static createForHostAttribute(directiveIndex: DirectiveIndex, ast: AST, static createForHostAttribute(directiveIndex: DirectiveIndex, ast: AST,
attributeName: string): BindingRecord { attributeName: string): BindingRecord {
return new BindingRecord(ELEMENT_ATTRIBUTE, directiveIndex, ast, directiveIndex.elementIndex, var t = new BindingTarget(ELEMENT_ATTRIBUTE, directiveIndex.elementIndex, attributeName, null,
attributeName, null, null, null, null, null); ast.toString());
return new BindingRecord(BINDING, t, directiveIndex, ast, null, null, null);
} }
static createForHostClass(directiveIndex: DirectiveIndex, ast: AST, static createForHostClass(directiveIndex: DirectiveIndex, ast: AST,
className: string): BindingRecord { className: string): BindingRecord {
return new BindingRecord(ELEMENT_CLASS, directiveIndex, ast, directiveIndex.elementIndex, var t = new BindingTarget(ELEMENT_CLASS, directiveIndex.elementIndex, className, null,
className, null, null, null, null, null); ast.toString());
return new BindingRecord(BINDING, t, directiveIndex, ast, null, null, null);
} }
static createForHostStyle(directiveIndex: DirectiveIndex, ast: AST, styleName: string, static createForHostStyle(directiveIndex: DirectiveIndex, ast: AST, styleName: string,
unit: string): BindingRecord { unit: string): BindingRecord {
return new BindingRecord(ELEMENT_STYLE, directiveIndex, ast, directiveIndex.elementIndex, var t = new BindingTarget(ELEMENT_STYLE, directiveIndex.elementIndex, styleName, unit,
styleName, unit, null, null, null, null); ast.toString());
return new BindingRecord(BINDING, t, directiveIndex, ast, null, null, null);
} }
static createForTextNode(ast: AST, elementIndex: number): BindingRecord { 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 { 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, static createForHostEvent(ast: AST, eventName: string,
directiveRecord: DirectiveRecord): BindingRecord { directiveRecord: DirectiveRecord): BindingRecord {
var directiveIndex = directiveRecord.directiveIndex; var directiveIndex = directiveRecord.directiveIndex;
return new BindingRecord(EVENT, directiveIndex, ast, directiveIndex.elementIndex, null, null, var t =
eventName, null, null, directiveRecord); new BindingTarget(HOST_EVENT, directiveIndex.elementIndex, eventName, null, ast.toString());
return new BindingRecord(HOST_EVENT, t, directiveIndex, ast, null, null, directiveRecord);
} }
} }

View File

@ -38,7 +38,7 @@ export {
} from './interfaces'; } from './interfaces';
export {CHECK_ONCE, CHECK_ALWAYS, DETACHED, CHECKED, ON_PUSH, DEFAULT} from './constants'; export {CHECK_ONCE, CHECK_ALWAYS, DETACHED, CHECKED, ON_PUSH, DEFAULT} from './constants';
export {DynamicProtoChangeDetector} from './proto_change_detector'; export {DynamicProtoChangeDetector} from './proto_change_detector';
export {BindingRecord} from './binding_record'; export {BindingRecord, BindingTarget} from './binding_record';
export {DirectiveIndex, DirectiveRecord} from './directive_record'; export {DirectiveIndex, DirectiveRecord} from './directive_record';
export {DynamicChangeDetector} from './dynamic_change_detector'; export {DynamicChangeDetector} from './dynamic_change_detector';
export {ChangeDetectorRef} from './change_detector_ref'; export {ChangeDetectorRef} from './change_detector_ref';
@ -93,13 +93,14 @@ export class PreGeneratedChangeDetection extends ChangeDetection {
static isSupported(): boolean { return PregenProtoChangeDetector.isSupported(); } static isSupported(): boolean { return PregenProtoChangeDetector.isSupported(); }
createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector { getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector {
var id = definition.id;
if (StringMapWrapper.contains(this._protoChangeDetectorFactories, id)) { if (StringMapWrapper.contains(this._protoChangeDetectorFactories, id)) {
return StringMapWrapper.get(this._protoChangeDetectorFactories, id)(definition); 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() @Injectable()
export class DynamicChangeDetection extends ChangeDetection { export class DynamicChangeDetection extends ChangeDetection {
createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector { getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector {
return new DynamicProtoChangeDetector(definition); return new DynamicProtoChangeDetector(definition);
} }
get generateDetectors(): boolean { return true; }
} }
/** /**
@ -126,7 +129,9 @@ export class DynamicChangeDetection extends ChangeDetection {
export class JitChangeDetection extends ChangeDetection { export class JitChangeDetection extends ChangeDetection {
static isSupported(): boolean { return JitProtoChangeDetector.isSupported(); } static isSupported(): boolean { return JitProtoChangeDetector.isSupported(); }
createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector { getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector {
return new JitProtoChangeDetector(definition); return new JitProtoChangeDetector(definition);
} }
get generateDetectors(): boolean { return true; }
} }

View File

@ -9,6 +9,7 @@ import {ProtoRecord, RecordType} from './proto_record';
import {CodegenNameUtil, sanitizeName} from './codegen_name_util'; import {CodegenNameUtil, sanitizeName} from './codegen_name_util';
import {CodegenLogicUtil} from './codegen_logic_util'; import {CodegenLogicUtil} from './codegen_logic_util';
import {EventBinding} from './event_binding'; import {EventBinding} from './event_binding';
import {BindingTarget} from './binding_record';
/** /**
@ -30,9 +31,10 @@ export class ChangeDetectorJITGenerator {
_names: CodegenNameUtil; _names: CodegenNameUtil;
_typeName: string; _typeName: string;
constructor(public id: string, private changeDetectionStrategy: string, constructor(private id: string, private changeDetectionStrategy: string,
public records: List<ProtoRecord>, public eventBindings: EventBinding[], private records: List<ProtoRecord>, private propertyBindingTargets: BindingTarget[],
public directiveRecords: List<any>, private generateCheckNoChanges: boolean) { private eventBindings: EventBinding[], private directiveRecords: List<any>,
private devMode: boolean) {
this._names = this._names =
new CodegenNameUtil(this.records, this.eventBindings, this.directiveRecords, UTIL); new CodegenNameUtil(this.records, this.eventBindings, this.directiveRecords, UTIL);
this._logic = new CodegenLogicUtil(this._names, UTIL, changeDetectionStrategy); this._logic = new CodegenLogicUtil(this._names, UTIL, changeDetectionStrategy);
@ -41,9 +43,10 @@ export class ChangeDetectorJITGenerator {
generate(): Function { generate(): Function {
var classDefinition = ` var classDefinition = `
var ${this._typeName} = function ${this._typeName}(dispatcher, protos, directiveRecords) { var ${this._typeName} = function ${this._typeName}(dispatcher) {
${ABSTRACT_CHANGE_DETECTOR}.call( ${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)}"); "${ChangeDetectionUtil.changeDetectionMode(this.changeDetectionStrategy)}");
this.dehydrateDirectives(false); this.dehydrateDirectives(false);
} }
@ -70,13 +73,27 @@ export class ChangeDetectorJITGenerator {
${this._maybeGenDehydrateDirectives()} ${this._maybeGenDehydrateDirectives()}
${this._genPropertyBindingTargets()};
${this._genDirectiveIndices()};
return function(dispatcher) { return function(dispatcher) {
return new ${this._typeName}(dispatcher, protos, directiveRecords); return new ${this._typeName}(dispatcher);
} }
`; `;
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos',
'directiveRecords', classDefinition)( return new Function(ABSTRACT_CHANGE_DETECTOR, UTIL, classDefinition)(AbstractChangeDetector,
AbstractChangeDetector, ChangeDetectionUtil, this.records, this.directiveRecords); 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 { _maybeGenHandleEventInternal(): string {
@ -156,7 +173,7 @@ export class ChangeDetectorJITGenerator {
var lines = ListWrapper.createFixedSize(directiveFieldNames.length); var lines = ListWrapper.createFixedSize(directiveFieldNames.length);
for (var i = 0, iLen = directiveFieldNames.length; i < iLen; ++i) { for (var i = 0, iLen = directiveFieldNames.length; i < iLen; ++i) {
lines[i] = `${directiveFieldNames[i]} = directives.getDirectiveFor( lines[i] = `${directiveFieldNames[i]} = directives.getDirectiveFor(
${this._names.getDirectivesAccessorName()}[${i}].directiveIndex);`; ${this._names.getDirectivesAccessorName()}[${i}]);`;
} }
return lines.join('\n'); return lines.join('\n');
} }
@ -284,9 +301,9 @@ export class ChangeDetectorJITGenerator {
var oldValue = this._names.getFieldName(r.selfIndex); var oldValue = this._names.getFieldName(r.selfIndex);
var br = r.bindingRecord; var br = r.bindingRecord;
if (br.isDirective()) { if (br.target.isDirective()) {
var directiveProperty = var directiveProperty =
`${this._names.getDirectiveName(br.directiveRecord.directiveIndex)}.${br.propertyName}`; `${this._names.getDirectiveName(br.directiveRecord.directiveIndex)}.${br.target.name}`;
return ` return `
${this._genThrowOnChangeCheck(oldValue, newValue)} ${this._genThrowOnChangeCheck(oldValue, newValue)}
${directiveProperty} = ${newValue}; ${directiveProperty} = ${newValue};
@ -301,7 +318,7 @@ export class ChangeDetectorJITGenerator {
} }
_genThrowOnChangeCheck(oldValue: string, newValue: string): string { _genThrowOnChangeCheck(oldValue: string, newValue: string): string {
if (this.generateCheckNoChanges) { if (this.devMode) {
return ` return `
if(throwOnChange) { if(throwOnChange) {
this.throwOnChangeError(${oldValue}, ${newValue}); this.throwOnChangeError(${oldValue}, ${newValue});
@ -313,7 +330,7 @@ export class ChangeDetectorJITGenerator {
} }
_genCheckNoChanges(): string { _genCheckNoChanges(): string {
if (this.generateCheckNoChanges) { if (this.devMode) {
return `${this._typeName}.prototype.checkNoChanges = function() { this.runDetectChanges(true); }`; return `${this._typeName}.prototype.checkNoChanges = function() { this.runDetectChanges(true); }`;
} else { } else {
return ''; return '';
@ -330,7 +347,9 @@ export class ChangeDetectorJITGenerator {
_maybeFirstInBinding(r: ProtoRecord): string { _maybeFirstInBinding(r: ProtoRecord): string {
var prev = ChangeDetectionUtil.protoByIndex(this.records, r.selfIndex - 1); var prev = ChangeDetectionUtil.protoByIndex(this.records, r.selfIndex - 1);
var firstInBindng = isBlank(prev) || prev.bindingRecord !== r.bindingRecord; 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 { _maybeGenLastInDirective(r: ProtoRecord): string {

View File

@ -16,6 +16,8 @@ import {
isDefaultChangeDetectionStrategy isDefaultChangeDetectionStrategy
} from './constants'; } from './constants';
import {implementsOnDestroy} from './pipe_lifecycle_reflector'; 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(); 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);
}
} }

View File

@ -44,8 +44,8 @@ export function coalesce(records: ProtoRecord[]): ProtoRecord[] {
function _selfRecord(r: ProtoRecord, contextIndex: number, selfIndex: number): ProtoRecord { function _selfRecord(r: ProtoRecord, contextIndex: number, selfIndex: number): ProtoRecord {
return new ProtoRecord(RecordType.SELF, "self", null, [], r.fixedArgs, contextIndex, return new ProtoRecord(RecordType.SELF, "self", null, [], r.fixedArgs, contextIndex,
r.directiveIndex, selfIndex, r.bindingRecord, r.expressionAsString, r.directiveIndex, selfIndex, r.bindingRecord, r.lastInBinding,
r.lastInBinding, r.lastInDirective, false, false); r.lastInDirective, false, false, r.propertyBindingIndex);
} }
function _findMatching(r: ProtoRecord, rs: List<ProtoRecord>) { function _findMatching(r: ProtoRecord, rs: List<ProtoRecord>) {
@ -70,9 +70,9 @@ function _replaceIndices(r: ProtoRecord, selfIndex: number, indexMap: Map<any, a
var args = ListWrapper.map(r.args, (a) => _map(indexMap, a)); var args = ListWrapper.map(r.args, (a) => _map(indexMap, a));
var contextIndex = _map(indexMap, r.contextIndex); var contextIndex = _map(indexMap, r.contextIndex);
return new ProtoRecord(r.mode, r.name, r.funcOrValue, args, r.fixedArgs, contextIndex, return new ProtoRecord(r.mode, r.name, r.funcOrValue, args, r.fixedArgs, contextIndex,
r.directiveIndex, selfIndex, r.bindingRecord, r.expressionAsString, r.directiveIndex, selfIndex, r.bindingRecord, r.lastInBinding,
r.lastInBinding, r.lastInDirective, r.argumentToPureFunction, r.lastInDirective, r.argumentToPureFunction, r.referencedBySelf,
r.referencedBySelf); r.propertyBindingIndex);
} }
function _map(indexMap: Map<any, any>, value: number) { function _map(indexMap: Map<any, any>, value: number) {

View File

@ -1,8 +1,9 @@
import {ListWrapper} from 'angular2/src/facade/collection'; 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 {CodegenNameUtil} from './codegen_name_util';
import {codify, combineGeneratedStrings, rawString} from './codegen_facade'; import {codify, combineGeneratedStrings, rawString} from './codegen_facade';
import {ProtoRecord, RecordType} from './proto_record'; import {ProtoRecord, RecordType} from './proto_record';
import {BindingTarget} from './binding_record';
import {DirectiveRecord} from './directive_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 { _genInterpolation(protoRec: ProtoRecord): string {
var iVals = []; var iVals = [];
for (var i = 0; i < protoRec.args.length; ++i) { for (var i = 0; i < protoRec.args.length; ++i) {

View File

@ -10,8 +10,8 @@ import {EventBinding} from './event_binding';
// detection will fail. // detection will fail.
const _ALREADY_CHECKED_ACCESSOR = "alreadyChecked"; const _ALREADY_CHECKED_ACCESSOR = "alreadyChecked";
const _CONTEXT_ACCESSOR = "context"; const _CONTEXT_ACCESSOR = "context";
const _FIRST_PROTO_IN_CURRENT_BINDING = "firstProtoInCurrentBinding"; const _PROP_BINDING_INDEX = "propertyBindingIndex";
const _DIRECTIVES_ACCESSOR = "directiveRecords"; const _DIRECTIVES_ACCESSOR = "directiveIndices";
const _DISPATCHER_ACCESSOR = "dispatcher"; const _DISPATCHER_ACCESSOR = "dispatcher";
const _LOCALS_ACCESSOR = "locals"; const _LOCALS_ACCESSOR = "locals";
const _MODE_ACCESSOR = "mode"; const _MODE_ACCESSOR = "mode";
@ -79,9 +79,7 @@ export class CodegenNameUtil {
getModeName(): string { return this._addFieldPrefix(_MODE_ACCESSOR); } getModeName(): string { return this._addFieldPrefix(_MODE_ACCESSOR); }
getFirstProtoInCurrentBinding(): string { getPropertyBindingIndex(): string { return this._addFieldPrefix(_PROP_BINDING_INDEX); }
return this._addFieldPrefix(_FIRST_PROTO_IN_CURRENT_BINDING);
}
getLocalName(idx: number): string { return `l_${this._sanitizedNames[idx]}`; } getLocalName(idx: number): string { return `l_${this._sanitizedNames[idx]}`; }

View File

@ -9,11 +9,11 @@ import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/faca
import {AbstractChangeDetector} from './abstract_change_detector'; import {AbstractChangeDetector} from './abstract_change_detector';
import {EventBinding} from './event_binding'; 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 {Locals} from './parser/locals';
import {ChangeDetectionUtil, SimpleChange} from './change_detection_util'; import {ChangeDetectionUtil, SimpleChange} from './change_detection_util';
import {ProtoRecord, RecordType} from './proto_record'; import {ProtoRecord, RecordType} from './proto_record';
export class DynamicChangeDetector extends AbstractChangeDetector<any> { export class DynamicChangeDetector extends AbstractChangeDetector<any> {
@ -23,12 +23,13 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
prevContexts: List<any>; prevContexts: List<any>;
directives: any = null; directives: any = null;
constructor(id: string, changeDetectionStrategy: string, dispatcher: any, constructor(id: string, dispatcher: any, numberOfPropertyProtoRecords: number,
protos: List<ProtoRecord>, public eventBindings: EventBinding[], propertyBindingTargets: BindingTarget[], directiveIndices: DirectiveIndex[],
directiveRecords: List<any>) { modeOnHydrate: string, private records: ProtoRecord[],
super(id, dispatcher, protos, directiveRecords, private eventBindings: EventBinding[], private directiveRecords: DirectiveRecord[]) {
ChangeDetectionUtil.changeDetectionMode(changeDetectionStrategy)); super(id, dispatcher, numberOfPropertyProtoRecords, propertyBindingTargets, directiveIndices,
var len = protos.length + 1; modeOnHydrate);
var len = records.length + 1;
this.values = ListWrapper.createFixedSize(len); this.values = ListWrapper.createFixedSize(len);
this.localPipes = ListWrapper.createFixedSize(len); this.localPipes = ListWrapper.createFixedSize(len);
this.prevContexts = ListWrapper.createFixedSize(len); this.prevContexts = ListWrapper.createFixedSize(len);
@ -109,7 +110,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
checkNoChanges(): void { this.runDetectChanges(true); } checkNoChanges(): void { this.runDetectChanges(true); }
detectChangesInRecordsInternal(throwOnChange: boolean) { detectChangesInRecordsInternal(throwOnChange: boolean) {
var protos = this.protos; var protos = this.records;
var changes = null; var changes = null;
var isChanged = false; var isChanged = false;
@ -119,7 +120,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
var directiveRecord = bindingRecord.directiveRecord; var directiveRecord = bindingRecord.directiveRecord;
if (this._firstInBinding(proto)) { if (this._firstInBinding(proto)) {
this.firstProtoInCurrentBinding = proto.selfIndex; this.propertyBindingIndex = proto.propertyBindingIndex;
} }
if (proto.isLifeCycleRecord()) { if (proto.isLifeCycleRecord()) {
@ -154,7 +155,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
} }
_firstInBinding(r: ProtoRecord): boolean { _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; return isBlank(prev) || prev.bindingRecord !== r.bindingRecord;
} }
@ -171,7 +172,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
_updateDirectiveOrElement(change, bindingRecord) { _updateDirectiveOrElement(change, bindingRecord) {
if (isBlank(bindingRecord.directiveRecord)) { if (isBlank(bindingRecord.directiveRecord)) {
this.dispatcher.notifyOnBinding(bindingRecord, change.currentValue); super.notifyDispatcher(change.currentValue);
} else { } else {
var directiveIndex = bindingRecord.directiveRecord.directiveIndex; var directiveIndex = bindingRecord.directiveRecord.directiveIndex;
bindingRecord.setter(this._getDirectiveFor(directiveIndex), change.currentValue); bindingRecord.setter(this._getDirectiveFor(directiveIndex), change.currentValue);

View File

@ -1,4 +1,3 @@
import {ProtoRecord} from './proto_record';
import {BaseException} from "angular2/src/facade/lang"; 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. * This exception is only thrown in dev mode.
*/ */
export class ExpressionChangedAfterItHasBeenCheckedException extends BaseException { export class ExpressionChangedAfterItHasBeenCheckedException extends BaseException {
constructor(proto: ProtoRecord, change: any, context: any) { constructor(exp: string, oldValue: any, currValue: any, context: any) {
super(`Expression '${proto.expressionAsString}' has changed after it was checked. ` + super(`Expression '${exp}' has changed after it was checked. ` +
`Previous value: '${change.previousValue}'. Current value: '${change.currentValue}'`); `Previous value: '${oldValue}'. Current value: '${currValue}'`);
} }
} }
@ -28,10 +27,9 @@ export class ChangeDetectionError extends BaseException {
*/ */
location: string; location: string;
constructor(proto: ProtoRecord, originalException: any, originalStack: any, context: any) { constructor(exp: string, originalException: any, originalStack: any, context: any) {
super(`${originalException} in [${proto.expressionAsString}]`, originalException, originalStack, super(`${originalException} in [${exp}]`, originalException, originalStack, context);
context); this.location = exp;
this.location = proto.expressionAsString;
} }
} }

View File

@ -1,7 +1,7 @@
import {List} from 'angular2/src/facade/collection'; import {List} from 'angular2/src/facade/collection';
import {CONST} from 'angular2/src/facade/lang'; import {CONST} from 'angular2/src/facade/lang';
import {Locals} from './parser/locals'; import {Locals} from './parser/locals';
import {BindingRecord} from './binding_record'; import {BindingTarget, BindingRecord} from './binding_record';
import {DirectiveIndex, DirectiveRecord} from './directive_record'; import {DirectiveIndex, DirectiveRecord} from './directive_record';
import {ChangeDetectorRef} from './change_detector_ref'; import {ChangeDetectorRef} from './change_detector_ref';
@ -32,9 +32,11 @@ import {ChangeDetectorRef} from './change_detector_ref';
*/ */
@CONST() @CONST()
export class ChangeDetection { export class ChangeDetection {
createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector { getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector {
return null; return null;
} }
get generateDetectors(): boolean { return null; }
} }
export class DebugContext { export class DebugContext {
@ -44,7 +46,7 @@ export class DebugContext {
export interface ChangeDispatcher { export interface ChangeDispatcher {
getDebugContext(elementIndex: number, directiveIndex: DirectiveIndex): DebugContext; getDebugContext(elementIndex: number, directiveIndex: DirectiveIndex): DebugContext;
notifyOnBinding(bindingRecord: BindingRecord, value: any): void; notifyOnBinding(bindingTarget: BindingTarget, value: any): void;
notifyOnAllChangesDone(): void; notifyOnAllChangesDone(): void;
} }
@ -72,5 +74,5 @@ export interface ProtoChangeDetector { instantiate(dispatcher: ChangeDispatcher)
export class ChangeDetectorDefinition { export class ChangeDetectorDefinition {
constructor(public id: string, public strategy: string, public variableNames: List<string>, constructor(public id: string, public strategy: string, public variableNames: List<string>,
public bindingRecords: BindingRecord[], public eventRecords: BindingRecord[], public bindingRecords: BindingRecord[], public eventRecords: BindingRecord[],
public directiveRecords: DirectiveRecord[], public generateCheckNoChanges: boolean) {} public directiveRecords: DirectiveRecord[], public devMode: boolean) {}
} }

View File

@ -1,4 +1,5 @@
import {ListWrapper} from 'angular2/src/facade/collection'; import {ListWrapper} from 'angular2/src/facade/collection';
import {isPresent} from 'angular2/src/facade/lang';
import {ProtoChangeDetector, ChangeDetector, ChangeDetectorDefinition} from './interfaces'; import {ProtoChangeDetector, ChangeDetector, ChangeDetectorDefinition} from './interfaces';
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator'; import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
@ -20,9 +21,11 @@ export class JitProtoChangeDetector implements ProtoChangeDetector {
_createFactory(definition: ChangeDetectorDefinition) { _createFactory(definition: ChangeDetectorDefinition) {
var propertyBindingRecords = createPropertyRecords(definition); var propertyBindingRecords = createPropertyRecords(definition);
var eventBindingRecords = createEventRecords(definition); var eventBindingRecords = createEventRecords(definition);
var propertyBindingTargets = this.definition.bindingRecords.map(b => b.target);
return new ChangeDetectorJITGenerator( return new ChangeDetectorJITGenerator(
definition.id, definition.strategy, propertyBindingRecords, eventBindingRecords, definition.id, definition.strategy, propertyBindingRecords, propertyBindingTargets,
this.definition.directiveRecords, this.definition.generateCheckNoChanges) eventBindingRecords, this.definition.directiveRecords, this.definition.devMode)
.generate(); .generate();
} }
} }

View File

@ -1,10 +1,6 @@
library angular2.src.change_detection.pregen_proto_change_detector; 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/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; import 'package:angular2/src/facade/lang.dart' show looseIdentical;
export 'dart:core' show List; export 'dart:core' show List;
@ -26,8 +22,7 @@ export 'package:angular2/src/facade/lang.dart' show looseIdentical;
typedef ProtoChangeDetector PregenProtoChangeDetectorFactory( typedef ProtoChangeDetector PregenProtoChangeDetectorFactory(
ChangeDetectorDefinition definition); ChangeDetectorDefinition definition);
typedef ChangeDetector InstantiateMethod(dynamic dispatcher, typedef ChangeDetector InstantiateMethod(dynamic dispatcher);
List<ProtoRecord> protoRecords, List<DirectiveRecord> directiveRecords);
/// Implementation of [ProtoChangeDetector] for use by pre-generated change /// Implementation of [ProtoChangeDetector] for use by pre-generated change
/// detectors in Angular 2 Dart. /// detectors in Angular 2 Dart.
@ -41,31 +36,18 @@ class PregenProtoChangeDetector extends ProtoChangeDetector {
/// Closure used to generate an actual [ChangeDetector]. /// Closure used to generate an actual [ChangeDetector].
final InstantiateMethod _instantiateMethod; final InstantiateMethod _instantiateMethod;
// [ChangeDetector] dependencies.
final List<ProtoRecord> _protoRecords;
final List<DirectiveRecord> _directiveRecords;
/// Internal ctor. /// Internal ctor.
PregenProtoChangeDetector._(this.id, this._instantiateMethod, PregenProtoChangeDetector._(this.id, this._instantiateMethod);
this._protoRecords, this._directiveRecords);
static bool isSupported() => true; static bool isSupported() => true;
factory PregenProtoChangeDetector( factory PregenProtoChangeDetector(
InstantiateMethod instantiateMethod, ChangeDetectorDefinition def) { InstantiateMethod instantiateMethod, ChangeDetectorDefinition def) {
// TODO(kegluneq): Pre-generate these (#2067). return new PregenProtoChangeDetector._(def.id, instantiateMethod);
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);
} }
@override @override
instantiate(dynamic dispatcher) => instantiate(dynamic dispatcher) => _instantiateMethod(dispatcher);
_instantiateMethod(dispatcher, _protoRecords, _directiveRecords);
} }
/// Provided as an optimization to cut down on '!' characters in generated /// Provided as an optimization to cut down on '!' characters in generated

View File

@ -29,7 +29,7 @@ import {
import {ChangeDetector, ProtoChangeDetector, ChangeDetectorDefinition} from './interfaces'; import {ChangeDetector, ProtoChangeDetector, ChangeDetectorDefinition} from './interfaces';
import {ChangeDetectionUtil} from './change_detection_util'; import {ChangeDetectionUtil} from './change_detection_util';
import {DynamicChangeDetector} from './dynamic_change_detector'; import {DynamicChangeDetector} from './dynamic_change_detector';
import {BindingRecord} from './binding_record'; import {BindingRecord, BindingTarget} from './binding_record';
import {DirectiveRecord, DirectiveIndex} from './directive_record'; import {DirectiveRecord, DirectiveIndex} from './directive_record';
import {EventBinding} from './event_binding'; import {EventBinding} from './event_binding';
@ -38,24 +38,30 @@ import {ProtoRecord, RecordType} from './proto_record';
export class DynamicProtoChangeDetector implements ProtoChangeDetector { export class DynamicProtoChangeDetector implements ProtoChangeDetector {
_propertyBindingRecords: ProtoRecord[]; _propertyBindingRecords: ProtoRecord[];
_propertyBindingTargets: BindingTarget[];
_eventBindingRecords: EventBinding[]; _eventBindingRecords: EventBinding[];
_directiveIndices: DirectiveIndex[];
constructor(private definition: ChangeDetectorDefinition) { constructor(private definition: ChangeDetectorDefinition) {
this._propertyBindingRecords = createPropertyRecords(definition); this._propertyBindingRecords = createPropertyRecords(definition);
this._eventBindingRecords = createEventRecords(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 { instantiate(dispatcher: any): ChangeDetector {
return new DynamicChangeDetector(this.definition.id, this.definition.strategy, dispatcher, return new DynamicChangeDetector(
this._propertyBindingRecords, this._eventBindingRecords, this.definition.id, dispatcher, this._propertyBindingRecords.length,
this.definition.directiveRecords); this._propertyBindingTargets, this._directiveIndices,
ChangeDetectionUtil.changeDetectionMode(this.definition.strategy),
this._propertyBindingRecords, this._eventBindingRecords, this.definition.directiveRecords);
} }
} }
export function createPropertyRecords(definition: ChangeDetectorDefinition): ProtoRecord[] { export function createPropertyRecords(definition: ChangeDetectorDefinition): ProtoRecord[] {
var recordBuilder = new ProtoRecordBuilder(); var recordBuilder = new ProtoRecordBuilder();
ListWrapper.forEach(definition.bindingRecords, ListWrapper.forEachWithIndex(definition.bindingRecords,
(b) => { recordBuilder.add(b, definition.variableNames); }); (b, index) => recordBuilder.add(b, definition.variableNames, index));
return coalesce(recordBuilder.records); return coalesce(recordBuilder.records);
} }
@ -65,7 +71,7 @@ export function createEventRecords(definition: ChangeDetectorDefinition): EventB
return definition.eventRecords.map(er => { return definition.eventRecords.map(er => {
var records = _ConvertAstIntoProtoRecords.create(er, varNames); var records = _ConvertAstIntoProtoRecords.create(er, varNames);
var dirIndex = er.implicitReceiver instanceof DirectiveIndex ? er.implicitReceiver : null; 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 = []; } constructor() { this.records = []; }
add(b: BindingRecord, variableNames: List<string> = null) { add(b: BindingRecord, variableNames: string[], bindingIndex: number) {
var oldLast = ListWrapper.last(this.records); var oldLast = ListWrapper.last(this.records);
if (isPresent(oldLast) && oldLast.bindingRecord.directiveRecord == b.directiveRecord) { if (isPresent(oldLast) && oldLast.bindingRecord.directiveRecord == b.directiveRecord) {
oldLast.lastInDirective = false; oldLast.lastInDirective = false;
} }
var numberOfRecordsBefore = this.records.length; var numberOfRecordsBefore = this.records.length;
this._appendRecords(b, variableNames); this._appendRecords(b, variableNames, bindingIndex);
var newLast = ListWrapper.last(this.records); var newLast = ListWrapper.last(this.records);
if (isPresent(newLast) && newLast !== oldLast) { if (isPresent(newLast) && newLast !== oldLast) {
newLast.lastInBinding = true; newLast.lastInBinding = true;
@ -99,29 +105,30 @@ export class ProtoRecordBuilder {
} }
} }
_appendRecords(b: BindingRecord, variableNames: List<string>) { _appendRecords(b: BindingRecord, variableNames: string[], bindingIndex: number) {
if (b.isDirectiveLifecycle()) { if (b.isDirectiveLifecycle()) {
this.records.push(new ProtoRecord(RecordType.DIRECTIVE_LIFECYCLE, b.lifecycleEvent, null, [], this.records.push(new ProtoRecord(RecordType.DIRECTIVE_LIFECYCLE, b.lifecycleEvent, null, [],
[], -1, null, this.records.length + 1, b, null, false, [], -1, null, this.records.length + 1, b, false, false,
false, false, false)); false, false, null));
} else { } else {
_ConvertAstIntoProtoRecords.append(this.records, b, variableNames); _ConvertAstIntoProtoRecords.append(this.records, b, variableNames, bindingIndex);
} }
} }
} }
class _ConvertAstIntoProtoRecords implements AstVisitor { class _ConvertAstIntoProtoRecords implements AstVisitor {
constructor(private _records: List<ProtoRecord>, private _bindingRecord: BindingRecord, constructor(private _records: List<ProtoRecord>, private _bindingRecord: BindingRecord,
private _expressionAsString: string, private _variableNames: List<any>) {} private _variableNames: string[], private _bindingIndex: number) {}
static append(records: List<ProtoRecord>, b: BindingRecord, variableNames: List<any>) { static append(records: List<ProtoRecord>, b: BindingRecord, variableNames: string[],
var c = new _ConvertAstIntoProtoRecords(records, b, b.ast.toString(), variableNames); bindingIndex: number) {
var c = new _ConvertAstIntoProtoRecords(records, b, variableNames, bindingIndex);
b.ast.visit(c); b.ast.visit(c);
} }
static create(b: BindingRecord, variableNames: List<any>): ProtoRecord[] { static create(b: BindingRecord, variableNames: List<any>): ProtoRecord[] {
var rec = []; var rec = [];
_ConvertAstIntoProtoRecords.append(rec, b, variableNames); _ConvertAstIntoProtoRecords.append(rec, b, variableNames, null);
rec[rec.length - 1].lastInBinding = true; rec[rec.length - 1].lastInBinding = true;
return rec; return rec;
} }
@ -261,12 +268,12 @@ class _ConvertAstIntoProtoRecords implements AstVisitor {
var selfIndex = this._records.length + 1; var selfIndex = this._records.length + 1;
if (context instanceof DirectiveIndex) { if (context instanceof DirectiveIndex) {
this._records.push(new ProtoRecord(type, name, funcOrValue, args, fixedArgs, -1, context, this._records.push(new ProtoRecord(type, name, funcOrValue, args, fixedArgs, -1, context,
selfIndex, this._bindingRecord, this._expressionAsString, selfIndex, this._bindingRecord, false, false, false, false,
false, false, false, false)); this._bindingIndex));
} else { } else {
this._records.push(new ProtoRecord(type, name, funcOrValue, args, fixedArgs, context, null, this._records.push(new ProtoRecord(type, name, funcOrValue, args, fixedArgs, context, null,
selfIndex, this._bindingRecord, this._expressionAsString, selfIndex, this._bindingRecord, false, false, false, false,
false, false, false, false)); this._bindingIndex));
} }
return selfIndex; return selfIndex;
} }

View File

@ -26,9 +26,9 @@ export class ProtoRecord {
constructor(public mode: RecordType, public name: string, public funcOrValue, constructor(public mode: RecordType, public name: string, public funcOrValue,
public args: List<any>, public fixedArgs: List<any>, public contextIndex: number, public args: List<any>, public fixedArgs: List<any>, public contextIndex: number,
public directiveIndex: DirectiveIndex, public selfIndex: number, public directiveIndex: DirectiveIndex, public selfIndex: number,
public bindingRecord: BindingRecord, public expressionAsString: string, public bindingRecord: BindingRecord, public lastInBinding: boolean,
public lastInBinding: boolean, public lastInDirective: boolean, public lastInDirective: boolean, public argumentToPureFunction: boolean,
public argumentToPureFunction: boolean, public referencedBySelf: boolean) {} public referencedBySelf: boolean, public propertyBindingIndex: number) {}
isPureFunction(): boolean { isPureFunction(): boolean {
return this.mode === RecordType.INTERPOLATE || this.mode === RecordType.COLLECTION_LITERAL; return this.mode === RecordType.INTERPOLATE || this.mode === RecordType.COLLECTION_LITERAL;

View File

@ -211,12 +211,10 @@ export class ProtoViewFactory {
var nestedPvVariableBindings = _collectNestedProtoViewsVariableBindings(nestedPvsWithIndex); var nestedPvVariableBindings = _collectNestedProtoViewsVariableBindings(nestedPvsWithIndex);
var nestedPvVariableNames = _collectNestedProtoViewsVariableNames(nestedPvsWithIndex); var nestedPvVariableNames = _collectNestedProtoViewsVariableNames(nestedPvsWithIndex);
var changeDetectorDefs = var protoChangeDetectors =
_getChangeDetectorDefinitions(hostComponentBinding.metadata, nestedPvsWithIndex, this._getProtoChangeDetectors(hostComponentBinding, nestedPvsWithIndex,
nestedPvVariableNames, allRenderDirectiveMetadata); nestedPvVariableNames, allRenderDirectiveMetadata);
var protoChangeDetectors = ListWrapper.map(
changeDetectorDefs,
changeDetectorDef => this._changeDetection.createProtoChangeDetector(changeDetectorDef));
var appProtoViews = ListWrapper.createFixedSize(nestedPvsWithIndex.length); var appProtoViews = ListWrapper.createFixedSize(nestedPvsWithIndex.length);
ListWrapper.forEach(nestedPvsWithIndex, (pvWithIndex: RenderProtoViewWithIndex) => { ListWrapper.forEach(nestedPvsWithIndex, (pvWithIndex: RenderProtoViewWithIndex) => {
var appProtoView = var appProtoView =
@ -230,6 +228,24 @@ export class ProtoViewFactory {
}); });
return appProtoViews; 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,20 +296,33 @@ function _getChangeDetectorDefinitions(
var directiveRecords = var directiveRecords =
bindingRecordsCreator.getDirectiveRecords(elementBinders, allRenderDirectiveMetadata); bindingRecordsCreator.getDirectiveRecords(elementBinders, allRenderDirectiveMetadata);
var strategyName = DEFAULT; var strategyName = DEFAULT;
var typeString;
if (pvWithIndex.renderProtoView.type === ViewType.COMPONENT) { if (pvWithIndex.renderProtoView.type === ViewType.COMPONENT) {
strategyName = hostComponentMetadata.changeDetection; strategyName = hostComponentMetadata.changeDetection;
}
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<RenderProtoViewWithIndex>):
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'; typeString = 'comp';
} else if (pvWithIndex.renderProtoView.type === ViewType.HOST) { } else if (pvWithIndex.renderProtoView.type === ViewType.HOST) {
typeString = 'host'; typeString = 'host';
} else { } else {
typeString = 'embedded'; typeString = 'embedded';
} }
var id = `${hostComponentMetadata.id}_${typeString}_${pvWithIndex.index}`; return `${hostComponentMetadata.id}_${typeString}_${pvWithIndex.index}`;
var variableNames = nestedPvVariableNames[pvWithIndex.index];
return new ChangeDetectorDefinition(id, strategyName, variableNames, propBindingRecords,
eventBindingRecords, directiveRecords, assertionsEnabled());
});
} }
function _createAppProtoView( function _createAppProtoView(

View File

@ -8,12 +8,12 @@ import {
} from 'angular2/src/facade/collection'; } from 'angular2/src/facade/collection';
import { import {
AST, AST,
BindingRecord,
ChangeDetector, ChangeDetector,
ChangeDetectorRef, ChangeDetectorRef,
ChangeDispatcher, ChangeDispatcher,
DirectiveIndex, DirectiveIndex,
DirectiveRecord, DirectiveRecord,
BindingTarget,
Locals, Locals,
ProtoChangeDetector ProtoChangeDetector
} from 'angular2/src/change_detection/change_detection'; } 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 // 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()) { if (b.isTextNode()) {
this.renderer.setText( this.renderer.setText(
this.render, this.mainMergeMapping.renderTextIndices[b.elementIndex + this.textOffset], this.render, this.mainMergeMapping.renderTextIndices[b.elementIndex + this.textOffset],
@ -179,14 +179,14 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
} else { } else {
var elementRef = this.elementRefs[this.elementOffset + b.elementIndex]; var elementRef = this.elementRefs[this.elementOffset + b.elementIndex];
if (b.isElementProperty()) { if (b.isElementProperty()) {
this.renderer.setElementProperty(elementRef, b.propertyName, currentValue); this.renderer.setElementProperty(elementRef, b.name, currentValue);
} else if (b.isElementAttribute()) { } else if (b.isElementAttribute()) {
this.renderer.setElementAttribute(elementRef, b.propertyName, currentValue); this.renderer.setElementAttribute(elementRef, b.name, currentValue);
} else if (b.isElementClass()) { } else if (b.isElementClass()) {
this.renderer.setElementClass(elementRef, b.propertyName, currentValue); this.renderer.setElementClass(elementRef, b.name, currentValue);
} else if (b.isElementStyle()) { } else if (b.isElementStyle()) {
var unit = isPresent(b.propertyUnit) ? b.propertyUnit : ''; var unit = isPresent(b.unit) ? b.unit : '';
this.renderer.setElementStyle(elementRef, b.propertyName, `${currentValue}${unit}`); this.renderer.setElementStyle(elementRef, b.name, `${currentValue}${unit}`);
} else { } else {
throw new BaseException('Unsupported directive record'); throw new BaseException('Unsupported directive record');
} }

View File

@ -134,6 +134,12 @@ class ListWrapper {
list.forEach(fn); 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) { static reduce(List list, fn(a, b), init) {
return list.fold(init, fn); return list.fold(init, fn);
} }

View File

@ -182,6 +182,11 @@ export class ListWrapper {
fn(array[i]); fn(array[i]);
} }
} }
static forEachWithIndex<T>(array: List<T>, fn: (T, number) => void) {
for (var i = 0; i < array.length; i++) {
fn(array[i], i);
}
}
static first<T>(array: List<T>): T { static first<T>(array: List<T>): T {
if (!array) return null; if (!array) return null;
return array[0]; return array[0];

View File

@ -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_change_detector.dart';
import 'package:angular2/src/change_detection/proto_record.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/event_binding.dart';
import 'package:angular2/src/change_detection/binding_record.dart';
import 'package:angular2/src/facade/lang.dart' show BaseException; import 'package:angular2/src/facade/lang.dart' show BaseException;
/// Responsible for generating change detector classes for Angular 2. /// Responsible for generating change detector classes for Angular 2.
@ -79,7 +80,8 @@ class _CodegenState {
final List<EventBinding> _eventBindings; final List<EventBinding> _eventBindings;
final CodegenLogicUtil _logic; final CodegenLogicUtil _logic;
final CodegenNameUtil _names; final CodegenNameUtil _names;
final bool _generateCheckNoChanges; final bool _devMode;
final List<BindingTarget> _propertyBindingTargets;
_CodegenState._( _CodegenState._(
this._changeDetectorDefId, this._changeDetectorDefId,
@ -87,11 +89,12 @@ class _CodegenState {
this._changeDetectorTypeName, this._changeDetectorTypeName,
String changeDetectionStrategy, String changeDetectionStrategy,
this._records, this._records,
this._propertyBindingTargets,
this._eventBindings, this._eventBindings,
this._directiveRecords, this._directiveRecords,
this._logic, this._logic,
this._names, this._names,
this._generateCheckNoChanges) this._devMode)
: _changeDetectionMode = : _changeDetectionMode =
ChangeDetectionUtil.changeDetectionMode(changeDetectionStrategy); ChangeDetectionUtil.changeDetectionMode(changeDetectionStrategy);
@ -99,6 +102,8 @@ class _CodegenState {
ChangeDetectorDefinition def) { ChangeDetectorDefinition def) {
var protoRecords = createPropertyRecords(def); var protoRecords = createPropertyRecords(def);
var eventBindings = createEventRecords(def); var eventBindings = createEventRecords(def);
var propertyBindingTargets = def.bindingRecords.map((b) => b.target).toList();
var names = new CodegenNameUtil(protoRecords, eventBindings, def.directiveRecords, _UTIL); var names = new CodegenNameUtil(protoRecords, eventBindings, def.directiveRecords, _UTIL);
var logic = new CodegenLogicUtil(names, _UTIL, def.strategy); var logic = new CodegenLogicUtil(names, _UTIL, def.strategy);
return new _CodegenState._( return new _CodegenState._(
@ -107,11 +112,12 @@ class _CodegenState {
changeDetectorTypeName, changeDetectorTypeName,
def.strategy, def.strategy,
protoRecords, protoRecords,
propertyBindingTargets,
eventBindings, eventBindings,
def.directiveRecords, def.directiveRecords,
logic, logic,
names, names,
def.generateCheckNoChanges); def.devMode);
} }
void _writeToBuf(StringBuffer buf) { void _writeToBuf(StringBuffer buf) {
@ -119,9 +125,12 @@ class _CodegenState {
class $_changeDetectorTypeName extends $_BASE_CLASS<$_contextTypeName> { class $_changeDetectorTypeName extends $_BASE_CLASS<$_contextTypeName> {
${_genDeclareFields()} ${_genDeclareFields()}
$_changeDetectorTypeName(dispatcher, protos, directiveRecords) $_changeDetectorTypeName(dispatcher)
: super(${codify(_changeDetectorDefId)}, : super(${codify(_changeDetectorDefId)},
dispatcher, protos, directiveRecords, '$_changeDetectionMode') { dispatcher, ${_records.length},
${_changeDetectorTypeName}.gen_propertyBindingTargets,
${_changeDetectorTypeName}.gen_directiveIndices,
'$_changeDetectionMode') {
dehydrateDirectives(false); dehydrateDirectives(false);
} }
@ -145,17 +154,31 @@ class _CodegenState {
${_maybeGenDehydrateDirectives()} ${_maybeGenDehydrateDirectives()}
${_genPropertyBindingTargets()};
${_genDirectiveIndices()};
static $_GEN_PREFIX.ProtoChangeDetector static $_GEN_PREFIX.ProtoChangeDetector
$PROTO_CHANGE_DETECTOR_FACTORY_METHOD( $PROTO_CHANGE_DETECTOR_FACTORY_METHOD(
$_GEN_PREFIX.ChangeDetectorDefinition def) { $_GEN_PREFIX.ChangeDetectorDefinition def) {
return new $_GEN_PREFIX.PregenProtoChangeDetector( return new $_GEN_PREFIX.PregenProtoChangeDetector(
(a, b, c) => new $_changeDetectorTypeName(a, b, c), (a) => new $_changeDetectorTypeName(a),
def); 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() { String _maybeGenHandleEventInternal() {
if (_eventBindings.length > 0) { if (_eventBindings.length > 0) {
var handlers = _eventBindings.map((eb) => _genEventBinding(eb)).join("\n"); var handlers = _eventBindings.map((eb) => _genEventBinding(eb)).join("\n");
@ -239,7 +262,7 @@ class _CodegenState {
var directiveFieldNames = _names.getAllDirectiveNames(); var directiveFieldNames = _names.getAllDirectiveNames();
for (var i = 0; i < directiveFieldNames.length; ++i) { for (var i = 0; i < directiveFieldNames.length; ++i) {
buf.writeln('${directiveFieldNames[i]} = directives.getDirectiveFor(' buf.writeln('${directiveFieldNames[i]} = directives.getDirectiveFor('
'${_names.getDirectivesAccessorName()}[$i].directiveIndex);'); '${_names.getDirectivesAccessorName()}[$i]);');
} }
return '$buf'; return '$buf';
} }
@ -378,9 +401,9 @@ class _CodegenState {
var oldValue = _names.getFieldName(r.selfIndex); var oldValue = _names.getFieldName(r.selfIndex);
var br = r.bindingRecord; var br = r.bindingRecord;
if (br.isDirective()) { if (br.target.isDirective()) {
var directiveProperty = var directiveProperty =
'${_names.getDirectiveName(br.directiveRecord.directiveIndex)}.${br.propertyName}'; '${_names.getDirectiveName(br.directiveRecord.directiveIndex)}.${br.target.name}';
return ''' return '''
${_genThrowOnChangeCheck(oldValue, newValue)} ${_genThrowOnChangeCheck(oldValue, newValue)}
$directiveProperty = $newValue; $directiveProperty = $newValue;
@ -395,7 +418,7 @@ class _CodegenState {
} }
String _genThrowOnChangeCheck(String oldValue, String newValue) { String _genThrowOnChangeCheck(String oldValue, String newValue) {
if (this._generateCheckNoChanges) { if (this._devMode) {
return ''' return '''
if(throwOnChange) { if(throwOnChange) {
this.throwOnChangeError(${oldValue}, ${newValue}); this.throwOnChangeError(${oldValue}, ${newValue});
@ -407,7 +430,7 @@ class _CodegenState {
} }
String _genCheckNoChanges() { String _genCheckNoChanges() {
if (this._generateCheckNoChanges) { if (this._devMode) {
return 'void checkNoChanges() { runDetectChanges(true); }'; return 'void checkNoChanges() { runDetectChanges(true); }';
} else { } else {
return ''; return '';
@ -417,8 +440,8 @@ class _CodegenState {
String _maybeFirstInBinding(ProtoRecord r) { String _maybeFirstInBinding(ProtoRecord r) {
var prev = ChangeDetectionUtil.protoByIndex(_records, r.selfIndex - 1); var prev = ChangeDetectionUtil.protoByIndex(_records, r.selfIndex - 1);
var firstInBindng = prev == null || prev.bindingRecord != r.bindingRecord; var firstInBindng = prev == null || prev.bindingRecord != r.bindingRecord;
return firstInBindng return firstInBindng && !r.bindingRecord.isDirectiveLifecycle()
? "${_names.getFirstProtoInCurrentBinding()} = ${r.selfIndex};" ? "${_names.getPropertyBindingIndex()} = ${r.propertyBindingIndex};"
: ''; : '';
} }

View File

@ -14,7 +14,7 @@ class NullReflectionCapabilities implements ReflectionCapabilities {
return false; return false;
} }
Function factory(Type type) => _notImplemented('factory'); Function factory(Type type) => _notImplemented("factory");
List<List> parameters(typeOrFunc) => _notImplemented('parameters'); List<List> parameters(typeOrFunc) => _notImplemented('parameters');

View File

@ -30,12 +30,12 @@ export function main() {
var map = {'id': (def) => proto}; var map = {'id': (def) => proto};
var cd = new PreGeneratedChangeDetection(map); 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", () => { it("should delegate to dynamic change detection otherwise", () => {
var cd = new PreGeneratedChangeDetection({}); var cd = new PreGeneratedChangeDetection({});
expect(cd.createProtoChangeDetector(def)).toBeAnInstanceOf(DynamicProtoChangeDetector); expect(cd.getProtoChangeDetector('id', def)).toBeAnInstanceOf(DynamicProtoChangeDetector);
}); });
}); });
} }

View File

@ -1135,8 +1135,8 @@ class TestDispatcher implements ChangeDispatcher {
this.onAllChangesDoneCalled = true; this.onAllChangesDoneCalled = true;
} }
notifyOnBinding(binding, value) { notifyOnBinding(target, value) {
this.log.push(`${binding.propertyName}=${this._asString(value)}`); this.log.push(`${target.name}=${this._asString(value)}`);
this.loggedValues.push(value); this.loggedValues.push(value);
} }

View File

@ -21,8 +21,7 @@ export function main() {
if (isBlank(argumentToPureFunction)) argumentToPureFunction = false; if (isBlank(argumentToPureFunction)) argumentToPureFunction = false;
return new ProtoRecord(mode, name, funcOrValue, args, null, contextIndex, directiveIndex, return new ProtoRecord(mode, name, funcOrValue, args, null, contextIndex, directiveIndex,
selfIndex, null, null, lastInBinding, false, argumentToPureFunction, selfIndex, null, lastInBinding, false, argumentToPureFunction, false, 0);
false);
} }
describe("change detection - coalesce", () => { describe("change detection - coalesce", () => {
@ -64,7 +63,7 @@ export function main() {
[r("user", [], 0, 1, {lastInBinding: true}), r("user", [], 0, 2, {lastInBinding: true})]); [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, 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", () => { it("should set referencedBySelf", () => {

View File

@ -19,7 +19,7 @@ export function main() {
it('should set argumentToPureFunction flag', inject([Parser], (p: Parser) => { it('should set argumentToPureFunction flag', inject([Parser], (p: Parser) => {
var builder = new ProtoRecordBuilder(); var builder = new ProtoRecordBuilder();
var ast = p.parseBinding("[1,2]", "location"); // collection literal is a pure function 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); var isPureFunc = builder.records.map(r => r.argumentToPureFunction);
expect(isPureFunc).toEqual([true, true, false]); expect(isPureFunc).toEqual([true, true, false]);
@ -29,7 +29,7 @@ export function main() {
inject([Parser], (p: Parser) => { inject([Parser], (p: Parser) => {
var builder = new ProtoRecordBuilder(); var builder = new ProtoRecordBuilder();
var ast = p.parseBinding("f(1,2)", "location"); 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); var isPureFunc = builder.records.map(r => r.argumentToPureFunction);
expect(isPureFunc).toEqual([false, false, false]); expect(isPureFunc).toEqual([false, false, false]);

View File

@ -20,8 +20,8 @@ export function main() {
if (isBlank(argumentToPureFunction)) argumentToPureFunction = false; if (isBlank(argumentToPureFunction)) argumentToPureFunction = false;
if (isBlank(referencedBySelf)) referencedBySelf = false; if (isBlank(referencedBySelf)) referencedBySelf = false;
return new ProtoRecord(mode, name, null, [], null, 0, directiveIndex, 0, null, null, return new ProtoRecord(mode, name, null, [], null, 0, directiveIndex, 0, null, lastInBinding,
lastInBinding, false, argumentToPureFunction, referencedBySelf); false, argumentToPureFunction, referencedBySelf, 0);
} }
describe("ProtoRecord", () => { describe("ProtoRecord", () => {

View File

@ -980,7 +980,7 @@ export function main() {
}); });
it("should inject ChangeDetectorRef of the component's view into the component", () => { 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 = <any>new DummyView(); var view = <any>new DummyView();
var childView = new DummyView(); var childView = new DummyView();
childView.changeDetector = cd; childView.changeDetector = cd;
@ -993,7 +993,7 @@ export function main() {
}); });
it("should inject ChangeDetectorRef of the containing component into directives", () => { 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 = <any>new DummyView(); var view = <any>new DummyView();
view.changeDetector =cd; view.changeDetector =cd;
var binding = DirectiveBinding.createFromType(DirectiveNeedsChangeDetectorRef, new DirectiveMetadata()); var binding = DirectiveBinding.createFromType(DirectiveNeedsChangeDetectorRef, new DirectiveMetadata());

View File

@ -20,7 +20,8 @@ import {
ChangeDetection, ChangeDetection,
ChangeDetectorDefinition, ChangeDetectorDefinition,
BindingRecord, BindingRecord,
DirectiveIndex DirectiveIndex,
Parser
} from 'angular2/src/change_detection/change_detection'; } from 'angular2/src/change_detection/change_detection';
import { import {
BindingRecordsCreator, BindingRecordsCreator,
@ -177,39 +178,44 @@ export function main() {
beforeEach(() => { creator = new BindingRecordsCreator(); }); beforeEach(() => { creator = new BindingRecordsCreator(); });
describe('getEventBindingRecords', () => { describe('getEventBindingRecords', () => {
it("should return template event records", () => { it("should return template event records", inject([Parser], (p: Parser) => {
var ast1 = p.parseAction("1", null);
var ast2 = p.parseAction("2", null);
var rec = creator.getEventBindingRecords( var rec = creator.getEventBindingRecords(
[ [
new RenderElementBinder( new RenderElementBinder(
{eventBindings: [new EventBinding("a", null)], directives: []}), {eventBindings: [new EventBinding("a", ast1)], directives: []}),
new RenderElementBinder( new RenderElementBinder(
{eventBindings: [new EventBinding("b", null)], directives: []}) {eventBindings: [new EventBinding("b", ast2)], directives: []})
], ],
[]); []);
expect(rec).toEqual([ expect(rec).toEqual([
BindingRecord.createForEvent(null, "a", 0), BindingRecord.createForEvent(ast1, "a", 0),
BindingRecord.createForEvent(null, "b", 1) BindingRecord.createForEvent(ast2, "b", 1)
]); ]);
}); }));
it('should return host event records', inject([Parser], (p: Parser) => {
var ast1 = p.parseAction("1", null);
it('should return host event records', () => {
var rec = creator.getEventBindingRecords( var rec = creator.getEventBindingRecords(
[ [
new RenderElementBinder({ new RenderElementBinder({
eventBindings: [], eventBindings: [],
directives: [ directives: [
new DirectiveBinder( new DirectiveBinder(
{directiveIndex: 0, eventBindings: [new EventBinding("a", null)]}) {directiveIndex: 0, eventBindings: [new EventBinding("a", ast1)]})
] ]
}) })
], ],
[RenderDirectiveMetadata.create({id: 'some-id'})]); [RenderDirectiveMetadata.create({id: 'some-id'})]);
expect(rec.length).toEqual(1); expect(rec.length).toEqual(1);
expect(rec[0].eventName).toEqual("a"); expect(rec[0].target.name).toEqual("a");
expect(rec[0].implicitReceiver).toBeAnInstanceOf(DirectiveIndex); expect(rec[0].implicitReceiver).toBeAnInstanceOf(DirectiveIndex);
}); }));
}); });
}); });
}); });
@ -253,6 +259,7 @@ function createRenderViewportElementBinder(nestedProtoView) {
class ChangeDetectionSpy extends SpyObject { class ChangeDetectionSpy extends SpyObject {
constructor() { super(ChangeDetection); } constructor() { super(ChangeDetection); }
noSuchMethod(m) { return super.noSuchMethod(m) } noSuchMethod(m) { return super.noSuchMethod(m) }
get generateDetectors() { return true; }
} }
@Component({selector: 'main-comp'}) @Component({selector: 'main-comp'})

View File

@ -90,6 +90,18 @@ export function main() {
it('should return null for an empty list', it('should return null for an empty list',
() => { expect(ListWrapper.maximum([], x => x)).toEqual(null); }); () => { 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', () => { describe('StringMapWrapper', () => {

View File

@ -13,9 +13,7 @@ void initReflector() {
if (_visited) return; if (_visited) return;
_visited = true; _visited = true;
_ngRef.reflector _ngRef.reflector
..registerType( ..registerType(MyComponent, new _ngRef.ReflectionInfo(const [
MyComponent,
new _ngRef.ReflectionInfo(const [
const Component(selector: '[soup]'), const Component(selector: '[soup]'),
const View(template: 'Salad: {{myNum}} is awesome') const View(template: 'Salad: {{myNum}} is awesome')
], const [], () => new MyComponent())) ], const [], () => new MyComponent()))
@ -28,19 +26,23 @@ class _MyComponent_ChangeDetector0
extends _gen.AbstractChangeDetector<MyComponent> { extends _gen.AbstractChangeDetector<MyComponent> {
var myNum0, interpolate1; var myNum0, interpolate1;
_MyComponent_ChangeDetector0(dispatcher, protos, directiveRecords) _MyComponent_ChangeDetector0(dispatcher) : super(
: super("MyComponent_comp_0", dispatcher, protos, directiveRecords, "MyComponent_comp_0", dispatcher, 2,
'ALWAYS_CHECK') { _MyComponent_ChangeDetector0.gen_propertyBindingTargets,
_MyComponent_ChangeDetector0.gen_directiveIndices, 'ALWAYS_CHECK') {
dehydrateDirectives(false); dehydrateDirectives(false);
} }
void detectChangesInRecordsInternal(throwOnChange) { 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; c_myNum0 = false;
var isChanged = false; var isChanged = false;
var changes = null; var changes = null;
this.firstProtoInCurrentBinding = 1; this.propertyBindingIndex = 0;
l_myNum0 = l_context.myNum; l_myNum0 = l_context.myNum;
if (_gen.looseNotIdentical(l_myNum0, this.myNum0)) { if (_gen.looseNotIdentical(l_myNum0, this.myNum0)) {
c_myNum0 = true; c_myNum0 = true;
@ -75,9 +77,16 @@ class _MyComponent_ChangeDetector0
this.myNum0 = this.interpolate1 = _gen.ChangeDetectionUtil.uninitialized; this.myNum0 = this.interpolate1 = _gen.ChangeDetectionUtil.uninitialized;
} }
static var gen_propertyBindingTargets = [
_gen.ChangeDetectionUtil.bindingTarget("textNode", 0, null, null,
"Salad: {{myNum}} is awesome in MyComponent: <template>")
];
static var gen_directiveIndices = [];
static _gen.ProtoChangeDetector newProtoChangeDetector( static _gen.ProtoChangeDetector newProtoChangeDetector(
_gen.ChangeDetectorDefinition def) { _gen.ChangeDetectorDefinition def) {
return new _gen.PregenProtoChangeDetector( return new _gen.PregenProtoChangeDetector(
(a, b, c) => new _MyComponent_ChangeDetector0(a, b, c), def); (a) => new _MyComponent_ChangeDetector0(a), def);
} }
} }

View File

@ -249,8 +249,8 @@ function setUpChangeDetection(changeDetection: ChangeDetection, iterations, obje
var dispatcher = new DummyDispatcher(); var dispatcher = new DummyDispatcher();
var parser = new Parser(new Lexer()); var parser = new Parser(new Lexer());
var parentProto = changeDetection.createProtoChangeDetector( var parentProto = changeDetection.getProtoChangeDetector(
new ChangeDetectorDefinition('parent', null, [], [], [], [], false)); "id", new ChangeDetectorDefinition('parent', null, [], [], [], [], false));
var parentCd = parentProto.instantiate(dispatcher); var parentCd = parentProto.instantiate(dispatcher);
var directiveRecord = new DirectiveRecord({directiveIndex: new DirectiveIndex(0, 0)}); var directiveRecord = new DirectiveRecord({directiveIndex: new DirectiveIndex(0, 0)});
@ -277,7 +277,8 @@ function setUpChangeDetection(changeDetection: ChangeDetection, iterations, obje
reflector.setter("field9"), directiveRecord) reflector.setter("field9"), directiveRecord)
]; ];
var proto = changeDetection.createProtoChangeDetector( var proto = changeDetection.getProtoChangeDetector(
"id",
new ChangeDetectorDefinition("proto", null, [], bindings, [], [directiveRecord], false)); new ChangeDetectorDefinition("proto", null, [], bindings, [], [directiveRecord], false));
var targetObj = new Obj(); var targetObj = new Obj();