Skip to content

Commit d2d0715

Browse files
committed
feat(change_detection): do not reparse AST when using generated detectors
1 parent b986c54 commit d2d0715

31 files changed

+426
-282
lines changed

modules/angular2/src/change_detection/abstract_change_detector.ts

Lines changed: 24 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,15 @@ import {isPresent, isBlank, BaseException, StringWrapper} from 'angular2/src/fac
22
import {List, ListWrapper} from 'angular2/src/facade/collection';
33
import {ChangeDetectionUtil} from './change_detection_util';
44
import {ChangeDetectorRef} from './change_detector_ref';
5-
import {DirectiveRecord} from './directive_record';
5+
import {DirectiveIndex} from './directive_record';
66
import {ChangeDetector, ChangeDispatcher} from './interfaces';
77
import {Pipes} from './pipes';
88
import {
99
ChangeDetectionError,
1010
ExpressionChangedAfterItHasBeenCheckedException,
1111
DehydratedException
1212
} from './exceptions';
13-
import {ProtoRecord} from './proto_record';
14-
import {BindingRecord} from './binding_record';
13+
import {BindingTarget} from './binding_record';
1514
import {Locals} from './parser/locals';
1615
import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from './constants';
1716
import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile';
@@ -20,9 +19,8 @@ import {isObservable} from './observable_facade';
2019
var _scope_check: WtfScopeFn = wtfCreateScope(`ChangeDetector#check(ascii id, bool throwOnChange)`);
2120

2221
class _Context {
23-
constructor(public element: any, public componentElement: any, public instance: any,
24-
public context: any, public locals: any, public injector: any,
25-
public expression: any) {}
22+
constructor(public element: any, public componentElement: any, public context: any,
23+
public locals: any, public injector: any, public expression: any) {}
2624
}
2725

2826
export class AbstractChangeDetector<T> implements ChangeDetector {
@@ -35,24 +33,19 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
3533
// change detection will fail.
3634
alreadyChecked: any = false;
3735
context: T;
38-
directiveRecords: List<DirectiveRecord>;
39-
dispatcher: ChangeDispatcher;
4036
locals: Locals = null;
4137
mode: string = null;
4238
pipes: Pipes = null;
43-
firstProtoInCurrentBinding: number;
44-
protos: List<ProtoRecord>;
39+
propertyBindingIndex: number;
4540

4641
// This is an experimental feature. Works only in Dart.
4742
subscriptions: any[];
4843
streams: any[];
4944

50-
constructor(public id: string, dispatcher: ChangeDispatcher, protos: List<ProtoRecord>,
51-
directiveRecords: List<DirectiveRecord>, public modeOnHydrate: string) {
45+
constructor(public id: string, public dispatcher: ChangeDispatcher,
46+
public numberOfPropertyProtoRecords: number, public bindingTargets: BindingTarget[],
47+
public directiveIndices: DirectiveIndex[], public modeOnHydrate: string) {
5248
this.ref = new ChangeDetectorRef(this);
53-
this.directiveRecords = directiveRecords;
54-
this.dispatcher = dispatcher;
55-
this.protos = protos;
5649
}
5750

5851
addChild(cd: ChangeDetector): void {
@@ -99,7 +92,7 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
9992
// implementation of `detectChangesInRecordsInternal` which does the work of detecting changes
10093
// and which this method will call.
10194
// This method expects that `detectChangesInRecordsInternal` will set the property
102-
// `this.firstProtoInCurrentBinding` to the selfIndex of the first proto record. This is to
95+
// `this.propertyBindingIndex` to the propertyBindingIndex of the first proto record. This is to
10396
// facilitate error reporting.
10497
detectChangesInRecords(throwOnChange: boolean): void {
10598
if (!this.hydrated()) {
@@ -115,9 +108,9 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
115108
// Subclasses should override this method to perform any work necessary to detect and report
116109
// changes. For example, changes should be reported via `ChangeDetectionUtil.addChange`, lifecycle
117110
// methods should be called, etc.
118-
// This implementation should also set `this.firstProtoInCurrentBinding` to the selfIndex of the
119-
// first proto record
120-
// to facilitate error reporting. See {@link #detectChangesInRecords}.
111+
// This implementation should also set `this.propertyBindingIndex` to the propertyBindingIndex of
112+
// the
113+
// first proto record to facilitate error reporting. See {@link #detectChangesInRecords}.
121114
detectChangesInRecordsInternal(throwOnChange: boolean): void {}
122115

123116
// This method is not intended to be overridden. Subclasses should instead provide an
@@ -195,8 +188,8 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
195188
protected observe(value: any, index: number): any {
196189
if (isObservable(value)) {
197190
if (isBlank(this.subscriptions)) {
198-
this.subscriptions = ListWrapper.createFixedSize(this.protos.length + 1);
199-
this.streams = ListWrapper.createFixedSize(this.protos.length + 1);
191+
this.subscriptions = ListWrapper.createFixedSize(this.numberOfPropertyProtoRecords + 1);
192+
this.streams = ListWrapper.createFixedSize(this.numberOfPropertyProtoRecords + 1);
200193
}
201194
if (isBlank(this.subscriptions[index])) {
202195
this.streams[index] = value.changes;
@@ -211,7 +204,7 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
211204
}
212205

213206
protected getDetectorFor(directives: any, index: number): ChangeDetector {
214-
return directives.getDetectorFor(this.directiveRecords[index].directiveIndex);
207+
return directives.getDetectorFor(this.directiveIndices[index]);
215208
}
216209

217210
protected notifyDispatcher(value: any): void {
@@ -223,31 +216,26 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
223216
if (isBlank(changes)) {
224217
changes = {};
225218
}
226-
changes[this._currentBinding().propertyName] =
227-
ChangeDetectionUtil.simpleChange(oldValue, newValue);
219+
changes[this._currentBinding().name] = ChangeDetectionUtil.simpleChange(oldValue, newValue);
228220
return changes;
229221
}
230222

231223
private _throwError(exception: any, stack: any): void {
232-
var proto = this._currentBindingProto();
233-
var c = this.dispatcher.getDebugContext(proto.bindingRecord.elementIndex, proto.directiveIndex);
234-
var context = isPresent(c) ? new _Context(c.element, c.componentElement, c.directive, c.context,
235-
c.locals, c.injector, proto.expressionAsString) :
224+
var c = this.dispatcher.getDebugContext(this._currentBinding().elementIndex, null);
225+
var context = isPresent(c) ? new _Context(c.element, c.componentElement, c.context, c.locals,
226+
c.injector, this._currentBinding().debug) :
236227
null;
237-
throw new ChangeDetectionError(proto, exception, stack, context);
228+
throw new ChangeDetectionError(this._currentBinding().debug, exception, stack, context);
238229
}
239230

240231
protected throwOnChangeError(oldValue: any, newValue: any): void {
241-
var change = ChangeDetectionUtil.simpleChange(oldValue, newValue);
242-
throw new ExpressionChangedAfterItHasBeenCheckedException(this._currentBindingProto(), change,
243-
null);
232+
throw new ExpressionChangedAfterItHasBeenCheckedException(this._currentBinding().debug,
233+
oldValue, newValue, null);
244234
}
245235

246236
protected throwDehydratedError(): void { throw new DehydratedException(); }
247237

248-
private _currentBinding(): BindingRecord { return this._currentBindingProto().bindingRecord; }
249-
250-
private _currentBindingProto(): ProtoRecord {
251-
return ChangeDetectionUtil.protoByIndex(this.protos, this.firstProtoInCurrentBinding);
238+
private _currentBinding(): BindingTarget {
239+
return this.bindingTargets[this.propertyBindingIndex];
252240
}
253241
}

modules/angular2/src/change_detection/binding_record.ts

Lines changed: 67 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ import {SetterFn} from 'angular2/src/reflection/types';
33
import {AST} from './parser/ast';
44
import {DirectiveIndex, DirectiveRecord} from './directive_record';
55

6-
const DIRECTIVE = "directive";
76
const DIRECTIVE_LIFECYCLE = "directiveLifecycle";
7+
const BINDING = "native";
8+
9+
const DIRECTIVE = "directive";
810
const ELEMENT_PROPERTY = "elementProperty";
911
const ELEMENT_ATTRIBUTE = "elementAttribute";
1012
const ELEMENT_CLASS = "elementClass";
@@ -13,24 +15,12 @@ const TEXT_NODE = "textNode";
1315
const EVENT = "event";
1416
const HOST_EVENT = "hostEvent";
1517

16-
export class BindingRecord {
17-
constructor(public mode: string, public implicitReceiver: any, public ast: AST,
18-
public elementIndex: number, public propertyName: string, public propertyUnit: string,
19-
public eventName: string, public setter: SetterFn, public lifecycleEvent: string,
20-
public directiveRecord: DirectiveRecord) {}
21-
22-
callOnChange(): boolean {
23-
return isPresent(this.directiveRecord) && this.directiveRecord.callOnChange;
24-
}
25-
26-
isDefaultChangeDetection(): boolean {
27-
return isBlank(this.directiveRecord) || this.directiveRecord.isDefaultChangeDetection();
28-
}
18+
export class BindingTarget {
19+
constructor(public mode: string, public elementIndex: number, public name: string,
20+
public unit: string, public debug: string) {}
2921

3022
isDirective(): boolean { return this.mode === DIRECTIVE; }
3123

32-
isDirectiveLifecycle(): boolean { return this.mode === DIRECTIVE_LIFECYCLE; }
33-
3424
isElementProperty(): boolean { return this.mode === ELEMENT_PROPERTY; }
3525

3626
isElementAttribute(): boolean { return this.mode === ELEMENT_ATTRIBUTE; }
@@ -40,87 +30,118 @@ export class BindingRecord {
4030
isElementStyle(): boolean { return this.mode === ELEMENT_STYLE; }
4131

4232
isTextNode(): boolean { return this.mode === TEXT_NODE; }
33+
}
4334

44-
static createForDirective(ast: AST, propertyName: string, setter: SetterFn,
45-
directiveRecord: DirectiveRecord): BindingRecord {
46-
return new BindingRecord(DIRECTIVE, 0, ast, 0, propertyName, null, null, setter, null,
47-
directiveRecord);
35+
export class BindingRecord {
36+
constructor(public mode: string, public target: BindingTarget, public implicitReceiver: any,
37+
public ast: AST, public setter: SetterFn, public lifecycleEvent: string,
38+
public directiveRecord: DirectiveRecord) {}
39+
40+
isDirectiveLifecycle(): boolean { return this.mode === DIRECTIVE_LIFECYCLE; }
41+
42+
callOnChange(): boolean {
43+
return isPresent(this.directiveRecord) && this.directiveRecord.callOnChange;
4844
}
4945

46+
isDefaultChangeDetection(): boolean {
47+
return isBlank(this.directiveRecord) || this.directiveRecord.isDefaultChangeDetection();
48+
}
49+
50+
5051
static createDirectiveOnCheck(directiveRecord: DirectiveRecord): BindingRecord {
51-
return new BindingRecord(DIRECTIVE_LIFECYCLE, 0, null, 0, null, null, null, null, "onCheck",
52-
directiveRecord);
52+
return new BindingRecord(DIRECTIVE_LIFECYCLE, null, 0, null, null, "onCheck", directiveRecord);
5353
}
5454

5555
static createDirectiveOnInit(directiveRecord: DirectiveRecord): BindingRecord {
56-
return new BindingRecord(DIRECTIVE_LIFECYCLE, 0, null, 0, null, null, null, null, "onInit",
57-
directiveRecord);
56+
return new BindingRecord(DIRECTIVE_LIFECYCLE, null, 0, null, null, "onInit", directiveRecord);
5857
}
5958

6059
static createDirectiveOnChange(directiveRecord: DirectiveRecord): BindingRecord {
61-
return new BindingRecord(DIRECTIVE_LIFECYCLE, 0, null, 0, null, null, null, null, "onChange",
62-
directiveRecord);
60+
return new BindingRecord(DIRECTIVE_LIFECYCLE, null, 0, null, null, "onChange", directiveRecord);
61+
}
62+
63+
64+
65+
static createForDirective(ast: AST, propertyName: string, setter: SetterFn,
66+
directiveRecord: DirectiveRecord): BindingRecord {
67+
var t = new BindingTarget(DIRECTIVE, null, propertyName, null, ast.toString());
68+
return new BindingRecord(DIRECTIVE, t, 0, ast, setter, null, directiveRecord);
6369
}
6470

71+
72+
6573
static createForElementProperty(ast: AST, elementIndex: number,
6674
propertyName: string): BindingRecord {
67-
return new BindingRecord(ELEMENT_PROPERTY, 0, ast, elementIndex, propertyName, null, null, null,
68-
null, null);
75+
var t = new BindingTarget(ELEMENT_PROPERTY, elementIndex, propertyName, null, ast.toString());
76+
return new BindingRecord(BINDING, t, 0, ast, null, null, null);
6977
}
7078

7179
static createForElementAttribute(ast: AST, elementIndex: number,
7280
attributeName: string): BindingRecord {
73-
return new BindingRecord(ELEMENT_ATTRIBUTE, 0, ast, elementIndex, attributeName, null, null,
74-
null, null, null);
81+
var t = new BindingTarget(ELEMENT_ATTRIBUTE, elementIndex, attributeName, null, ast.toString());
82+
return new BindingRecord(BINDING, t, 0, ast, null, null, null);
7583
}
7684

7785
static createForElementClass(ast: AST, elementIndex: number, className: string): BindingRecord {
78-
return new BindingRecord(ELEMENT_CLASS, 0, ast, elementIndex, className, null, null, null, null,
79-
null);
86+
var t = new BindingTarget(ELEMENT_CLASS, elementIndex, className, null, ast.toString());
87+
return new BindingRecord(BINDING, t, 0, ast, null, null, null);
8088
}
8189

8290
static createForElementStyle(ast: AST, elementIndex: number, styleName: string,
8391
unit: string): BindingRecord {
84-
return new BindingRecord(ELEMENT_STYLE, 0, ast, elementIndex, styleName, unit, null, null, null,
85-
null);
92+
var t = new BindingTarget(ELEMENT_STYLE, elementIndex, styleName, unit, ast.toString());
93+
return new BindingRecord(BINDING, t, 0, ast, null, null, null);
8694
}
8795

96+
97+
8898
static createForHostProperty(directiveIndex: DirectiveIndex, ast: AST,
8999
propertyName: string): BindingRecord {
90-
return new BindingRecord(ELEMENT_PROPERTY, directiveIndex, ast, directiveIndex.elementIndex,
91-
propertyName, null, null, null, null, null);
100+
var t = new BindingTarget(ELEMENT_PROPERTY, directiveIndex.elementIndex, propertyName, null,
101+
ast.toString());
102+
return new BindingRecord(BINDING, t, directiveIndex, ast, null, null, null);
92103
}
93104

94105
static createForHostAttribute(directiveIndex: DirectiveIndex, ast: AST,
95106
attributeName: string): BindingRecord {
96-
return new BindingRecord(ELEMENT_ATTRIBUTE, directiveIndex, ast, directiveIndex.elementIndex,
97-
attributeName, null, null, null, null, null);
107+
var t = new BindingTarget(ELEMENT_ATTRIBUTE, directiveIndex.elementIndex, attributeName, null,
108+
ast.toString());
109+
return new BindingRecord(BINDING, t, directiveIndex, ast, null, null, null);
98110
}
99111

100112
static createForHostClass(directiveIndex: DirectiveIndex, ast: AST,
101113
className: string): BindingRecord {
102-
return new BindingRecord(ELEMENT_CLASS, directiveIndex, ast, directiveIndex.elementIndex,
103-
className, null, null, null, null, null);
114+
var t = new BindingTarget(ELEMENT_CLASS, directiveIndex.elementIndex, className, null,
115+
ast.toString());
116+
return new BindingRecord(BINDING, t, directiveIndex, ast, null, null, null);
104117
}
105118

106119
static createForHostStyle(directiveIndex: DirectiveIndex, ast: AST, styleName: string,
107120
unit: string): BindingRecord {
108-
return new BindingRecord(ELEMENT_STYLE, directiveIndex, ast, directiveIndex.elementIndex,
109-
styleName, unit, null, null, null, null);
121+
var t = new BindingTarget(ELEMENT_STYLE, directiveIndex.elementIndex, styleName, unit,
122+
ast.toString());
123+
return new BindingRecord(BINDING, t, directiveIndex, ast, null, null, null);
110124
}
111125

126+
127+
112128
static createForTextNode(ast: AST, elementIndex: number): BindingRecord {
113-
return new BindingRecord(TEXT_NODE, 0, ast, elementIndex, null, null, null, null, null, null);
129+
var t = new BindingTarget(TEXT_NODE, elementIndex, null, null, ast.toString());
130+
return new BindingRecord(BINDING, t, 0, ast, null, null, null);
114131
}
115132

133+
134+
116135
static createForEvent(ast: AST, eventName: string, elementIndex: number): BindingRecord {
117-
return new BindingRecord(EVENT, 0, ast, elementIndex, null, null, eventName, null, null, null);
136+
var t = new BindingTarget(EVENT, elementIndex, eventName, null, ast.toString());
137+
return new BindingRecord(EVENT, t, 0, ast, null, null, null);
118138
}
119139

120140
static createForHostEvent(ast: AST, eventName: string,
121141
directiveRecord: DirectiveRecord): BindingRecord {
122142
var directiveIndex = directiveRecord.directiveIndex;
123-
return new BindingRecord(EVENT, directiveIndex, ast, directiveIndex.elementIndex, null, null,
124-
eventName, null, null, directiveRecord);
143+
var t =
144+
new BindingTarget(HOST_EVENT, directiveIndex.elementIndex, eventName, null, ast.toString());
145+
return new BindingRecord(HOST_EVENT, t, directiveIndex, ast, null, null, directiveRecord);
125146
}
126147
}

modules/angular2/src/change_detection/change_detection.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export {
3838
} from './interfaces';
3939
export {CHECK_ONCE, CHECK_ALWAYS, DETACHED, CHECKED, ON_PUSH, DEFAULT} from './constants';
4040
export {DynamicProtoChangeDetector} from './proto_change_detector';
41-
export {BindingRecord} from './binding_record';
41+
export {BindingRecord, BindingTarget} from './binding_record';
4242
export {DirectiveIndex, DirectiveRecord} from './directive_record';
4343
export {DynamicChangeDetector} from './dynamic_change_detector';
4444
export {ChangeDetectorRef} from './change_detector_ref';
@@ -93,13 +93,14 @@ export class PreGeneratedChangeDetection extends ChangeDetection {
9393

9494
static isSupported(): boolean { return PregenProtoChangeDetector.isSupported(); }
9595

96-
createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector {
97-
var id = definition.id;
96+
getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector {
9897
if (StringMapWrapper.contains(this._protoChangeDetectorFactories, id)) {
9998
return StringMapWrapper.get(this._protoChangeDetectorFactories, id)(definition);
10099
}
101-
return this._dynamicChangeDetection.createProtoChangeDetector(definition);
100+
return this._dynamicChangeDetection.getProtoChangeDetector(id, definition);
102101
}
102+
103+
get generateDetectors(): boolean { return true; }
103104
}
104105

105106

@@ -110,9 +111,11 @@ export class PreGeneratedChangeDetection extends ChangeDetection {
110111
*/
111112
@Injectable()
112113
export class DynamicChangeDetection extends ChangeDetection {
113-
createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector {
114+
getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector {
114115
return new DynamicProtoChangeDetector(definition);
115116
}
117+
118+
get generateDetectors(): boolean { return true; }
116119
}
117120

118121
/**
@@ -126,7 +129,9 @@ export class DynamicChangeDetection extends ChangeDetection {
126129
export class JitChangeDetection extends ChangeDetection {
127130
static isSupported(): boolean { return JitProtoChangeDetector.isSupported(); }
128131

129-
createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector {
132+
getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector {
130133
return new JitProtoChangeDetector(definition);
131134
}
135+
136+
get generateDetectors(): boolean { return true; }
132137
}

0 commit comments

Comments
 (0)