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:
Tim Blasi 2015-05-29 16:34:51 -07:00
parent 21dcfc89e9
commit 8e3bf3907a
24 changed files with 215 additions and 96 deletions

View File

@ -38,10 +38,7 @@ export {
ON_PUSH, ON_PUSH,
DEFAULT DEFAULT
} from './src/change_detection/constants'; } from './src/change_detection/constants';
export { export {DynamicProtoChangeDetector} from './src/change_detection/proto_change_detector';
DynamicProtoChangeDetector,
JitProtoChangeDetector
} from './src/change_detection/proto_change_detector';
export {BindingRecord} from './src/change_detection/binding_record'; export {BindingRecord} from './src/change_detection/binding_record';
export {DirectiveIndex, DirectiveRecord} from './src/change_detection/directive_record'; export {DirectiveIndex, DirectiveRecord} from './src/change_detection/directive_record';
export {DynamicChangeDetector} from './src/change_detection/dynamic_change_detector'; export {DynamicChangeDetector} from './src/change_detection/dynamic_change_detector';

View File

@ -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 {PipeFactory, Pipe} from './pipes/pipe';
import {PipeRegistry} from './pipes/pipe_registry'; import {PipeRegistry} from './pipes/pipe_registry';
import {IterableChangesFactory} from './pipes/iterable_changes'; import {IterableChangesFactory} from './pipes/iterable_changes';
@ -10,9 +12,9 @@ import {LowerCaseFactory} from './pipes/lowercase_pipe';
import {JsonPipe} from './pipes/json_pipe'; import {JsonPipe} from './pipes/json_pipe';
import {NullPipeFactory} from './pipes/null_pipe'; import {NullPipeFactory} from './pipes/null_pipe';
import {ChangeDetection, ProtoChangeDetector, ChangeDetectorDefinition} from './interfaces'; import {ChangeDetection, ProtoChangeDetector, ChangeDetectorDefinition} from './interfaces';
import {Injectable} from 'angular2/src/di/decorators'; import {Inject, Injectable, OpaqueToken, Optional} from 'angular2/di';
import {List, StringMapWrapper} from 'angular2/src/facade/collection'; import {List, StringMap, StringMapWrapper} from 'angular2/src/facade/collection';
import {isPresent, BaseException} from 'angular2/src/facade/lang'; import {CONST_EXPR, isPresent, BaseException} from 'angular2/src/facade/lang';
/** /**
* Structural diffing for `Object`s and `Map`s. * Structural diffing for `Object`s and `Map`s.
@ -66,29 +68,44 @@ export var defaultPipes = {
"json": json "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. * Implements change detection using a map of pregenerated proto detectors.
* *
* @exportedAs angular2/change_detection * @exportedAs angular2/change_detection
*/ */
@Injectable()
export class PreGeneratedChangeDetection extends ChangeDetection { export class PreGeneratedChangeDetection extends ChangeDetection {
_dynamicChangeDetection: ChangeDetection; _dynamicChangeDetection: ChangeDetection;
_protoChangeDetectorFactories: StringMap<string, Function>; _protoChangeDetectorFactories: StringMap<string, Function>;
constructor(private registry: PipeRegistry, protoChangeDetectors?) { constructor(private registry: PipeRegistry,
@Inject(PROTO_CHANGE_DETECTOR_KEY) @Optional()
protoChangeDetectorsForTest?: StringMap<string, Function>) {
super(); super();
this._dynamicChangeDetection = new DynamicChangeDetection(registry); this._dynamicChangeDetection = new DynamicChangeDetection(registry);
this._protoChangeDetectorFactories = this._protoChangeDetectorFactories = isPresent(protoChangeDetectorsForTest) ?
isPresent(protoChangeDetectors) ? protoChangeDetectors : preGeneratedProtoDetectors; protoChangeDetectorsForTest :
preGeneratedProtoDetectors;
} }
static isSupported(): boolean { return PregenProtoChangeDetector.isSupported(); }
createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector { createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector {
var id = definition.id; var id = definition.id;
if (StringMapWrapper.contains(this._protoChangeDetectorFactories, 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); 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 * This requires `eval()`. For change detection that does not require `eval()`, see
* {@link DynamicChangeDetection}. * {@link DynamicChangeDetection} and {@link PreGeneratedChangeDetection}.
* *
* @exportedAs angular2/change_detection * @exportedAs angular2/change_detection
*/ */
@ -123,6 +140,8 @@ export class DynamicChangeDetection extends ChangeDetection {
export class JitChangeDetection extends ChangeDetection { export class JitChangeDetection extends ChangeDetection {
constructor(public registry: PipeRegistry) { super(); } constructor(public registry: PipeRegistry) { super(); }
static isSupported(): boolean { return JitProtoChangeDetector.isSupported(); }
createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector { createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector {
return new JitProtoChangeDetector(this.registry, definition); return new JitProtoChangeDetector(this.registry, definition);
} }

View File

@ -1,4 +1,4 @@
library change_detectoin.change_detection_jit_generator; library change_detection.change_detection_jit_generator;
/// Placeholder JIT generator for Dart. /// Placeholder JIT generator for Dart.
/// Dart does not support `eval`, so JIT generation is not an option. Instead, /// Dart does not support `eval`, so JIT generation is not an option. Instead,
@ -12,4 +12,6 @@ class ChangeDetectorJITGenerator {
generate() { generate() {
throw "Jit Change Detection is not supported in Dart"; throw "Jit Change Detection is not supported in Dart";
} }
static bool isSupported() => false;
} }

View File

@ -24,7 +24,7 @@ export class ProtoChangeDetector {
* `JitChangeDetection` strategy at compile time. * `JitChangeDetection` strategy at compile time.
* *
* *
* See: {@link DynamicChangeDetection}, {@link JitChangeDetection} * See: {@link DynamicChangeDetection}, {@link JitChangeDetection}, {@link PregenChangeDetection}
* *
* # Example * # Example
* ```javascript * ```javascript

View File

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

View File

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

View File

@ -10,6 +10,8 @@ import 'package:angular2/src/change_detection/proto_record.dart';
export 'dart:core' show List; export 'dart:core' show List;
export 'package:angular2/src/change_detection/abstract_change_detector.dart' export 'package:angular2/src/change_detection/abstract_change_detector.dart'
show AbstractChangeDetector; show AbstractChangeDetector;
export 'package:angular2/src/change_detection/change_detection.dart'
show preGeneratedProtoDetectors;
export 'package:angular2/src/change_detection/directive_record.dart' export 'package:angular2/src/change_detection/directive_record.dart'
show DirectiveIndex, DirectiveRecord; show DirectiveIndex, DirectiveRecord;
export 'package:angular2/src/change_detection/interfaces.dart' export 'package:angular2/src/change_detection/interfaces.dart'
@ -22,6 +24,9 @@ export 'package:angular2/src/change_detection/change_detection_util.dart'
show ChangeDetectionUtil; show ChangeDetectionUtil;
export 'package:angular2/src/facade/lang.dart' show looseIdentical; export 'package:angular2/src/facade/lang.dart' show looseIdentical;
typedef ProtoChangeDetector PregenProtoChangeDetectorFactory(
PipeRegistry registry, ChangeDetectorDefinition definition);
typedef ChangeDetector InstantiateMethod(dynamic dispatcher, typedef ChangeDetector InstantiateMethod(dynamic dispatcher,
PipeRegistry registry, List<ProtoRecord> protoRecords, PipeRegistry registry, List<ProtoRecord> protoRecords,
List<DirectiveRecord> directiveRecords); List<DirectiveRecord> directiveRecords);
@ -47,6 +52,8 @@ class PregenProtoChangeDetector extends ProtoChangeDetector {
PregenProtoChangeDetector._(this.id, this._instantiateMethod, PregenProtoChangeDetector._(this.id, this._instantiateMethod,
this._pipeRegistry, this._protoRecords, this._directiveRecords); this._pipeRegistry, this._protoRecords, this._directiveRecords);
static bool isSupported() => true;
factory PregenProtoChangeDetector(InstantiateMethod instantiateMethod, factory PregenProtoChangeDetector(InstantiateMethod instantiateMethod,
PipeRegistry registry, ChangeDetectorDefinition def) { PipeRegistry registry, ChangeDetectorDefinition def) {
// TODO(kegluneq): Pre-generate these (#2067). // TODO(kegluneq): Pre-generate these (#2067).

View File

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

View File

@ -32,7 +32,6 @@ import {
} from './interfaces'; } from './interfaces';
import {ChangeDetectionUtil} from './change_detection_util'; import {ChangeDetectionUtil} from './change_detection_util';
import {DynamicChangeDetector} from './dynamic_change_detector'; import {DynamicChangeDetector} from './dynamic_change_detector';
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
import {PipeRegistry} from './pipes/pipe_registry'; import {PipeRegistry} from './pipes/pipe_registry';
import {BindingRecord} from './binding_record'; import {BindingRecord} from './binding_record';
import {DirectiveRecord, DirectiveIndex} from './directive_record'; import {DirectiveRecord, DirectiveIndex} from './directive_record';
@ -62,31 +61,7 @@ export class DynamicProtoChangeDetector extends ProtoChangeDetector {
} }
} }
var _jitProtoChangeDetectorClassCounter: number = 0; export class ProtoRecordBuilder {
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 {
records: List<ProtoRecord>; records: List<ProtoRecord>;
constructor() { this.records = []; } constructor() { this.records = []; }

View File

@ -18,6 +18,8 @@ import {
Lexer, Lexer,
ChangeDetection, ChangeDetection,
DynamicChangeDetection, DynamicChangeDetection,
JitChangeDetection,
PreGeneratedChangeDetection,
PipeRegistry, PipeRegistry,
defaultPipeRegistry defaultPipeRegistry
} from 'angular2/change_detection'; } from 'angular2/change_detection';
@ -66,6 +68,12 @@ var _rootInjector: Injector;
var _rootBindings = [bind(Reflector).toValue(reflector), TestabilityRegistry]; var _rootBindings = [bind(Reflector).toValue(reflector), TestabilityRegistry];
function _injectorBindings(appComponentType): List<Type | Binding | List<any>> { function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
var bestChangeDetection: Type = DynamicChangeDetection;
if (PreGeneratedChangeDetection.isSupported()) {
bestChangeDetection = PreGeneratedChangeDetection;
} else if (JitChangeDetection.isSupported()) {
bestChangeDetection = JitChangeDetection;
}
return [ return [
bind(DOCUMENT_TOKEN) bind(DOCUMENT_TOKEN)
.toValue(DOM.defaultDoc()), .toValue(DOM.defaultDoc()),
@ -117,7 +125,7 @@ function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
CompilerCache, CompilerCache,
TemplateResolver, TemplateResolver,
bind(PipeRegistry).toValue(defaultPipeRegistry), bind(PipeRegistry).toValue(defaultPipeRegistry),
bind(ChangeDetection).toClass(DynamicChangeDetection), bind(ChangeDetection).toClass(bestChangeDetection),
TemplateLoader, TemplateLoader,
DirectiveResolver, DirectiveResolver,
Parser, Parser,

View File

@ -88,20 +88,20 @@ class _DirectiveMetadataVisitor extends Object
} }
DirectiveMetadata get meta => DirectiveMetadata.create( DirectiveMetadata get meta => DirectiveMetadata.create(
type: _type, type: _type,
selector: _selector, selector: _selector,
compileChildren: _compileChildren, compileChildren: _compileChildren,
properties: _properties, properties: _properties,
host: _host, host: _host,
readAttributes: _readAttributes, readAttributes: _readAttributes,
exportAs: _exportAs, exportAs: _exportAs,
callOnDestroy: _callOnDestroy, callOnDestroy: _callOnDestroy,
callOnChange: _callOnChange, callOnChange: _callOnChange,
callOnCheck: _callOnCheck, callOnCheck: _callOnCheck,
callOnInit: _callOnInit, callOnInit: _callOnInit,
callOnAllChangesDone: _callOnAllChangesDone, callOnAllChangesDone: _callOnAllChangesDone,
changeDetection: _changeDetection, changeDetection: _changeDetection,
events: _events); events: _events);
@override @override
Object visitAnnotation(Annotation node) { Object visitAnnotation(Annotation node) {

View File

@ -8,6 +8,9 @@ const DEFAULT_OPTIMIZATION_PHASES = 5;
const CUSTOM_ANNOTATIONS_PARAM = 'custom_annotations'; const CUSTOM_ANNOTATIONS_PARAM = 'custom_annotations';
const ENTRY_POINT_PARAM = 'entry_points'; const ENTRY_POINT_PARAM = 'entry_points';
const GENERATE_CHANGE_DETECTORS_PARAM = 'generate_change_detectors';
const INIT_REFLECTOR_PARAM = 'init_reflector';
const MIRROR_MODE_PARAM = 'mirror_mode';
const OPTIMIZATION_PHASES_PARAM = 'optimization_phases'; const OPTIMIZATION_PHASES_PARAM = 'optimization_phases';
const REFLECTION_ENTRY_POINT_PARAM = 'reflection_entry_points'; const REFLECTION_ENTRY_POINT_PARAM = 'reflection_entry_points';
@ -32,6 +35,9 @@ class TransformerOptions {
/// The [AnnotationMatcher] which is used to identify angular annotations. /// The [AnnotationMatcher] which is used to identify angular annotations.
final AnnotationMatcher annotationMatcher; final AnnotationMatcher annotationMatcher;
/// Whether to create change detector classes for discovered `@View`s.
final bool generateChangeDetectors;
/// The number of phases to spend optimizing output size. /// The number of phases to spend optimizing output size.
/// Each additional phase adds time to the transformation but may decrease /// Each additional phase adds time to the transformation but may decrease
/// final output size. There is a limit beyond which this will no longer /// final output size. There is a limit beyond which this will no longer
@ -43,13 +49,15 @@ class TransformerOptions {
TransformerOptions._internal(this.entryPoints, this.reflectionEntryPoints, TransformerOptions._internal(this.entryPoints, this.reflectionEntryPoints,
this.modeName, this.mirrorMode, this.initReflector, this.modeName, this.mirrorMode, this.initReflector,
this.annotationMatcher, this.optimizationPhases); this.annotationMatcher, this.optimizationPhases,
this.generateChangeDetectors);
factory TransformerOptions(List<String> entryPoints, factory TransformerOptions(List<String> entryPoints,
{List<String> reflectionEntryPoints, String modeName: 'release', {List<String> reflectionEntryPoints, String modeName: 'release',
MirrorMode mirrorMode: MirrorMode.none, bool initReflector: true, MirrorMode mirrorMode: MirrorMode.none, bool initReflector: true,
List<AnnotationDescriptor> customAnnotationDescriptors: const [], List<AnnotationDescriptor> customAnnotationDescriptors: const [],
int optimizationPhases: DEFAULT_OPTIMIZATION_PHASES}) { int optimizationPhases: DEFAULT_OPTIMIZATION_PHASES,
bool generateChangeDetectors: true}) {
if (reflectionEntryPoints == null || reflectionEntryPoints.isEmpty) { if (reflectionEntryPoints == null || reflectionEntryPoints.isEmpty) {
reflectionEntryPoints = entryPoints; reflectionEntryPoints = entryPoints;
} }
@ -58,6 +66,6 @@ class TransformerOptions {
optimizationPhases = optimizationPhases.isNegative ? 0 : optimizationPhases; optimizationPhases = optimizationPhases.isNegative ? 0 : optimizationPhases;
return new TransformerOptions._internal(entryPoints, reflectionEntryPoints, return new TransformerOptions._internal(entryPoints, reflectionEntryPoints,
modeName, mirrorMode, initReflector, annotationMatcher, modeName, mirrorMode, initReflector, annotationMatcher,
optimizationPhases); optimizationPhases, generateChangeDetectors);
} }
} }

View File

@ -10,10 +10,12 @@ TransformerOptions parseBarbackSettings(BarbackSettings settings) {
var entryPoints = _readFileList(config, ENTRY_POINT_PARAM); var entryPoints = _readFileList(config, ENTRY_POINT_PARAM);
var reflectionEntryPoints = var reflectionEntryPoints =
_readFileList(config, REFLECTION_ENTRY_POINT_PARAM); _readFileList(config, REFLECTION_ENTRY_POINT_PARAM);
var initReflector = !config.containsKey('init_reflector') || var initReflector =
config['init_reflector'] != false; _readBool(config, INIT_REFLECTOR_PARAM, defaultValue: true);
var generateChangeDetectors =
_readBool(config, GENERATE_CHANGE_DETECTORS_PARAM, defaultValue: true);
String mirrorModeVal = String mirrorModeVal =
config.containsKey('mirror_mode') ? config['mirror_mode'] : ''; config.containsKey(MIRROR_MODE_PARAM) ? config[MIRROR_MODE_PARAM] : '';
var mirrorMode = MirrorMode.none; var mirrorMode = MirrorMode.none;
switch (mirrorModeVal) { switch (mirrorModeVal) {
case 'debug': case 'debug':
@ -34,7 +36,14 @@ TransformerOptions parseBarbackSettings(BarbackSettings settings) {
mirrorMode: mirrorMode, mirrorMode: mirrorMode,
initReflector: initReflector, initReflector: initReflector,
customAnnotationDescriptors: _readCustomAnnotations(config), customAnnotationDescriptors: _readCustomAnnotations(config),
optimizationPhases: optimizationPhases); optimizationPhases: optimizationPhases,
generateChangeDetectors: generateChangeDetectors);
}
bool _readBool(Map config, String paramName, {bool defaultValue}) {
return config.containsKey(paramName)
? config[paramName] != false
: defaultValue;
} }
/// Cribbed from the polymer project. /// Cribbed from the polymer project.

View File

@ -14,15 +14,31 @@ import 'package:angular2/src/facade/lang.dart' show BaseException;
/// This code should be kept in sync with the `ChangeDetectorJITGenerator` /// This code should be kept in sync with the `ChangeDetectorJITGenerator`
/// class. If you make updates here, please make equivalent changes there. /// class. If you make updates here, please make equivalent changes there.
class Codegen { class Codegen {
/// Stores the generated class definitions.
final StringBuffer _buf = new StringBuffer(); final StringBuffer _buf = new StringBuffer();
/// Stores all generated initialization code.
final StringBuffer _initBuf = new StringBuffer();
/// The names of already generated classes.
final Set<String> _names = new Set<String>();
/// Generates a change detector class with name `changeDetectorTypeName` /// Generates a change detector class with name `changeDetectorTypeName`,
/// which is used to detect changes in Objects of type `typeName`. /// which must not conflict with other generated classes in the same
/// `.ng_deps.dart` file. The change detector is used to detect changes in
/// Objects of type `typeName`.
void generate(String typeName, String changeDetectorTypeName, void generate(String typeName, String changeDetectorTypeName,
ChangeDetectorDefinition def) { ChangeDetectorDefinition def) {
new _CodegenState(typeName, changeDetectorTypeName, def)._writeToBuf(_buf); if (_names.contains(changeDetectorTypeName)) {
throw new BaseException(
'Change detector named "${changeDetectorTypeName}" for ${typeName} '
'conflicts with an earlier generated change detector class.');
}
_names.add(changeDetectorTypeName);
new _CodegenState(typeName, changeDetectorTypeName, def)
.._writeToBuf(_buf)
.._writeInitToBuf(_initBuf);
} }
/// Gets all imports necessary for the generated code.
String get imports { String get imports {
return _buf.isEmpty return _buf.isEmpty
? '' ? ''
@ -31,13 +47,27 @@ class Codegen {
bool get isEmpty => _buf.isEmpty; bool get isEmpty => _buf.isEmpty;
/// Gets the initilization code that registers the generated classes with
/// the Angular 2 change detection system.
String get initialize => '$_initBuf';
@override @override
String toString() => '$_buf'; String toString() => '$_buf';
} }
/// The state needed to generate a change detector for a single `Component`. /// The state needed to generate a change detector for a single `Component`.
class _CodegenState { class _CodegenState {
/// The `id` of the `ChangeDetectorDefinition` we are generating this class
/// for.
final String _changeDetectorDefId;
/// The name of the `Type` this change detector is generated for. For example,
/// this is `MyComponent` if the generated class will detect changes in
/// `MyComponent` objects.
final String _contextTypeName; final String _contextTypeName;
/// The name of the generated change detector class. This is an implementation
/// detail and should not be visible to users.
final String _changeDetectorTypeName; final String _changeDetectorTypeName;
final String _changeDetectionMode; final String _changeDetectionMode;
final List<ProtoRecord> _records; final List<ProtoRecord> _records;
@ -47,9 +77,9 @@ class _CodegenState {
final List<String> _fieldNames; final List<String> _fieldNames;
final List<String> _pipeNames; final List<String> _pipeNames;
_CodegenState._(this._contextTypeName, this._changeDetectorTypeName, _CodegenState._(this._changeDetectorDefId, this._contextTypeName,
String changeDetectionStrategy, this._records, this._directiveRecords, this._changeDetectorTypeName, String changeDetectionStrategy,
List<String> localNames) this._records, this._directiveRecords, List<String> localNames)
: this._localNames = localNames, : this._localNames = localNames,
_changeNames = _getChangeNames(localNames), _changeNames = _getChangeNames(localNames),
_fieldNames = _getFieldNames(localNames), _fieldNames = _getFieldNames(localNames),
@ -63,8 +93,8 @@ class _CodegenState {
def.bindingRecords def.bindingRecords
.forEach((rec) => protoRecords.add(rec, def.variableNames)); .forEach((rec) => protoRecords.add(rec, def.variableNames));
var records = coalesce(protoRecords.records); var records = coalesce(protoRecords.records);
return new _CodegenState._(typeName, changeDetectorTypeName, def.strategy, return new _CodegenState._(def.id, typeName, changeDetectorTypeName,
records, def.directiveRecords, _getLocalNames(records)); def.strategy, records, def.directiveRecords, _getLocalNames(records));
} }
/// Generates sanitized names for use as local variables. /// Generates sanitized names for use as local variables.
@ -167,6 +197,13 @@ class _CodegenState {
'''); ''');
} }
void _writeInitToBuf(StringBuffer buf) {
buf.write('''
$_GEN_PREFIX.preGeneratedProtoDetectors['$_changeDetectorDefId'] =
$_changeDetectorTypeName.newProtoChangeDetector;
''');
}
List<String> _genGetDirectiveFieldNames() { List<String> _genGetDirectiveFieldNames() {
return _directiveRecords return _directiveRecords
.map((d) => _genGetDirective(d.directiveIndex)) .map((d) => _genGetDirective(d.directiveIndex))

View File

@ -64,11 +64,14 @@ Future<String> processTemplates(AssetReader reader, AssetId entryPoint,
viewDefResults.ngDeps.lib != null ? viewDefResults.ngDeps.lib.end : 0; viewDefResults.ngDeps.lib != null ? viewDefResults.ngDeps.lib.end : 0;
var codeInjectIdx = var codeInjectIdx =
viewDefResults.ngDeps.registeredTypes.last.registerMethod.end; viewDefResults.ngDeps.registeredTypes.last.registerMethod.end;
var initInjectIdx = viewDefResults.ngDeps.setupMethod.end - 1;
return '${code.substring(0, importInjectIdx)}' return '${code.substring(0, importInjectIdx)}'
'${changeDetectorClasses.imports}' '${changeDetectorClasses.imports}'
'${code.substring(importInjectIdx, codeInjectIdx)}' '${code.substring(importInjectIdx, codeInjectIdx)}'
'${registrations}' '${registrations}'
'${code.substring(codeInjectIdx)}' '${code.substring(codeInjectIdx, initInjectIdx)}'
'${changeDetectorClasses.initialize}'
'${code.substring(initInjectIdx)}'
'$changeDetectorClasses'; '$changeDetectorClasses';
} }
@ -100,8 +103,8 @@ class _TemplateExtractor {
var pipeline = var pipeline =
new CompilePipeline(_factory.createSteps(viewDef, subtaskPromises)); new CompilePipeline(_factory.createSteps(viewDef, subtaskPromises));
var compileElements = pipeline.process( var compileElements =
templateEl, ViewType.COMPONENT, viewDef.componentId); pipeline.process(templateEl, ViewType.COMPONENT, viewDef.componentId);
var protoViewDto = compileElements[0].inheritedProtoView var protoViewDto = compileElements[0].inheritedProtoView
.build(new PropertySetterFactory()); .build(new PropertySetterFactory());

View File

@ -34,8 +34,8 @@ class TemplateCompiler extends Transformer {
Html5LibDomAdapter.makeCurrent(); Html5LibDomAdapter.makeCurrent();
var id = transform.primaryInput.id; var id = transform.primaryInput.id;
var reader = new AssetReader.fromTransform(transform); var reader = new AssetReader.fromTransform(transform);
var transformedCode = var transformedCode = formatter.format(await processTemplates(reader, id,
formatter.format(await processTemplates(reader, id)); generateChangeDetectors: options.generateChangeDetectors));
transform.addOutput(new Asset.fromString(id, transformedCode)); transform.addOutput(new Asset.fromString(id, transformedCode));
} catch (ex, stackTrace) { } catch (ex, stackTrace) {
log.logger.error('Parsing ng templates failed.\n' log.logger.error('Parsing ng templates failed.\n'

View File

@ -33,8 +33,7 @@ class ViewDefinitionEntry {
ViewDefinitionEntry._(this.hostMetadata, this.viewDef); ViewDefinitionEntry._(this.hostMetadata, this.viewDef);
} }
String _getComponentId(AssetId assetId, String className) => String _getComponentId(AssetId assetId, String className) => '$className';
'$assetId:$className';
// TODO(kegluenq): Improve this test. // TODO(kegluenq): Improve this test.
bool _isViewAnnotation(InstanceCreationExpression node) => bool _isViewAnnotation(InstanceCreationExpression node) =>

View File

@ -20,7 +20,7 @@ export function main() {
}); });
it("should return a proto change detector when one is available", () => { it("should return a proto change detector when one is available", () => {
var map = {'id': (registry) => proto}; var map = {'id': (registry, def) => proto};
var cd = new PreGeneratedChangeDetection(null, map); var cd = new PreGeneratedChangeDetection(null, map);
expect(cd.createProtoChangeDetector(def)).toBe(proto) expect(cd.createProtoChangeDetector(def)).toBe(proto)

View File

@ -37,7 +37,6 @@ import {
ON_PUSH, ON_PUSH,
DEFAULT, DEFAULT,
WrappedValue, WrappedValue,
JitProtoChangeDetector,
DynamicProtoChangeDetector, DynamicProtoChangeDetector,
ChangeDetectorDefinition, ChangeDetectorDefinition,
Lexer, Lexer,
@ -45,6 +44,7 @@ import {
Locals, Locals,
ProtoChangeDetector ProtoChangeDetector
} from 'angular2/change_detection'; } from 'angular2/change_detection';
import {JitProtoChangeDetector} from 'angular2/src/change_detection/jit_proto_change_detector';
import {getDefinition} from './change_detector_config'; import {getDefinition} from './change_detector_config';
import {getFactoryById} from './generated/change_detector_classes'; import {getFactoryById} from './generated/change_detector_classes';

View File

@ -22,8 +22,6 @@ void initReflector(reflector) {
..registerType(HelloCmp, { ..registerType(HelloCmp, {
'factory': () => new HelloCmp(), 'factory': () => new HelloCmp(),
'parameters': const [const []], 'parameters': const [const []],
'annotations': const [ 'annotations': const [const Component(changeDetection: 'CHECK_ONCE')]
const Component(changeDetection: 'CHECK_ONCE')
]
}); });
} }

View File

@ -22,8 +22,6 @@ void initReflector(reflector) {
..registerType(HelloCmp, { ..registerType(HelloCmp, {
'factory': () => new HelloCmp(), 'factory': () => new HelloCmp(),
'parameters': const [const []], 'parameters': const [const []],
'annotations': const [ 'annotations': const [const Component(events: ['onFoo', 'onBar'])]
const Component(events: ['onFoo', 'onBar'])
]
}); });
} }

View File

@ -20,6 +20,8 @@ void initReflector(reflector) {
const View(template: 'Salad') const View(template: 'Salad')
] ]
}); });
_gen.preGeneratedProtoDetectors['MyComponent_comp_0'] =
_MyComponent_ChangeDetector0.newProtoChangeDetector;
} }
class _MyComponent_ChangeDetector0 extends _gen.AbstractChangeDetector { class _MyComponent_ChangeDetector0 extends _gen.AbstractChangeDetector {
final dynamic _dispatcher; final dynamic _dispatcher;

View File

@ -24,6 +24,7 @@ transformers:
- web/src/largetable/largetable_benchmark.dart - web/src/largetable/largetable_benchmark.dart
- web/src/naive_infinite_scroll/index.dart - web/src/naive_infinite_scroll/index.dart
- web/src/tree/tree_benchmark.dart - web/src/tree/tree_benchmark.dart
generate_change_detectors: false
- $dart2js: - $dart2js:
minify: false minify: false
commandLineOptions: commandLineOptions:

View File

@ -28,12 +28,4 @@ export function setupReflector() {
MapWrapper.forEach(m, function(v, k) { o.style.setProperty(k, v); }); MapWrapper.forEach(m, function(v, k) { o.style.setProperty(k, v); });
} }
}); });
reflector.registerMethods({
'onScroll': (o, args) => {
// HACK
o.onScroll(args[0]);
},
'setStage': (o, args) => o.setStage(args[0])
});
} }