feat(compiler): add change detector generation

Runtime and Codegen.

Part of #3605
Closes #4057
This commit is contained in:
Tobias Bosch
2015-09-08 09:57:51 -07:00
parent 2daf2eedb6
commit 12dd44f7f6
15 changed files with 373 additions and 120 deletions

View File

@ -103,7 +103,7 @@ export class PreGeneratedChangeDetection extends ChangeDetection {
this._genConfig =
isPresent(config) ? config : new ChangeDetectorGenConfig(assertionsEnabled(),
assertionsEnabled(), false);
assertionsEnabled(), false, false);
}
static isSupported(): boolean { return PregenProtoChangeDetector.isSupported(); }
@ -133,7 +133,7 @@ export class DynamicChangeDetection extends ChangeDetection {
super();
this._genConfig =
isPresent(config) ? config : new ChangeDetectorGenConfig(assertionsEnabled(),
assertionsEnabled(), false);
assertionsEnabled(), false, false);
}
getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector {
@ -157,7 +157,7 @@ export class JitChangeDetection extends ChangeDetection {
super();
this._genConfig =
isPresent(config) ? config : new ChangeDetectorGenConfig(assertionsEnabled(),
assertionsEnabled(), false);
assertionsEnabled(), false, true);
}
static isSupported(): boolean { return JitProtoChangeDetector.isSupported(); }

View File

@ -7,11 +7,16 @@ library change_detection.change_detection_jit_generator;
/// `PregenProtoChangeDetector`, and
/// `src/transform/template_compiler/change_detector_codegen.dart` for details.
class ChangeDetectorJITGenerator {
ChangeDetectorJITGenerator(typeName, strategy, records, directiveMementos) {}
String typeName;
ChangeDetectorJITGenerator(definition, changeDetectionUtilVarName, abstractChangeDetectorVarName) {}
generate() {
throw "Jit Change Detection is not supported in Dart";
}
generateSource() {
throw "Jit Change Detection is not supported in Dart";
}
static bool isSupported() => false;
}

View File

@ -1,4 +1,10 @@
import {BaseException, Type, isBlank, isPresent} from 'angular2/src/core/facade/lang';
import {
BaseException,
Type,
isBlank,
isPresent,
StringWrapper
} from 'angular2/src/core/facade/lang';
import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
import {AbstractChangeDetector} from './abstract_change_detector';
@ -11,10 +17,9 @@ import {CodegenLogicUtil} from './codegen_logic_util';
import {codify} from './codegen_facade';
import {EventBinding} from './event_binding';
import {BindingTarget} from './binding_record';
import {ChangeDetectorGenConfig} from './interfaces';
import {ChangeDetectorGenConfig, ChangeDetectorDefinition} from './interfaces';
import {ChangeDetectionStrategy} from './constants';
import {createPropertyRecords, createEventRecords} from './proto_change_detector';
/**
* The code generator takes a list of proto records and creates a function/class
@ -25,39 +30,65 @@ import {ChangeDetectionStrategy} from './constants';
* `angular2.transform.template_compiler.change_detector_codegen` library. If you make updates
* here, please make equivalent changes there.
*/
const ABSTRACT_CHANGE_DETECTOR = "AbstractChangeDetector";
const UTIL = "ChangeDetectionUtil";
const IS_CHANGED_LOCAL = "isChanged";
const CHANGES_LOCAL = "changes";
export class ChangeDetectorJITGenerator {
_logic: CodegenLogicUtil;
_names: CodegenNameUtil;
_typeName: string;
private _logic: CodegenLogicUtil;
private _names: CodegenNameUtil;
private id: string;
private changeDetectionStrategy: ChangeDetectionStrategy;
private records: ProtoRecord[];
private propertyBindingTargets: BindingTarget[];
private eventBindings: EventBinding[];
private directiveRecords: any[];
private genConfig: ChangeDetectorGenConfig;
typeName: string;
constructor(private id: string, private changeDetectionStrategy: ChangeDetectionStrategy,
private records: ProtoRecord[], private propertyBindingTargets: BindingTarget[],
private eventBindings: EventBinding[], private directiveRecords: any[],
private genConfig: ChangeDetectorGenConfig) {
this._names =
new CodegenNameUtil(this.records, this.eventBindings, this.directiveRecords, UTIL);
this._logic = new CodegenLogicUtil(this._names, UTIL, changeDetectionStrategy);
this._typeName = sanitizeName(`ChangeDetector_${this.id}`);
constructor(definition: ChangeDetectorDefinition, private changeDetectionUtilVarName: string,
private abstractChangeDetectorVarName: string) {
var propertyBindingRecords = createPropertyRecords(definition);
var eventBindingRecords = createEventRecords(definition);
var propertyBindingTargets = definition.bindingRecords.map(b => b.target);
this.id = definition.id;
this.changeDetectionStrategy = definition.strategy;
this.genConfig = definition.genConfig;
this.records = propertyBindingRecords;
this.propertyBindingTargets = propertyBindingTargets;
this.eventBindings = eventBindingRecords;
this.directiveRecords = definition.directiveRecords;
this._names = new CodegenNameUtil(this.records, this.eventBindings, this.directiveRecords,
this.changeDetectionUtilVarName);
this._logic = new CodegenLogicUtil(this._names, this.changeDetectionUtilVarName,
this.changeDetectionStrategy);
this.typeName = sanitizeName(`ChangeDetector_${this.id}`);
}
generate(): Function {
var classDefinition = `
var ${this._typeName} = function ${this._typeName}(dispatcher) {
${ABSTRACT_CHANGE_DETECTOR}.call(
var factorySource = `
${this.generateSource()}
return function(dispatcher) {
return new ${this.typeName}(dispatcher);
}
`;
return new Function(this.abstractChangeDetectorVarName, this.changeDetectionUtilVarName,
factorySource)(AbstractChangeDetector, ChangeDetectionUtil);
}
generateSource(): string {
return `
var ${this.typeName} = function ${this.typeName}(dispatcher) {
${this.abstractChangeDetectorVarName}.call(
this, ${JSON.stringify(this.id)}, dispatcher, ${this.records.length},
${this._typeName}.gen_propertyBindingTargets, ${this._typeName}.gen_directiveIndices,
${this.typeName}.gen_propertyBindingTargets, ${this.typeName}.gen_directiveIndices,
${codify(this.changeDetectionStrategy)});
this.dehydrateDirectives(false);
}
${this._typeName}.prototype = Object.create(${ABSTRACT_CHANGE_DETECTOR}.prototype);
${this.typeName}.prototype = Object.create(${this.abstractChangeDetectorVarName}.prototype);
${this._typeName}.prototype.detectChangesInRecordsInternal = function(throwOnChange) {
${this.typeName}.prototype.detectChangesInRecordsInternal = function(throwOnChange) {
${this._names.genInitLocals()}
var ${IS_CHANGED_LOCAL} = false;
var ${CHANGES_LOCAL} = null;
@ -80,31 +111,25 @@ export class ChangeDetectorJITGenerator {
${this._genPropertyBindingTargets()}
${this._genDirectiveIndices()}
return function(dispatcher) {
return new ${this._typeName}(dispatcher);
}
`;
return new Function(ABSTRACT_CHANGE_DETECTOR, UTIL, classDefinition)(AbstractChangeDetector,
ChangeDetectionUtil);
}
_genPropertyBindingTargets(): string {
var targets = this._logic.genPropertyBindingTargets(this.propertyBindingTargets,
this.genConfig.genDebugInfo);
return `${this._typeName}.gen_propertyBindingTargets = ${targets};`;
return `${this.typeName}.gen_propertyBindingTargets = ${targets};`;
}
_genDirectiveIndices(): string {
var indices = this._logic.genDirectiveIndices(this.directiveRecords);
return `${this._typeName}.gen_directiveIndices = ${indices};`;
return `${this.typeName}.gen_directiveIndices = ${indices};`;
}
_maybeGenHandleEventInternal(): string {
if (this.eventBindings.length > 0) {
var handlers = this.eventBindings.map(eb => this._genEventBinding(eb)).join("\n");
return `
${this._typeName}.prototype.handleEventInternal = function(eventName, elIndex, locals) {
${this.typeName}.prototype.handleEventInternal = function(eventName, elIndex, locals) {
var ${this._names.getPreventDefaultAccesor()} = false;
${this._names.genInitEventLocals()}
${handlers}
@ -156,7 +181,7 @@ export class ChangeDetectorJITGenerator {
}
var dehydrateFieldsCode = this._names.genDehydrateFields();
if (!destroyPipesCode && !dehydrateFieldsCode) return '';
return `${this._typeName}.prototype.dehydrateDirectives = function(destroyPipes) {
return `${this.typeName}.prototype.dehydrateDirectives = function(destroyPipes) {
${destroyPipesCode}
${dehydrateFieldsCode}
}`;
@ -166,7 +191,7 @@ export class ChangeDetectorJITGenerator {
var hydrateDirectivesCode = this._logic.genHydrateDirectives(this.directiveRecords);
var hydrateDetectorsCode = this._logic.genHydrateDetectors(this.directiveRecords);
if (!hydrateDirectivesCode && !hydrateDetectorsCode) return '';
return `${this._typeName}.prototype.hydrateDirectives = function(directives) {
return `${this.typeName}.prototype.hydrateDirectives = function(directives) {
${hydrateDirectivesCode}
${hydrateDetectorsCode}
}`;
@ -177,7 +202,7 @@ export class ChangeDetectorJITGenerator {
if (notifications.length > 0) {
var directiveNotifications = notifications.join("\n");
return `
${this._typeName}.prototype.afterContentLifecycleCallbacksInternal = function() {
${this.typeName}.prototype.afterContentLifecycleCallbacksInternal = function() {
${directiveNotifications}
}
`;
@ -191,7 +216,7 @@ export class ChangeDetectorJITGenerator {
if (notifications.length > 0) {
var directiveNotifications = notifications.join("\n");
return `
${this._typeName}.prototype.afterViewLifecycleCallbacksInternal = function() {
${this.typeName}.prototype.afterViewLifecycleCallbacksInternal = function() {
${directiveNotifications}
}
`;
@ -239,7 +264,7 @@ export class ChangeDetectorJITGenerator {
var pipeName = r.name;
var init = `
if (${pipe} === ${UTIL}.uninitialized) {
if (${pipe} === ${this.changeDetectionUtilVarName}.uninitialized) {
${pipe} = ${this._names.getPipesAccessorName()}.get('${pipeName}');
}
`;
@ -251,7 +276,7 @@ export class ChangeDetectorJITGenerator {
var check = `
if (${oldValue} !== ${newValue}) {
${newValue} = ${UTIL}.unwrapValue(${newValue})
${newValue} = ${this.changeDetectionUtilVarName}.unwrapValue(${newValue})
${this._genChangeMarker(r)}
${this._genUpdateDirectiveOrElement(r)}
${this._genAddToChanges(r)}
@ -342,7 +367,7 @@ export class ChangeDetectorJITGenerator {
_genCheckNoChanges(): string {
if (this.genConfig.genCheckNoChanges) {
return `${this._typeName}.prototype.checkNoChanges = function() { this.runDetectChanges(true); }`;
return `${this.typeName}.prototype.checkNoChanges = function() { this.runDetectChanges(true); }`;
} else {
return '';
}

View File

@ -77,7 +77,7 @@ export interface ProtoChangeDetector { instantiate(dispatcher: ChangeDispatcher)
export class ChangeDetectorGenConfig {
constructor(public genCheckNoChanges: boolean, public genDebugInfo: boolean,
public logBindingUpdate: boolean) {}
public logBindingUpdate: boolean, public useJit: boolean) {}
}
export class ChangeDetectorDefinition {

View File

@ -4,9 +4,6 @@ import {isPresent} from 'angular2/src/core/facade/lang';
import {ProtoChangeDetector, ChangeDetector, ChangeDetectorDefinition} from './interfaces';
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
import {coalesce} from './coalesce';
import {createPropertyRecords, createEventRecords} from './proto_change_detector';
export class JitProtoChangeDetector implements ProtoChangeDetector {
_factory: Function;
@ -19,13 +16,6 @@ export class JitProtoChangeDetector implements ProtoChangeDetector {
instantiate(dispatcher: any): ChangeDetector { return this._factory(dispatcher); }
_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, propertyBindingTargets,
eventBindingRecords, this.definition.directiveRecords, this.definition.genConfig)
.generate();
return new ChangeDetectorJITGenerator(definition, 'util', 'AbstractChangeDetector').generate();
}
}