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 {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<T> implements ChangeDetector {
@ -35,24 +33,19 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
// change detection will fail.
alreadyChecked: any = false;
context: T;
directiveRecords: List<DirectiveRecord>;
dispatcher: ChangeDispatcher;
locals: Locals = null;
mode: string = null;
pipes: Pipes = null;
firstProtoInCurrentBinding: number;
protos: List<ProtoRecord>;
propertyBindingIndex: number;
// This is an experimental feature. Works only in Dart.
subscriptions: any[];
streams: any[];
constructor(public id: string, dispatcher: ChangeDispatcher, protos: List<ProtoRecord>,
directiveRecords: List<DirectiveRecord>, 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<T> 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<T> 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<T> 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<T> 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<T> 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];
}
}

View File

@ -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);
}
}

View File

@ -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; }
}

View File

@ -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<ProtoRecord>, public eventBindings: EventBinding[],
public directiveRecords: List<any>, private generateCheckNoChanges: boolean) {
constructor(private id: string, private changeDetectionStrategy: string,
private records: List<ProtoRecord>, private propertyBindingTargets: BindingTarget[],
private eventBindings: EventBinding[], private directiveRecords: List<any>,
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 {

View File

@ -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);
}
}

View File

@ -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<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 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<any, any>, value: number) {

View File

@ -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) {

View File

@ -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]}`; }

View File

@ -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<any> {
@ -23,12 +23,13 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
prevContexts: List<any>;
directives: any = null;
constructor(id: string, changeDetectionStrategy: string, dispatcher: any,
protos: List<ProtoRecord>, public eventBindings: EventBinding[],
directiveRecords: List<any>) {
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<any> {
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<any> {
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<any> {
}
_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<any> {
_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);

View File

@ -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;
}
}

View File

@ -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<string>,
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 {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();
}
}

View File

@ -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<ProtoRecord> protoRecords, List<DirectiveRecord> 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<ProtoRecord> _protoRecords;
final List<DirectiveRecord> _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

View File

@ -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<string> = 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<string>) {
_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<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>) {
var c = new _ConvertAstIntoProtoRecords(records, b, b.ast.toString(), variableNames);
static append(records: List<ProtoRecord>, b: BindingRecord, variableNames: string[],
bindingIndex: number) {
var c = new _ConvertAstIntoProtoRecords(records, b, variableNames, bindingIndex);
b.ast.visit(c);
}
static create(b: BindingRecord, variableNames: List<any>): 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;
}

View File

@ -26,9 +26,9 @@ export class ProtoRecord {
constructor(public mode: RecordType, public name: string, public funcOrValue,
public args: List<any>, public fixedArgs: List<any>, 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;

View File

@ -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<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';
} 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<string, string>, allDirectives: List<DirectiveBinding>,

View File

@ -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');
}

View File

@ -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);
}

View File

@ -182,6 +182,11 @@ export class ListWrapper {
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 {
if (!array) return null;
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_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<EventBinding> _eventBindings;
final CodegenLogicUtil _logic;
final CodegenNameUtil _names;
final bool _generateCheckNoChanges;
final bool _devMode;
final List<BindingTarget> _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};"
: '';
}

View File

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

View File

@ -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);
});
});
}

View File

@ -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);
}

View File

@ -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", () => {

View File

@ -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]);

View File

@ -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", () => {

View File

@ -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 = <any>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 = <any>new DummyView();
view.changeDetector =cd;
var binding = DirectiveBinding.createFromType(DirectiveNeedsChangeDetectorRef, new DirectiveMetadata());

View File

@ -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'})

View File

@ -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', () => {

View File

@ -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<MyComponent> {
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: <template>")
];
static var gen_directiveIndices = [];
static _gen.ProtoChangeDetector newProtoChangeDetector(
_gen.ChangeDetectorDefinition def) {
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 parser = new Parser(new Lexer());
var parentProto = changeDetection.createProtoChangeDetector(
new ChangeDetectorDefinition('parent', null, [], [], [], [], false));
var parentProto = changeDetection.getProtoChangeDetector(
"id", new ChangeDetectorDefinition('parent', null, [], [], [], [], false));
var parentCd = parentProto.instantiate(dispatcher);
var directiveRecord = new DirectiveRecord({directiveIndex: new DirectiveIndex(0, 0)});
@ -277,7 +277,8 @@ function setUpChangeDetection(changeDetection: ChangeDetection, iterations, obje
reflector.setter("field9"), directiveRecord)
];
var proto = changeDetection.createProtoChangeDetector(
var proto = changeDetection.getProtoChangeDetector(
"id",
new ChangeDetectorDefinition("proto", null, [], bindings, [], [directiveRecord], false));
var targetObj = new Obj();