Skip to content

Commit 8e3bf39

Browse files
author
Tim Blasi
committed
feat(dart/transform): Use the best available Change Detectors
Enable pregenerated (for Dart) and JIT (for Js) change detectors when possible. Previously we would always use `DynamicChangeDetector`s, but these cause megamorphic calls and are therefore much slower. Closes angular#502
1 parent 21dcfc8 commit 8e3bf39

24 files changed

+216
-97
lines changed

modules/angular2/change_detection.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,7 @@ export {
3838
ON_PUSH,
3939
DEFAULT
4040
} from './src/change_detection/constants';
41-
export {
42-
DynamicProtoChangeDetector,
43-
JitProtoChangeDetector
44-
} from './src/change_detection/proto_change_detector';
41+
export {DynamicProtoChangeDetector} from './src/change_detection/proto_change_detector';
4542
export {BindingRecord} from './src/change_detection/binding_record';
4643
export {DirectiveIndex, DirectiveRecord} from './src/change_detection/directive_record';
4744
export {DynamicChangeDetector} from './src/change_detection/dynamic_change_detector';

modules/angular2/src/change_detection/change_detection.ts

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import {DynamicProtoChangeDetector, JitProtoChangeDetector} from './proto_change_detector';
1+
import {JitProtoChangeDetector} from './jit_proto_change_detector';
2+
import {PregenProtoChangeDetector} from './pregen_proto_change_detector';
3+
import {DynamicProtoChangeDetector} from './proto_change_detector';
24
import {PipeFactory, Pipe} from './pipes/pipe';
35
import {PipeRegistry} from './pipes/pipe_registry';
46
import {IterableChangesFactory} from './pipes/iterable_changes';
@@ -10,9 +12,9 @@ import {LowerCaseFactory} from './pipes/lowercase_pipe';
1012
import {JsonPipe} from './pipes/json_pipe';
1113
import {NullPipeFactory} from './pipes/null_pipe';
1214
import {ChangeDetection, ProtoChangeDetector, ChangeDetectorDefinition} from './interfaces';
13-
import {Injectable} from 'angular2/src/di/decorators';
14-
import {List, StringMapWrapper} from 'angular2/src/facade/collection';
15-
import {isPresent, BaseException} from 'angular2/src/facade/lang';
15+
import {Inject, Injectable, OpaqueToken, Optional} from 'angular2/di';
16+
import {List, StringMap, StringMapWrapper} from 'angular2/src/facade/collection';
17+
import {CONST_EXPR, isPresent, BaseException} from 'angular2/src/facade/lang';
1618

1719
/**
1820
* Structural diffing for `Object`s and `Map`s.
@@ -66,29 +68,44 @@ export var defaultPipes = {
6668
"json": json
6769
};
6870

69-
export var preGeneratedProtoDetectors = {};
71+
/**
72+
* Map from {@link ChangeDetectorDefinition#id} to a factory method which takes a
73+
* {@link PipeRegistry} and a {@link ChangeDetectorDefinition} and generates a
74+
* {@link ProtoChangeDetector} associated with the definition.
75+
*/
76+
// TODO(kegluneq): Use PregenProtoChangeDetectorFactory rather than Function once possible in
77+
// dart2js. See https://github.com/dart-lang/sdk/issues/23630 for details.
78+
export var preGeneratedProtoDetectors: StringMap<string, Function> = {};
7079

80+
export const PROTO_CHANGE_DETECTOR_KEY = CONST_EXPR(new OpaqueToken('ProtoChangeDetectors'));
7181

7282
/**
7383
* Implements change detection using a map of pregenerated proto detectors.
7484
*
7585
* @exportedAs angular2/change_detection
7686
*/
87+
@Injectable()
7788
export class PreGeneratedChangeDetection extends ChangeDetection {
7889
_dynamicChangeDetection: ChangeDetection;
7990
_protoChangeDetectorFactories: StringMap<string, Function>;
8091

81-
constructor(private registry: PipeRegistry, protoChangeDetectors?) {
92+
constructor(private registry: PipeRegistry,
93+
@Inject(PROTO_CHANGE_DETECTOR_KEY) @Optional()
94+
protoChangeDetectorsForTest?: StringMap<string, Function>) {
8295
super();
8396
this._dynamicChangeDetection = new DynamicChangeDetection(registry);
84-
this._protoChangeDetectorFactories =
85-
isPresent(protoChangeDetectors) ? protoChangeDetectors : preGeneratedProtoDetectors;
97+
this._protoChangeDetectorFactories = isPresent(protoChangeDetectorsForTest) ?
98+
protoChangeDetectorsForTest :
99+
preGeneratedProtoDetectors;
86100
}
87101

102+
static isSupported(): boolean { return PregenProtoChangeDetector.isSupported(); }
103+
88104
createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector {
89105
var id = definition.id;
90106
if (StringMapWrapper.contains(this._protoChangeDetectorFactories, id)) {
91-
return StringMapWrapper.get(this._protoChangeDetectorFactories, id)(this.registry);
107+
return StringMapWrapper.get(this._protoChangeDetectorFactories, id)(this.registry,
108+
definition);
92109
}
93110
return this._dynamicChangeDetection.createProtoChangeDetector(definition);
94111
}
@@ -112,17 +129,19 @@ export class DynamicChangeDetection extends ChangeDetection {
112129
}
113130

114131
/**
115-
* Implements faster change detection, by generating source code.
132+
* Implements faster change detection by generating source code.
116133
*
117134
* This requires `eval()`. For change detection that does not require `eval()`, see
118-
* {@link DynamicChangeDetection}.
135+
* {@link DynamicChangeDetection} and {@link PreGeneratedChangeDetection}.
119136
*
120137
* @exportedAs angular2/change_detection
121138
*/
122139
@Injectable()
123140
export class JitChangeDetection extends ChangeDetection {
124141
constructor(public registry: PipeRegistry) { super(); }
125142

143+
static isSupported(): boolean { return JitProtoChangeDetector.isSupported(); }
144+
126145
createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector {
127146
return new JitProtoChangeDetector(this.registry, definition);
128147
}

modules/angular2/src/change_detection/change_detection_jit_generator.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
library change_detectoin.change_detection_jit_generator;
1+
library change_detection.change_detection_jit_generator;
22

33
/// Placeholder JIT generator for Dart.
44
/// Dart does not support `eval`, so JIT generation is not an option. Instead,
@@ -12,4 +12,6 @@ class ChangeDetectorJITGenerator {
1212
generate() {
1313
throw "Jit Change Detection is not supported in Dart";
1414
}
15+
16+
static bool isSupported() => false;
1517
}

modules/angular2/src/change_detection/interfaces.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export class ProtoChangeDetector {
2424
* `JitChangeDetection` strategy at compile time.
2525
*
2626
*
27-
* See: {@link DynamicChangeDetection}, {@link JitChangeDetection}
27+
* See: {@link DynamicChangeDetection}, {@link JitChangeDetection}, {@link PregenChangeDetection}
2828
*
2929
* # Example
3030
* ```javascript
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
library change_detection.jit_proto_change_detector;
2+
3+
import 'interfaces.dart' show ChangeDetector, ProtoChangeDetector;
4+
5+
class JitProtoChangeDetector implements ProtoChangeDetector {
6+
JitProtoChangeDetector(registry, definition) : super();
7+
8+
static bool isSupported() => false;
9+
10+
ChangeDetector instantiate(dispatcher) {
11+
throw "Jit Change Detection not supported in Dart";
12+
}
13+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import {ListWrapper} from 'angular2/src/facade/collection';
2+
3+
import {ProtoChangeDetector, ChangeDetector, ChangeDetectorDefinition} from './interfaces';
4+
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
5+
6+
import {coalesce} from './coalesce';
7+
import {ProtoRecordBuilder} from './proto_change_detector';
8+
9+
var _jitProtoChangeDetectorClassCounter: number = 0;
10+
export class JitProtoChangeDetector extends ProtoChangeDetector {
11+
_factory: Function;
12+
13+
constructor(private _pipeRegistry, private definition: ChangeDetectorDefinition) {
14+
super();
15+
this._factory = this._createFactory(definition);
16+
}
17+
18+
static isSupported(): boolean { return true; }
19+
20+
instantiate(dispatcher: any): ChangeDetector {
21+
return this._factory(dispatcher, this._pipeRegistry);
22+
}
23+
24+
_createFactory(definition: ChangeDetectorDefinition) {
25+
var recordBuilder = new ProtoRecordBuilder();
26+
ListWrapper.forEach(definition.bindingRecords,
27+
(b) => { recordBuilder.add(b, definition.variableNames); });
28+
var c = _jitProtoChangeDetectorClassCounter++;
29+
var records = coalesce(recordBuilder.records);
30+
var typeName = `ChangeDetector${c}`;
31+
return new ChangeDetectorJITGenerator(typeName, definition.strategy, records,
32+
this.definition.directiveRecords)
33+
.generate();
34+
}
35+
}

modules/angular2/src/change_detection/pregen_proto_change_detector.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import 'package:angular2/src/change_detection/proto_record.dart';
1010
export 'dart:core' show List;
1111
export 'package:angular2/src/change_detection/abstract_change_detector.dart'
1212
show AbstractChangeDetector;
13+
export 'package:angular2/src/change_detection/change_detection.dart'
14+
show preGeneratedProtoDetectors;
1315
export 'package:angular2/src/change_detection/directive_record.dart'
1416
show DirectiveIndex, DirectiveRecord;
1517
export 'package:angular2/src/change_detection/interfaces.dart'
@@ -22,6 +24,9 @@ export 'package:angular2/src/change_detection/change_detection_util.dart'
2224
show ChangeDetectionUtil;
2325
export 'package:angular2/src/facade/lang.dart' show looseIdentical;
2426

27+
typedef ProtoChangeDetector PregenProtoChangeDetectorFactory(
28+
PipeRegistry registry, ChangeDetectorDefinition definition);
29+
2530
typedef ChangeDetector InstantiateMethod(dynamic dispatcher,
2631
PipeRegistry registry, List<ProtoRecord> protoRecords,
2732
List<DirectiveRecord> directiveRecords);
@@ -47,6 +52,8 @@ class PregenProtoChangeDetector extends ProtoChangeDetector {
4752
PregenProtoChangeDetector._(this.id, this._instantiateMethod,
4853
this._pipeRegistry, this._protoRecords, this._directiveRecords);
4954

55+
static bool isSupported() => true;
56+
5057
factory PregenProtoChangeDetector(InstantiateMethod instantiateMethod,
5158
PipeRegistry registry, ChangeDetectorDefinition def) {
5259
// TODO(kegluneq): Pre-generate these (#2067).
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import {BaseException} from 'angular2/src/facade/lang';
2+
3+
import {ProtoChangeDetector, ChangeDetector} from './interfaces';
4+
import {coalesce} from './coalesce';
5+
6+
export {Function as PregenProtoChangeDetectorFactory};
7+
8+
export class PregenProtoChangeDetector extends ProtoChangeDetector {
9+
constructor() { super(); }
10+
11+
static isSupported(): boolean { return false; }
12+
13+
instantiate(dispatcher: any): ChangeDetector {
14+
throw new BaseException('Pregen change detection not supported in Js');
15+
}
16+
}

modules/angular2/src/change_detection/proto_change_detector.ts

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ import {
3232
} from './interfaces';
3333
import {ChangeDetectionUtil} from './change_detection_util';
3434
import {DynamicChangeDetector} from './dynamic_change_detector';
35-
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
3635
import {PipeRegistry} from './pipes/pipe_registry';
3736
import {BindingRecord} from './binding_record';
3837
import {DirectiveRecord, DirectiveIndex} from './directive_record';
@@ -62,31 +61,7 @@ export class DynamicProtoChangeDetector extends ProtoChangeDetector {
6261
}
6362
}
6463

65-
var _jitProtoChangeDetectorClassCounter: number = 0;
66-
export class JitProtoChangeDetector extends ProtoChangeDetector {
67-
_factory: Function;
68-
69-
constructor(private _pipeRegistry, private definition: ChangeDetectorDefinition) {
70-
super();
71-
this._factory = this._createFactory(definition);
72-
}
73-
74-
instantiate(dispatcher: any) { return this._factory(dispatcher, this._pipeRegistry); }
75-
76-
_createFactory(definition: ChangeDetectorDefinition) {
77-
var recordBuilder = new ProtoRecordBuilder();
78-
ListWrapper.forEach(definition.bindingRecords,
79-
(b) => { recordBuilder.add(b, definition.variableNames); });
80-
var c = _jitProtoChangeDetectorClassCounter++;
81-
var records = coalesce(recordBuilder.records);
82-
var typeName = `ChangeDetector${c}`;
83-
return new ChangeDetectorJITGenerator(typeName, definition.strategy, records,
84-
this.definition.directiveRecords)
85-
.generate();
86-
}
87-
}
88-
89-
class ProtoRecordBuilder {
64+
export class ProtoRecordBuilder {
9065
records: List<ProtoRecord>;
9166

9267
constructor() { this.records = []; }

modules/angular2/src/core/application.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import {
1818
Lexer,
1919
ChangeDetection,
2020
DynamicChangeDetection,
21+
JitChangeDetection,
22+
PreGeneratedChangeDetection,
2123
PipeRegistry,
2224
defaultPipeRegistry
2325
} from 'angular2/change_detection';
@@ -66,6 +68,12 @@ var _rootInjector: Injector;
6668
var _rootBindings = [bind(Reflector).toValue(reflector), TestabilityRegistry];
6769

6870
function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
71+
var bestChangeDetection: Type = DynamicChangeDetection;
72+
if (PreGeneratedChangeDetection.isSupported()) {
73+
bestChangeDetection = PreGeneratedChangeDetection;
74+
} else if (JitChangeDetection.isSupported()) {
75+
bestChangeDetection = JitChangeDetection;
76+
}
6977
return [
7078
bind(DOCUMENT_TOKEN)
7179
.toValue(DOM.defaultDoc()),
@@ -117,7 +125,7 @@ function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
117125
CompilerCache,
118126
TemplateResolver,
119127
bind(PipeRegistry).toValue(defaultPipeRegistry),
120-
bind(ChangeDetection).toClass(DynamicChangeDetection),
128+
bind(ChangeDetection).toClass(bestChangeDetection),
121129
TemplateLoader,
122130
DirectiveResolver,
123131
Parser,

0 commit comments

Comments
 (0)