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 #502
This commit is contained in:
@ -1,4 +1,6 @@
|
||||
import {DynamicProtoChangeDetector, JitProtoChangeDetector} from './proto_change_detector';
|
||||
import {JitProtoChangeDetector} from './jit_proto_change_detector';
|
||||
import {PregenProtoChangeDetector} from './pregen_proto_change_detector';
|
||||
import {DynamicProtoChangeDetector} from './proto_change_detector';
|
||||
import {PipeFactory, Pipe} from './pipes/pipe';
|
||||
import {PipeRegistry} from './pipes/pipe_registry';
|
||||
import {IterableChangesFactory} from './pipes/iterable_changes';
|
||||
@ -10,9 +12,9 @@ import {LowerCaseFactory} from './pipes/lowercase_pipe';
|
||||
import {JsonPipe} from './pipes/json_pipe';
|
||||
import {NullPipeFactory} from './pipes/null_pipe';
|
||||
import {ChangeDetection, ProtoChangeDetector, ChangeDetectorDefinition} from './interfaces';
|
||||
import {Injectable} from 'angular2/src/di/decorators';
|
||||
import {List, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {isPresent, BaseException} from 'angular2/src/facade/lang';
|
||||
import {Inject, Injectable, OpaqueToken, Optional} from 'angular2/di';
|
||||
import {List, StringMap, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {CONST_EXPR, isPresent, BaseException} from 'angular2/src/facade/lang';
|
||||
|
||||
/**
|
||||
* Structural diffing for `Object`s and `Map`s.
|
||||
@ -66,29 +68,44 @@ export var defaultPipes = {
|
||||
"json": json
|
||||
};
|
||||
|
||||
export var preGeneratedProtoDetectors = {};
|
||||
/**
|
||||
* Map from {@link ChangeDetectorDefinition#id} to a factory method which takes a
|
||||
* {@link PipeRegistry} and a {@link ChangeDetectorDefinition} and generates a
|
||||
* {@link ProtoChangeDetector} associated with the definition.
|
||||
*/
|
||||
// TODO(kegluneq): Use PregenProtoChangeDetectorFactory rather than Function once possible in
|
||||
// dart2js. See https://github.com/dart-lang/sdk/issues/23630 for details.
|
||||
export var preGeneratedProtoDetectors: StringMap<string, Function> = {};
|
||||
|
||||
export const PROTO_CHANGE_DETECTOR_KEY = CONST_EXPR(new OpaqueToken('ProtoChangeDetectors'));
|
||||
|
||||
/**
|
||||
* Implements change detection using a map of pregenerated proto detectors.
|
||||
*
|
||||
* @exportedAs angular2/change_detection
|
||||
*/
|
||||
@Injectable()
|
||||
export class PreGeneratedChangeDetection extends ChangeDetection {
|
||||
_dynamicChangeDetection: ChangeDetection;
|
||||
_protoChangeDetectorFactories: StringMap<string, Function>;
|
||||
|
||||
constructor(private registry: PipeRegistry, protoChangeDetectors?) {
|
||||
constructor(private registry: PipeRegistry,
|
||||
@Inject(PROTO_CHANGE_DETECTOR_KEY) @Optional()
|
||||
protoChangeDetectorsForTest?: StringMap<string, Function>) {
|
||||
super();
|
||||
this._dynamicChangeDetection = new DynamicChangeDetection(registry);
|
||||
this._protoChangeDetectorFactories =
|
||||
isPresent(protoChangeDetectors) ? protoChangeDetectors : preGeneratedProtoDetectors;
|
||||
this._protoChangeDetectorFactories = isPresent(protoChangeDetectorsForTest) ?
|
||||
protoChangeDetectorsForTest :
|
||||
preGeneratedProtoDetectors;
|
||||
}
|
||||
|
||||
static isSupported(): boolean { return PregenProtoChangeDetector.isSupported(); }
|
||||
|
||||
createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector {
|
||||
var id = definition.id;
|
||||
if (StringMapWrapper.contains(this._protoChangeDetectorFactories, id)) {
|
||||
return StringMapWrapper.get(this._protoChangeDetectorFactories, id)(this.registry);
|
||||
return StringMapWrapper.get(this._protoChangeDetectorFactories, id)(this.registry,
|
||||
definition);
|
||||
}
|
||||
return this._dynamicChangeDetection.createProtoChangeDetector(definition);
|
||||
}
|
||||
@ -112,10 +129,10 @@ export class DynamicChangeDetection extends ChangeDetection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements faster change detection, by generating source code.
|
||||
* Implements faster change detection by generating source code.
|
||||
*
|
||||
* This requires `eval()`. For change detection that does not require `eval()`, see
|
||||
* {@link DynamicChangeDetection}.
|
||||
* {@link DynamicChangeDetection} and {@link PreGeneratedChangeDetection}.
|
||||
*
|
||||
* @exportedAs angular2/change_detection
|
||||
*/
|
||||
@ -123,6 +140,8 @@ export class DynamicChangeDetection extends ChangeDetection {
|
||||
export class JitChangeDetection extends ChangeDetection {
|
||||
constructor(public registry: PipeRegistry) { super(); }
|
||||
|
||||
static isSupported(): boolean { return JitProtoChangeDetector.isSupported(); }
|
||||
|
||||
createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector {
|
||||
return new JitProtoChangeDetector(this.registry, definition);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
library change_detectoin.change_detection_jit_generator;
|
||||
library change_detection.change_detection_jit_generator;
|
||||
|
||||
/// Placeholder JIT generator for Dart.
|
||||
/// Dart does not support `eval`, so JIT generation is not an option. Instead,
|
||||
@ -12,4 +12,6 @@ class ChangeDetectorJITGenerator {
|
||||
generate() {
|
||||
throw "Jit Change Detection is not supported in Dart";
|
||||
}
|
||||
|
||||
static bool isSupported() => false;
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ export class ProtoChangeDetector {
|
||||
* `JitChangeDetection` strategy at compile time.
|
||||
*
|
||||
*
|
||||
* See: {@link DynamicChangeDetection}, {@link JitChangeDetection}
|
||||
* See: {@link DynamicChangeDetection}, {@link JitChangeDetection}, {@link PregenChangeDetection}
|
||||
*
|
||||
* # Example
|
||||
* ```javascript
|
||||
|
@ -0,0 +1,13 @@
|
||||
library change_detection.jit_proto_change_detector;
|
||||
|
||||
import 'interfaces.dart' show ChangeDetector, ProtoChangeDetector;
|
||||
|
||||
class JitProtoChangeDetector implements ProtoChangeDetector {
|
||||
JitProtoChangeDetector(registry, definition) : super();
|
||||
|
||||
static bool isSupported() => false;
|
||||
|
||||
ChangeDetector instantiate(dispatcher) {
|
||||
throw "Jit Change Detection not supported in Dart";
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {ProtoChangeDetector, ChangeDetector, ChangeDetectorDefinition} from './interfaces';
|
||||
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
|
||||
|
||||
import {coalesce} from './coalesce';
|
||||
import {ProtoRecordBuilder} from './proto_change_detector';
|
||||
|
||||
var _jitProtoChangeDetectorClassCounter: number = 0;
|
||||
export class JitProtoChangeDetector extends ProtoChangeDetector {
|
||||
_factory: Function;
|
||||
|
||||
constructor(private _pipeRegistry, private definition: ChangeDetectorDefinition) {
|
||||
super();
|
||||
this._factory = this._createFactory(definition);
|
||||
}
|
||||
|
||||
static isSupported(): boolean { return true; }
|
||||
|
||||
instantiate(dispatcher: any): ChangeDetector {
|
||||
return this._factory(dispatcher, this._pipeRegistry);
|
||||
}
|
||||
|
||||
_createFactory(definition: ChangeDetectorDefinition) {
|
||||
var recordBuilder = new ProtoRecordBuilder();
|
||||
ListWrapper.forEach(definition.bindingRecords,
|
||||
(b) => { recordBuilder.add(b, definition.variableNames); });
|
||||
var c = _jitProtoChangeDetectorClassCounter++;
|
||||
var records = coalesce(recordBuilder.records);
|
||||
var typeName = `ChangeDetector${c}`;
|
||||
return new ChangeDetectorJITGenerator(typeName, definition.strategy, records,
|
||||
this.definition.directiveRecords)
|
||||
.generate();
|
||||
}
|
||||
}
|
@ -10,6 +10,8 @@ import 'package:angular2/src/change_detection/proto_record.dart';
|
||||
export 'dart:core' show List;
|
||||
export 'package:angular2/src/change_detection/abstract_change_detector.dart'
|
||||
show AbstractChangeDetector;
|
||||
export 'package:angular2/src/change_detection/change_detection.dart'
|
||||
show preGeneratedProtoDetectors;
|
||||
export 'package:angular2/src/change_detection/directive_record.dart'
|
||||
show DirectiveIndex, DirectiveRecord;
|
||||
export 'package:angular2/src/change_detection/interfaces.dart'
|
||||
@ -22,6 +24,9 @@ export 'package:angular2/src/change_detection/change_detection_util.dart'
|
||||
show ChangeDetectionUtil;
|
||||
export 'package:angular2/src/facade/lang.dart' show looseIdentical;
|
||||
|
||||
typedef ProtoChangeDetector PregenProtoChangeDetectorFactory(
|
||||
PipeRegistry registry, ChangeDetectorDefinition definition);
|
||||
|
||||
typedef ChangeDetector InstantiateMethod(dynamic dispatcher,
|
||||
PipeRegistry registry, List<ProtoRecord> protoRecords,
|
||||
List<DirectiveRecord> directiveRecords);
|
||||
@ -47,6 +52,8 @@ class PregenProtoChangeDetector extends ProtoChangeDetector {
|
||||
PregenProtoChangeDetector._(this.id, this._instantiateMethod,
|
||||
this._pipeRegistry, this._protoRecords, this._directiveRecords);
|
||||
|
||||
static bool isSupported() => true;
|
||||
|
||||
factory PregenProtoChangeDetector(InstantiateMethod instantiateMethod,
|
||||
PipeRegistry registry, ChangeDetectorDefinition def) {
|
||||
// TODO(kegluneq): Pre-generate these (#2067).
|
||||
|
@ -0,0 +1,16 @@
|
||||
import {BaseException} from 'angular2/src/facade/lang';
|
||||
|
||||
import {ProtoChangeDetector, ChangeDetector} from './interfaces';
|
||||
import {coalesce} from './coalesce';
|
||||
|
||||
export {Function as PregenProtoChangeDetectorFactory};
|
||||
|
||||
export class PregenProtoChangeDetector extends ProtoChangeDetector {
|
||||
constructor() { super(); }
|
||||
|
||||
static isSupported(): boolean { return false; }
|
||||
|
||||
instantiate(dispatcher: any): ChangeDetector {
|
||||
throw new BaseException('Pregen change detection not supported in Js');
|
||||
}
|
||||
}
|
@ -32,7 +32,6 @@ import {
|
||||
} from './interfaces';
|
||||
import {ChangeDetectionUtil} from './change_detection_util';
|
||||
import {DynamicChangeDetector} from './dynamic_change_detector';
|
||||
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
|
||||
import {PipeRegistry} from './pipes/pipe_registry';
|
||||
import {BindingRecord} from './binding_record';
|
||||
import {DirectiveRecord, DirectiveIndex} from './directive_record';
|
||||
@ -62,31 +61,7 @@ export class DynamicProtoChangeDetector extends ProtoChangeDetector {
|
||||
}
|
||||
}
|
||||
|
||||
var _jitProtoChangeDetectorClassCounter: number = 0;
|
||||
export class JitProtoChangeDetector extends ProtoChangeDetector {
|
||||
_factory: Function;
|
||||
|
||||
constructor(private _pipeRegistry, private definition: ChangeDetectorDefinition) {
|
||||
super();
|
||||
this._factory = this._createFactory(definition);
|
||||
}
|
||||
|
||||
instantiate(dispatcher: any) { return this._factory(dispatcher, this._pipeRegistry); }
|
||||
|
||||
_createFactory(definition: ChangeDetectorDefinition) {
|
||||
var recordBuilder = new ProtoRecordBuilder();
|
||||
ListWrapper.forEach(definition.bindingRecords,
|
||||
(b) => { recordBuilder.add(b, definition.variableNames); });
|
||||
var c = _jitProtoChangeDetectorClassCounter++;
|
||||
var records = coalesce(recordBuilder.records);
|
||||
var typeName = `ChangeDetector${c}`;
|
||||
return new ChangeDetectorJITGenerator(typeName, definition.strategy, records,
|
||||
this.definition.directiveRecords)
|
||||
.generate();
|
||||
}
|
||||
}
|
||||
|
||||
class ProtoRecordBuilder {
|
||||
export class ProtoRecordBuilder {
|
||||
records: List<ProtoRecord>;
|
||||
|
||||
constructor() { this.records = []; }
|
||||
|
Reference in New Issue
Block a user