Skip to content

Commit 12dd44f

Browse files
committed
feat(compiler): add change detector generation
Runtime and Codegen. Part of #3605 Closes #4057
1 parent 2daf2ee commit 12dd44f

File tree

15 files changed

+374
-121
lines changed

15 files changed

+374
-121
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import {TypeMetadata, SourceModule} from './api';
2+
import {
3+
ChangeDetectorJITGenerator
4+
} from 'angular2/src/core/change_detection/change_detection_jit_generator';
5+
6+
import {createChangeDetectorDefinitions} from './change_definition_factory';
7+
import {isJsObject, CONST_EXPR} from 'angular2/src/core/facade/lang';
8+
9+
import {
10+
ChangeDetectorGenConfig,
11+
ChangeDetectorDefinition,
12+
DynamicProtoChangeDetector,
13+
ChangeDetectionStrategy
14+
} from 'angular2/src/core/change_detection/change_detection';
15+
16+
import {TemplateAst} from './template_ast';
17+
import {Codegen} from 'angular2/src/transform/template_compiler/change_detector_codegen';
18+
19+
var IS_DART = !isJsObject({});
20+
21+
const ABSTRACT_CHANGE_DETECTOR = "AbstractChangeDetector";
22+
const UTIL = "ChangeDetectionUtil";
23+
24+
const JS_CHANGE_DETECTOR_IMPORTS = CONST_EXPR([
25+
['angular2/src/core/change_detection/abstract_change_detector', 'acd'],
26+
['angular2/src/core/change_detection/change_detection_util', 'cdu']
27+
]);
28+
29+
const DART_CHANGE_DETECTOR_IMPORTS =
30+
CONST_EXPR([['angular2/src/core/change_detection/pregen_proto_change_detector', '_gen']]);
31+
32+
export class ChangeDetectionCompiler {
33+
constructor(private _genConfig: ChangeDetectorGenConfig) {}
34+
35+
compileComponentRuntime(componentType: TypeMetadata, strategy: ChangeDetectionStrategy,
36+
parsedTemplate: TemplateAst[]): Function[] {
37+
var changeDetectorDefinitions =
38+
createChangeDetectorDefinitions(componentType, strategy, this._genConfig, parsedTemplate);
39+
return changeDetectorDefinitions.map(definition =>
40+
this._createChangeDetectorFactory(definition));
41+
}
42+
43+
private _createChangeDetectorFactory(definition: ChangeDetectorDefinition): Function {
44+
if (IS_DART) {
45+
var proto = new DynamicProtoChangeDetector(definition);
46+
return (dispatcher) => proto.instantiate(dispatcher);
47+
} else {
48+
// TODO(tbosch): provide a flag in _genConfig whether to allow eval or fall back to dynamic
49+
// change detection as well!
50+
return new ChangeDetectorJITGenerator(definition, UTIL, ABSTRACT_CHANGE_DETECTOR).generate();
51+
}
52+
}
53+
54+
compileComponentCodeGen(componentType: TypeMetadata, strategy: ChangeDetectionStrategy,
55+
parsedTemplate: TemplateAst[]): SourceModule {
56+
var changeDetectorDefinitions =
57+
createChangeDetectorDefinitions(componentType, strategy, this._genConfig, parsedTemplate);
58+
var imports = IS_DART ? DART_CHANGE_DETECTOR_IMPORTS : JS_CHANGE_DETECTOR_IMPORTS;
59+
var factories = [];
60+
var sourceParts = changeDetectorDefinitions.map(definition => {
61+
var codegen: any;
62+
// TODO(tbosch): move the 2 code generators to the same place, one with .dart and one with .ts
63+
// suffix
64+
// and have the same API for calling them!
65+
if (IS_DART) {
66+
codegen = new Codegen();
67+
var className = definition.id;
68+
codegen.generate(componentType.typeName, className, definition);
69+
factories.push(`(dispatcher) => new ${className}(dispatcher)`);
70+
return codegen.toString();
71+
} else {
72+
codegen = new ChangeDetectorJITGenerator(definition, `cdu.${UTIL}`,
73+
`acd.${ABSTRACT_CHANGE_DETECTOR}`);
74+
factories.push(`function(dispatcher) { return new ${codegen.typeName}(dispatcher); }`);
75+
return codegen.generateSource();
76+
}
77+
});
78+
sourceParts.push(`var CHANGE_DETECTORS = [ ${factories.join(',')} ];`);
79+
return new SourceModule(componentType.typeUrl, sourceParts.join('\n'), imports);
80+
}
81+
}

modules/angular2/src/compiler/style_compiler.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export class StyleCompiler {
7676
private _styleCodeGen(moduleName: string, plainStyles: string[], absUrls: string[], shim: boolean,
7777
suffix: string): SourceModule {
7878
var imports: string[][] = [];
79-
var moduleSource = `${codeGenExportVar('STYLES')} (`;
79+
var moduleSource = `var STYLES = (`;
8080
moduleSource +=
8181
`[${plainStyles.map( plainStyle => escapeString(this._shimIfNeeded(plainStyle, shim)) ).join(',')}]`;
8282
for (var i = 0; i < absUrls.length; i++) {
@@ -109,14 +109,6 @@ function escapeString(input: string): string {
109109
return `'${escapedInput}'`;
110110
}
111111

112-
function codeGenExportVar(name: string): string {
113-
if (IS_DART) {
114-
return `var ${name} =`;
115-
} else {
116-
return `var ${name} = exports.${name} =`;
117-
}
118-
}
119-
120112
function codeGenConcatArray(expression: string): string {
121113
return `${IS_DART ? '..addAll' : '.concat'}(${expression})`;
122114
}

modules/angular2/src/core/change_detection/change_detection.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export class PreGeneratedChangeDetection extends ChangeDetection {
103103

104104
this._genConfig =
105105
isPresent(config) ? config : new ChangeDetectorGenConfig(assertionsEnabled(),
106-
assertionsEnabled(), false);
106+
assertionsEnabled(), false, false);
107107
}
108108

109109
static isSupported(): boolean { return PregenProtoChangeDetector.isSupported(); }
@@ -133,7 +133,7 @@ export class DynamicChangeDetection extends ChangeDetection {
133133
super();
134134
this._genConfig =
135135
isPresent(config) ? config : new ChangeDetectorGenConfig(assertionsEnabled(),
136-
assertionsEnabled(), false);
136+
assertionsEnabled(), false, false);
137137
}
138138

139139
getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector {
@@ -157,7 +157,7 @@ export class JitChangeDetection extends ChangeDetection {
157157
super();
158158
this._genConfig =
159159
isPresent(config) ? config : new ChangeDetectorGenConfig(assertionsEnabled(),
160-
assertionsEnabled(), false);
160+
assertionsEnabled(), false, true);
161161
}
162162

163163
static isSupported(): boolean { return JitProtoChangeDetector.isSupported(); }

modules/angular2/src/core/change_detection/change_detection_jit_generator.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,16 @@ library change_detection.change_detection_jit_generator;
77
/// `PregenProtoChangeDetector`, and
88
/// `src/transform/template_compiler/change_detector_codegen.dart` for details.
99
class ChangeDetectorJITGenerator {
10-
ChangeDetectorJITGenerator(typeName, strategy, records, directiveMementos) {}
10+
String typeName;
11+
ChangeDetectorJITGenerator(definition, changeDetectionUtilVarName, abstractChangeDetectorVarName) {}
1112

1213
generate() {
1314
throw "Jit Change Detection is not supported in Dart";
1415
}
1516

17+
generateSource() {
18+
throw "Jit Change Detection is not supported in Dart";
19+
}
20+
1621
static bool isSupported() => false;
1722
}

modules/angular2/src/core/change_detection/change_detection_jit_generator.ts

Lines changed: 65 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import {BaseException, Type, isBlank, isPresent} from 'angular2/src/core/facade/lang';
1+
import {
2+
BaseException,
3+
Type,
4+
isBlank,
5+
isPresent,
6+
StringWrapper
7+
} from 'angular2/src/core/facade/lang';
28
import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
39

410
import {AbstractChangeDetector} from './abstract_change_detector';
@@ -11,10 +17,9 @@ import {CodegenLogicUtil} from './codegen_logic_util';
1117
import {codify} from './codegen_facade';
1218
import {EventBinding} from './event_binding';
1319
import {BindingTarget} from './binding_record';
14-
import {ChangeDetectorGenConfig} from './interfaces';
20+
import {ChangeDetectorGenConfig, ChangeDetectorDefinition} from './interfaces';
1521
import {ChangeDetectionStrategy} from './constants';
16-
17-
22+
import {createPropertyRecords, createEventRecords} from './proto_change_detector';
1823

1924
/**
2025
* The code generator takes a list of proto records and creates a function/class
@@ -25,39 +30,65 @@ import {ChangeDetectionStrategy} from './constants';
2530
* `angular2.transform.template_compiler.change_detector_codegen` library. If you make updates
2631
* here, please make equivalent changes there.
2732
*/
28-
const ABSTRACT_CHANGE_DETECTOR = "AbstractChangeDetector";
29-
const UTIL = "ChangeDetectionUtil";
3033
const IS_CHANGED_LOCAL = "isChanged";
3134
const CHANGES_LOCAL = "changes";
3235

3336
export class ChangeDetectorJITGenerator {
34-
_logic: CodegenLogicUtil;
35-
_names: CodegenNameUtil;
36-
_typeName: string;
37-
38-
constructor(private id: string, private changeDetectionStrategy: ChangeDetectionStrategy,
39-
private records: ProtoRecord[], private propertyBindingTargets: BindingTarget[],
40-
private eventBindings: EventBinding[], private directiveRecords: any[],
41-
private genConfig: ChangeDetectorGenConfig) {
42-
this._names =
43-
new CodegenNameUtil(this.records, this.eventBindings, this.directiveRecords, UTIL);
44-
this._logic = new CodegenLogicUtil(this._names, UTIL, changeDetectionStrategy);
45-
this._typeName = sanitizeName(`ChangeDetector_${this.id}`);
37+
private _logic: CodegenLogicUtil;
38+
private _names: CodegenNameUtil;
39+
private id: string;
40+
private changeDetectionStrategy: ChangeDetectionStrategy;
41+
private records: ProtoRecord[];
42+
private propertyBindingTargets: BindingTarget[];
43+
private eventBindings: EventBinding[];
44+
private directiveRecords: any[];
45+
private genConfig: ChangeDetectorGenConfig;
46+
typeName: string;
47+
48+
constructor(definition: ChangeDetectorDefinition, private changeDetectionUtilVarName: string,
49+
private abstractChangeDetectorVarName: string) {
50+
var propertyBindingRecords = createPropertyRecords(definition);
51+
var eventBindingRecords = createEventRecords(definition);
52+
var propertyBindingTargets = definition.bindingRecords.map(b => b.target);
53+
this.id = definition.id;
54+
this.changeDetectionStrategy = definition.strategy;
55+
this.genConfig = definition.genConfig;
56+
57+
this.records = propertyBindingRecords;
58+
this.propertyBindingTargets = propertyBindingTargets;
59+
this.eventBindings = eventBindingRecords;
60+
this.directiveRecords = definition.directiveRecords;
61+
this._names = new CodegenNameUtil(this.records, this.eventBindings, this.directiveRecords,
62+
this.changeDetectionUtilVarName);
63+
this._logic = new CodegenLogicUtil(this._names, this.changeDetectionUtilVarName,
64+
this.changeDetectionStrategy);
65+
this.typeName = sanitizeName(`ChangeDetector_${this.id}`);
4666
}
4767

4868
generate(): Function {
49-
var classDefinition = `
50-
var ${this._typeName} = function ${this._typeName}(dispatcher) {
51-
${ABSTRACT_CHANGE_DETECTOR}.call(
69+
var factorySource = `
70+
${this.generateSource()}
71+
return function(dispatcher) {
72+
return new ${this.typeName}(dispatcher);
73+
}
74+
`;
75+
return new Function(this.abstractChangeDetectorVarName, this.changeDetectionUtilVarName,
76+
factorySource)(AbstractChangeDetector, ChangeDetectionUtil);
77+
}
78+
79+
generateSource(): string {
80+
return `
81+
var ${this.typeName} = function ${this.typeName}(dispatcher) {
82+
${this.abstractChangeDetectorVarName}.call(
5283
this, ${JSON.stringify(this.id)}, dispatcher, ${this.records.length},
53-
${this._typeName}.gen_propertyBindingTargets, ${this._typeName}.gen_directiveIndices,
84+
${this.typeName}.gen_propertyBindingTargets, ${this.typeName}.gen_directiveIndices,
5485
${codify(this.changeDetectionStrategy)});
5586
this.dehydrateDirectives(false);
5687
}
5788
58-
${this._typeName}.prototype = Object.create(${ABSTRACT_CHANGE_DETECTOR}.prototype);
89+
${this.typeName}.prototype = Object.create(${this.abstractChangeDetectorVarName}.prototype);
5990
60-
${this._typeName}.prototype.detectChangesInRecordsInternal = function(throwOnChange) {
91+
${this.typeName}.prototype.detectChangesInRecordsInternal = function(throwOnChange) {
6192
${this._names.genInitLocals()}
6293
var ${IS_CHANGED_LOCAL} = false;
6394
var ${CHANGES_LOCAL} = null;
@@ -80,31 +111,25 @@ export class ChangeDetectorJITGenerator {
80111
${this._genPropertyBindingTargets()}
81112
82113
${this._genDirectiveIndices()}
83-
84-
return function(dispatcher) {
85-
return new ${this._typeName}(dispatcher);
86-
}
87114
`;
88-
return new Function(ABSTRACT_CHANGE_DETECTOR, UTIL, classDefinition)(AbstractChangeDetector,
89-
ChangeDetectionUtil);
90115
}
91116

92117
_genPropertyBindingTargets(): string {
93118
var targets = this._logic.genPropertyBindingTargets(this.propertyBindingTargets,
94119
this.genConfig.genDebugInfo);
95-
return `${this._typeName}.gen_propertyBindingTargets = ${targets};`;
120+
return `${this.typeName}.gen_propertyBindingTargets = ${targets};`;
96121
}
97122

98123
_genDirectiveIndices(): string {
99124
var indices = this._logic.genDirectiveIndices(this.directiveRecords);
100-
return `${this._typeName}.gen_directiveIndices = ${indices};`;
125+
return `${this.typeName}.gen_directiveIndices = ${indices};`;
101126
}
102127

103128
_maybeGenHandleEventInternal(): string {
104129
if (this.eventBindings.length > 0) {
105130
var handlers = this.eventBindings.map(eb => this._genEventBinding(eb)).join("\n");
106131
return `
107-
${this._typeName}.prototype.handleEventInternal = function(eventName, elIndex, locals) {
132+
${this.typeName}.prototype.handleEventInternal = function(eventName, elIndex, locals) {
108133
var ${this._names.getPreventDefaultAccesor()} = false;
109134
${this._names.genInitEventLocals()}
110135
${handlers}
@@ -156,7 +181,7 @@ export class ChangeDetectorJITGenerator {
156181
}
157182
var dehydrateFieldsCode = this._names.genDehydrateFields();
158183
if (!destroyPipesCode && !dehydrateFieldsCode) return '';
159-
return `${this._typeName}.prototype.dehydrateDirectives = function(destroyPipes) {
184+
return `${this.typeName}.prototype.dehydrateDirectives = function(destroyPipes) {
160185
${destroyPipesCode}
161186
${dehydrateFieldsCode}
162187
}`;
@@ -166,7 +191,7 @@ export class ChangeDetectorJITGenerator {
166191
var hydrateDirectivesCode = this._logic.genHydrateDirectives(this.directiveRecords);
167192
var hydrateDetectorsCode = this._logic.genHydrateDetectors(this.directiveRecords);
168193
if (!hydrateDirectivesCode && !hydrateDetectorsCode) return '';
169-
return `${this._typeName}.prototype.hydrateDirectives = function(directives) {
194+
return `${this.typeName}.prototype.hydrateDirectives = function(directives) {
170195
${hydrateDirectivesCode}
171196
${hydrateDetectorsCode}
172197
}`;
@@ -177,7 +202,7 @@ export class ChangeDetectorJITGenerator {
177202
if (notifications.length > 0) {
178203
var directiveNotifications = notifications.join("\n");
179204
return `
180-
${this._typeName}.prototype.afterContentLifecycleCallbacksInternal = function() {
205+
${this.typeName}.prototype.afterContentLifecycleCallbacksInternal = function() {
181206
${directiveNotifications}
182207
}
183208
`;
@@ -191,7 +216,7 @@ export class ChangeDetectorJITGenerator {
191216
if (notifications.length > 0) {
192217
var directiveNotifications = notifications.join("\n");
193218
return `
194-
${this._typeName}.prototype.afterViewLifecycleCallbacksInternal = function() {
219+
${this.typeName}.prototype.afterViewLifecycleCallbacksInternal = function() {
195220
${directiveNotifications}
196221
}
197222
`;
@@ -239,7 +264,7 @@ export class ChangeDetectorJITGenerator {
239264
var pipeName = r.name;
240265

241266
var init = `
242-
if (${pipe} === ${UTIL}.uninitialized) {
267+
if (${pipe} === ${this.changeDetectionUtilVarName}.uninitialized) {
243268
${pipe} = ${this._names.getPipesAccessorName()}.get('${pipeName}');
244269
}
245270
`;
@@ -251,7 +276,7 @@ export class ChangeDetectorJITGenerator {
251276

252277
var check = `
253278
if (${oldValue} !== ${newValue}) {
254-
${newValue} = ${UTIL}.unwrapValue(${newValue})
279+
${newValue} = ${this.changeDetectionUtilVarName}.unwrapValue(${newValue})
255280
${this._genChangeMarker(r)}
256281
${this._genUpdateDirectiveOrElement(r)}
257282
${this._genAddToChanges(r)}
@@ -342,7 +367,7 @@ export class ChangeDetectorJITGenerator {
342367

343368
_genCheckNoChanges(): string {
344369
if (this.genConfig.genCheckNoChanges) {
345-
return `${this._typeName}.prototype.checkNoChanges = function() { this.runDetectChanges(true); }`;
370+
return `${this.typeName}.prototype.checkNoChanges = function() { this.runDetectChanges(true); }`;
346371
} else {
347372
return '';
348373
}

modules/angular2/src/core/change_detection/interfaces.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export interface ProtoChangeDetector { instantiate(dispatcher: ChangeDispatcher)
7777

7878
export class ChangeDetectorGenConfig {
7979
constructor(public genCheckNoChanges: boolean, public genDebugInfo: boolean,
80-
public logBindingUpdate: boolean) {}
80+
public logBindingUpdate: boolean, public useJit: boolean) {}
8181
}
8282

8383
export class ChangeDetectorDefinition {

modules/angular2/src/core/change_detection/jit_proto_change_detector.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ import {isPresent} from 'angular2/src/core/facade/lang';
44
import {ProtoChangeDetector, ChangeDetector, ChangeDetectorDefinition} from './interfaces';
55
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
66

7-
import {coalesce} from './coalesce';
8-
import {createPropertyRecords, createEventRecords} from './proto_change_detector';
9-
107
export class JitProtoChangeDetector implements ProtoChangeDetector {
118
_factory: Function;
129

@@ -19,13 +16,6 @@ export class JitProtoChangeDetector implements ProtoChangeDetector {
1916
instantiate(dispatcher: any): ChangeDetector { return this._factory(dispatcher); }
2017

2118
_createFactory(definition: ChangeDetectorDefinition) {
22-
var propertyBindingRecords = createPropertyRecords(definition);
23-
var eventBindingRecords = createEventRecords(definition);
24-
var propertyBindingTargets = this.definition.bindingRecords.map(b => b.target);
25-
26-
return new ChangeDetectorJITGenerator(
27-
definition.id, definition.strategy, propertyBindingRecords, propertyBindingTargets,
28-
eventBindingRecords, this.definition.directiveRecords, this.definition.genConfig)
29-
.generate();
19+
return new ChangeDetectorJITGenerator(definition, 'util', 'AbstractChangeDetector').generate();
3020
}
3121
}

0 commit comments

Comments
 (0)