feat(compiler): add change detector generation
Runtime and Codegen. Part of #3605 Closes #4057
This commit is contained in:
parent
2daf2eedb6
commit
12dd44f7f6
|
@ -0,0 +1,81 @@
|
|||
import {TypeMetadata, SourceModule} from './api';
|
||||
import {
|
||||
ChangeDetectorJITGenerator
|
||||
} from 'angular2/src/core/change_detection/change_detection_jit_generator';
|
||||
|
||||
import {createChangeDetectorDefinitions} from './change_definition_factory';
|
||||
import {isJsObject, CONST_EXPR} from 'angular2/src/core/facade/lang';
|
||||
|
||||
import {
|
||||
ChangeDetectorGenConfig,
|
||||
ChangeDetectorDefinition,
|
||||
DynamicProtoChangeDetector,
|
||||
ChangeDetectionStrategy
|
||||
} from 'angular2/src/core/change_detection/change_detection';
|
||||
|
||||
import {TemplateAst} from './template_ast';
|
||||
import {Codegen} from 'angular2/src/transform/template_compiler/change_detector_codegen';
|
||||
|
||||
var IS_DART = !isJsObject({});
|
||||
|
||||
const ABSTRACT_CHANGE_DETECTOR = "AbstractChangeDetector";
|
||||
const UTIL = "ChangeDetectionUtil";
|
||||
|
||||
const JS_CHANGE_DETECTOR_IMPORTS = CONST_EXPR([
|
||||
['angular2/src/core/change_detection/abstract_change_detector', 'acd'],
|
||||
['angular2/src/core/change_detection/change_detection_util', 'cdu']
|
||||
]);
|
||||
|
||||
const DART_CHANGE_DETECTOR_IMPORTS =
|
||||
CONST_EXPR([['angular2/src/core/change_detection/pregen_proto_change_detector', '_gen']]);
|
||||
|
||||
export class ChangeDetectionCompiler {
|
||||
constructor(private _genConfig: ChangeDetectorGenConfig) {}
|
||||
|
||||
compileComponentRuntime(componentType: TypeMetadata, strategy: ChangeDetectionStrategy,
|
||||
parsedTemplate: TemplateAst[]): Function[] {
|
||||
var changeDetectorDefinitions =
|
||||
createChangeDetectorDefinitions(componentType, strategy, this._genConfig, parsedTemplate);
|
||||
return changeDetectorDefinitions.map(definition =>
|
||||
this._createChangeDetectorFactory(definition));
|
||||
}
|
||||
|
||||
private _createChangeDetectorFactory(definition: ChangeDetectorDefinition): Function {
|
||||
if (IS_DART) {
|
||||
var proto = new DynamicProtoChangeDetector(definition);
|
||||
return (dispatcher) => proto.instantiate(dispatcher);
|
||||
} else {
|
||||
// TODO(tbosch): provide a flag in _genConfig whether to allow eval or fall back to dynamic
|
||||
// change detection as well!
|
||||
return new ChangeDetectorJITGenerator(definition, UTIL, ABSTRACT_CHANGE_DETECTOR).generate();
|
||||
}
|
||||
}
|
||||
|
||||
compileComponentCodeGen(componentType: TypeMetadata, strategy: ChangeDetectionStrategy,
|
||||
parsedTemplate: TemplateAst[]): SourceModule {
|
||||
var changeDetectorDefinitions =
|
||||
createChangeDetectorDefinitions(componentType, strategy, this._genConfig, parsedTemplate);
|
||||
var imports = IS_DART ? DART_CHANGE_DETECTOR_IMPORTS : JS_CHANGE_DETECTOR_IMPORTS;
|
||||
var factories = [];
|
||||
var sourceParts = changeDetectorDefinitions.map(definition => {
|
||||
var codegen: any;
|
||||
// TODO(tbosch): move the 2 code generators to the same place, one with .dart and one with .ts
|
||||
// suffix
|
||||
// and have the same API for calling them!
|
||||
if (IS_DART) {
|
||||
codegen = new Codegen();
|
||||
var className = definition.id;
|
||||
codegen.generate(componentType.typeName, className, definition);
|
||||
factories.push(`(dispatcher) => new ${className}(dispatcher)`);
|
||||
return codegen.toString();
|
||||
} else {
|
||||
codegen = new ChangeDetectorJITGenerator(definition, `cdu.${UTIL}`,
|
||||
`acd.${ABSTRACT_CHANGE_DETECTOR}`);
|
||||
factories.push(`function(dispatcher) { return new ${codegen.typeName}(dispatcher); }`);
|
||||
return codegen.generateSource();
|
||||
}
|
||||
});
|
||||
sourceParts.push(`var CHANGE_DETECTORS = [ ${factories.join(',')} ];`);
|
||||
return new SourceModule(componentType.typeUrl, sourceParts.join('\n'), imports);
|
||||
}
|
||||
}
|
|
@ -76,7 +76,7 @@ export class StyleCompiler {
|
|||
private _styleCodeGen(moduleName: string, plainStyles: string[], absUrls: string[], shim: boolean,
|
||||
suffix: string): SourceModule {
|
||||
var imports: string[][] = [];
|
||||
var moduleSource = `${codeGenExportVar('STYLES')} (`;
|
||||
var moduleSource = `var STYLES = (`;
|
||||
moduleSource +=
|
||||
`[${plainStyles.map( plainStyle => escapeString(this._shimIfNeeded(plainStyle, shim)) ).join(',')}]`;
|
||||
for (var i = 0; i < absUrls.length; i++) {
|
||||
|
@ -109,14 +109,6 @@ function escapeString(input: string): string {
|
|||
return `'${escapedInput}'`;
|
||||
}
|
||||
|
||||
function codeGenExportVar(name: string): string {
|
||||
if (IS_DART) {
|
||||
return `var ${name} =`;
|
||||
} else {
|
||||
return `var ${name} = exports.${name} =`;
|
||||
}
|
||||
}
|
||||
|
||||
function codeGenConcatArray(expression: string): string {
|
||||
return `${IS_DART ? '..addAll' : '.concat'}(${expression})`;
|
||||
}
|
||||
|
|
|
@ -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(); }
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 '';
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import {
|
||||
ChangeDetectorDefinition,
|
||||
} from 'angular2/src/core/change_detection/change_detection';
|
||||
|
||||
// Note: This class is only here so that we can reference it from TypeScript code.
|
||||
// The actual implementation lives under modules_dart.
|
||||
// TODO(tbosch): Move the corresponding code into angular2/src/compiler once
|
||||
// the new compiler is done.
|
||||
export class Codegen {
|
||||
generate(typeName: string, changeDetectorTypeName: string, def: ChangeDetectorDefinition): void {
|
||||
throw "Not implemented in JS";
|
||||
}
|
||||
toString(): string { throw "Not implemented in JS"; }
|
||||
}
|
|
@ -12,7 +12,6 @@ import {
|
|||
TestComponentBuilder
|
||||
} from 'angular2/test_lib';
|
||||
import {MapWrapper} from 'angular2/src/core/facade/collection';
|
||||
import {isBlank} from 'angular2/src/core/facade/lang';
|
||||
import {HtmlParser} from 'angular2/src/compiler/html_parser';
|
||||
import {DirectiveMetadata, TypeMetadata, ChangeDetectionMetadata} from 'angular2/src/compiler/api';
|
||||
import {MockSchemaRegistry} from './template_parser_spec';
|
||||
|
@ -23,7 +22,6 @@ import {
|
|||
ChangeDetectorDefinition,
|
||||
ChangeDetectorGenConfig,
|
||||
DynamicProtoChangeDetector,
|
||||
ProtoChangeDetector,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDispatcher,
|
||||
DirectiveIndex,
|
||||
|
@ -33,6 +31,7 @@ import {
|
|||
} from 'angular2/src/core/change_detection/change_detection';
|
||||
import {Pipes} from 'angular2/src/core/change_detection/pipes';
|
||||
import {createChangeDetectorDefinitions} from 'angular2/src/compiler/change_definition_factory';
|
||||
import {TestContext, TestDirective, TestDispatcher, TestPipes} from './change_detector_mocks';
|
||||
|
||||
export function main() {
|
||||
describe('ChangeDefinitionFactory', () => {
|
||||
|
@ -62,8 +61,8 @@ export function main() {
|
|||
protoViewIndex: number = 0): ChangeDetector {
|
||||
var protoChangeDetectors =
|
||||
createChangeDetectorDefinitions(
|
||||
new TypeMetadata({typeName: 'SomeComp'}), ChangeDetectionStrategy.CheckAlways,
|
||||
new ChangeDetectorGenConfig(true, true, false),
|
||||
new TypeMetadata({typeName: 'SomeComp'}), ChangeDetectionStrategy.Default,
|
||||
new ChangeDetectorGenConfig(true, true, false, false),
|
||||
parser.parse(domParser.parse(template, 'TestComp'), directives))
|
||||
.map(definition => new DynamicProtoChangeDetector(definition));
|
||||
var changeDetector = protoChangeDetectors[protoViewIndex].instantiate(dispatcher);
|
||||
|
@ -164,47 +163,3 @@ export function main() {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
class TestContext {
|
||||
eventLog: string[] = [];
|
||||
someProp: string;
|
||||
someProp2: string;
|
||||
|
||||
onEvent(value: string) { this.eventLog.push(value); }
|
||||
}
|
||||
|
||||
class TestDirective {
|
||||
eventLog: string[] = [];
|
||||
dirProp: string;
|
||||
|
||||
onEvent(value: string) { this.eventLog.push(value); }
|
||||
}
|
||||
|
||||
class TestDispatcher implements ChangeDispatcher {
|
||||
log: string[];
|
||||
|
||||
constructor(public directives: any[], public detectors: ProtoChangeDetector[]) { this.clear(); }
|
||||
|
||||
getDirectiveFor(di: DirectiveIndex) { return this.directives[di.directiveIndex]; }
|
||||
|
||||
getDetectorFor(di: DirectiveIndex) { return this.detectors[di.directiveIndex]; }
|
||||
|
||||
clear() { this.log = []; }
|
||||
|
||||
notifyOnBinding(target: BindingTarget, value) {
|
||||
this.log.push(`${target.mode}(${target.name})=${this._asString(value)}`);
|
||||
}
|
||||
|
||||
logBindingUpdate(target, value) {}
|
||||
|
||||
notifyAfterContentChecked() {}
|
||||
notifyAfterViewChecked() {}
|
||||
|
||||
getDebugContext(a, b) { return null; }
|
||||
|
||||
_asString(value) { return (isBlank(value) ? 'null' : value.toString()); }
|
||||
}
|
||||
|
||||
class TestPipes implements Pipes {
|
||||
get(type: string) { return null; }
|
||||
}
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
import {
|
||||
ddescribe,
|
||||
describe,
|
||||
xdescribe,
|
||||
it,
|
||||
iit,
|
||||
xit,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
inject
|
||||
} from 'angular2/test_lib';
|
||||
import {IS_DART} from '../platform';
|
||||
|
||||
import {CONST_EXPR} from 'angular2/src/core/facade/lang';
|
||||
import {MapWrapper} from 'angular2/src/core/facade/collection';
|
||||
import {Promise} from 'angular2/src/core/facade/async';
|
||||
|
||||
import {ChangeDetectionCompiler} from 'angular2/src/compiler/change_detector_compiler';
|
||||
|
||||
import {HtmlParser} from 'angular2/src/compiler/html_parser';
|
||||
import {
|
||||
DirectiveMetadata,
|
||||
TypeMetadata,
|
||||
ChangeDetectionMetadata,
|
||||
SourceModule
|
||||
} from 'angular2/src/compiler/api';
|
||||
|
||||
import {MockSchemaRegistry} from './template_parser_spec';
|
||||
|
||||
import {TemplateParser} from 'angular2/src/compiler/template_parser';
|
||||
|
||||
import {
|
||||
Parser,
|
||||
Lexer,
|
||||
ChangeDetectorGenConfig,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDispatcher,
|
||||
DirectiveIndex,
|
||||
Locals,
|
||||
BindingTarget,
|
||||
ChangeDetector
|
||||
} from 'angular2/src/core/change_detection/change_detection';
|
||||
|
||||
import {evalModule} from './eval_module';
|
||||
|
||||
import {TestContext, TestDispatcher, TestPipes} from './change_detector_mocks';
|
||||
|
||||
// Attention: These module names have to correspond to real modules!
|
||||
const MODULE_NAME = 'angular2/test/compiler/change_detector_compiler_spec';
|
||||
|
||||
export function main() {
|
||||
describe('ChangeDetectorCompiler', () => {
|
||||
var domParser: HtmlParser;
|
||||
var parser: TemplateParser;
|
||||
|
||||
function createCompiler(useJit: boolean): ChangeDetectionCompiler {
|
||||
return new ChangeDetectionCompiler(new ChangeDetectorGenConfig(true, true, false, useJit));
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
domParser = new HtmlParser();
|
||||
parser = new TemplateParser(
|
||||
new Parser(new Lexer()),
|
||||
new MockSchemaRegistry({'invalidProp': false}, {'mappedAttr': 'mappedProp'}));
|
||||
});
|
||||
|
||||
describe('compileComponentRuntime', () => {
|
||||
function detectChanges(compiler: ChangeDetectionCompiler, template: string,
|
||||
directives: DirectiveMetadata[] = CONST_EXPR([])): string[] {
|
||||
var type = new TypeMetadata({typeName: 'SomeComp'});
|
||||
var parsedTemplate = parser.parse(domParser.parse(template, 'TestComp'), directives);
|
||||
var factories =
|
||||
compiler.compileComponentRuntime(type, ChangeDetectionStrategy.Default, parsedTemplate);
|
||||
return testChangeDetector(factories[0]);
|
||||
}
|
||||
|
||||
it('should watch element properties (no jit)', () => {
|
||||
expect(detectChanges(createCompiler(false), '<div [el-prop]="someProp">'))
|
||||
.toEqual(['elementProperty(elProp)=someValue']);
|
||||
});
|
||||
|
||||
it('should watch element properties (jit)', () => {
|
||||
expect(detectChanges(createCompiler(true), '<div [el-prop]="someProp">'))
|
||||
.toEqual(['elementProperty(elProp)=someValue']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('compileComponentCodeGen', () => {
|
||||
function detectChanges(compiler: ChangeDetectionCompiler, template: string,
|
||||
directives: DirectiveMetadata[] = CONST_EXPR([])): Promise<string[]> {
|
||||
var type = new TypeMetadata({typeName: 'SomeComp'});
|
||||
var parsedTemplate = parser.parse(domParser.parse(template, 'TestComp'), directives);
|
||||
var sourceModule =
|
||||
compiler.compileComponentCodeGen(type, ChangeDetectionStrategy.Default, parsedTemplate);
|
||||
var testableModule = createTestableModule(sourceModule, 0);
|
||||
return evalModule(testableModule.source, testableModule.imports, null);
|
||||
}
|
||||
|
||||
it('should watch element properties', inject([AsyncTestCompleter], (async) => {
|
||||
detectChanges(createCompiler(true), '<div [el-prop]="someProp">')
|
||||
.then((value) => {
|
||||
expect(value).toEqual(['elementProperty(elProp)=someValue']);
|
||||
async.done();
|
||||
});
|
||||
|
||||
}));
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function createTestableModule(sourceModule: SourceModule, changeDetectorIndex: number):
|
||||
SourceModule {
|
||||
var testableSource;
|
||||
var testableImports = [[MODULE_NAME, 'mocks']].concat(sourceModule.imports);
|
||||
if (IS_DART) {
|
||||
testableSource = `${sourceModule.source}
|
||||
run(_) { return mocks.testChangeDetector(CHANGE_DETECTORS[${changeDetectorIndex}]); }`;
|
||||
} else {
|
||||
testableSource = `${sourceModule.source}
|
||||
exports.run = function(_) { return mocks.testChangeDetector(CHANGE_DETECTORS[${changeDetectorIndex}]); }`;
|
||||
}
|
||||
return new SourceModule(null, testableSource, testableImports);
|
||||
}
|
||||
|
||||
export function testChangeDetector(changeDetectorFactory: Function): string[] {
|
||||
var dispatcher = new TestDispatcher([], []);
|
||||
var cd = changeDetectorFactory(dispatcher);
|
||||
var ctx = new TestContext();
|
||||
ctx.someProp = 'someValue';
|
||||
var locals = new Locals(null, MapWrapper.createFromStringMap({'someVar': null}));
|
||||
cd.hydrate(ctx, locals, dispatcher, new TestPipes());
|
||||
cd.detectChanges();
|
||||
return dispatcher.log;
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
import {isBlank} from 'angular2/src/core/facade/lang';
|
||||
import {Pipes} from 'angular2/src/core/change_detection/pipes';
|
||||
import {
|
||||
ProtoChangeDetector,
|
||||
ChangeDispatcher,
|
||||
DirectiveIndex,
|
||||
BindingTarget
|
||||
} from 'angular2/src/core/change_detection/change_detection';
|
||||
|
||||
export class TestContext {
|
||||
eventLog: string[] = [];
|
||||
someProp: string;
|
||||
someProp2: string;
|
||||
|
||||
onEvent(value: string) { this.eventLog.push(value); }
|
||||
}
|
||||
|
||||
export class TestDirective {
|
||||
eventLog: string[] = [];
|
||||
dirProp: string;
|
||||
|
||||
onEvent(value: string) { this.eventLog.push(value); }
|
||||
}
|
||||
|
||||
export class TestDispatcher implements ChangeDispatcher {
|
||||
log: string[];
|
||||
|
||||
constructor(public directives: any[], public detectors: ProtoChangeDetector[]) { this.clear(); }
|
||||
|
||||
getDirectiveFor(di: DirectiveIndex) { return this.directives[di.directiveIndex]; }
|
||||
|
||||
getDetectorFor(di: DirectiveIndex) { return this.detectors[di.directiveIndex]; }
|
||||
|
||||
clear() { this.log = []; }
|
||||
|
||||
notifyOnBinding(target: BindingTarget, value) {
|
||||
this.log.push(`${target.mode}(${target.name})=${this._asString(value)}`);
|
||||
}
|
||||
|
||||
logBindingUpdate(target, value) {}
|
||||
|
||||
notifyAfterContentChecked() {}
|
||||
notifyAfterViewChecked() {}
|
||||
|
||||
getDebugContext(a, b) { return null; }
|
||||
|
||||
_asString(value) { return (isBlank(value) ? 'null' : value.toString()); }
|
||||
}
|
||||
|
||||
export class TestPipes implements Pipes {
|
||||
get(type: string) { return null; }
|
||||
}
|
|
@ -65,7 +65,7 @@ export const PROP_NAME = 'propName';
|
|||
* In this case, we expect `id` and `expression` to be the same string.
|
||||
*/
|
||||
export function getDefinition(id: string): TestDefinition {
|
||||
var genConfig = new ChangeDetectorGenConfig(true, true, true);
|
||||
var genConfig = new ChangeDetectorGenConfig(true, true, true, true);
|
||||
var testDef = null;
|
||||
if (StringMapWrapper.contains(_ExpressionWithLocals.availableDefinitions, id)) {
|
||||
let val = StringMapWrapper.get(_ExpressionWithLocals.availableDefinitions, id);
|
||||
|
@ -121,7 +121,7 @@ export function getDefinition(id: string): TestDefinition {
|
|||
[_DirectiveUpdating.recordNoCallbacks], genConfig);
|
||||
testDef = new TestDefinition(id, cdDef, null);
|
||||
} else if (id == "updateElementProduction") {
|
||||
var genConfig = new ChangeDetectorGenConfig(false, false, false);
|
||||
var genConfig = new ChangeDetectorGenConfig(false, false, false, true);
|
||||
var records = _createBindingRecords("name");
|
||||
let cdDef = new ChangeDetectorDefinition(id, null, [], records, [], [], genConfig);
|
||||
testDef = new TestDefinition(id, cdDef, null);
|
||||
|
@ -167,7 +167,7 @@ class _ExpressionWithLocals {
|
|||
var variableBindings = _convertLocalsToVariableBindings(this.locals);
|
||||
var bindingRecords = _createBindingRecords(this._expression);
|
||||
var directiveRecords = [];
|
||||
var genConfig = new ChangeDetectorGenConfig(true, true, true);
|
||||
var genConfig = new ChangeDetectorGenConfig(true, true, true, true);
|
||||
return new ChangeDetectorDefinition('(empty id)', strategy, variableBindings, bindingRecords,
|
||||
[], directiveRecords, genConfig);
|
||||
}
|
||||
|
@ -231,7 +231,7 @@ class _ExpressionWithMode {
|
|||
_createHostEventRecords("(host-event)='false'", dirRecordWithOnPush))
|
||||
}
|
||||
|
||||
var genConfig = new ChangeDetectorGenConfig(true, true, true);
|
||||
var genConfig = new ChangeDetectorGenConfig(true, true, true, true);
|
||||
|
||||
return new ChangeDetectorDefinition('(empty id)', this._strategy, variableBindings,
|
||||
bindingRecords, eventRecords, directiveRecords, genConfig);
|
||||
|
@ -260,7 +260,7 @@ class _DirectiveUpdating {
|
|||
createChangeDetectorDefinition(): ChangeDetectorDefinition {
|
||||
var strategy = null;
|
||||
var variableBindings = [];
|
||||
var genConfig = new ChangeDetectorGenConfig(true, true, true);
|
||||
var genConfig = new ChangeDetectorGenConfig(true, true, true, true);
|
||||
|
||||
return new ChangeDetectorDefinition('(empty id)', strategy, variableBindings,
|
||||
this._bindingRecords, [], this._directiveRecords,
|
||||
|
|
|
@ -1512,7 +1512,8 @@ export function main() {
|
|||
describe('logging property updates', () => {
|
||||
beforeEachBindings(() => [
|
||||
bind(ChangeDetection)
|
||||
.toValue(new DynamicChangeDetection(new ChangeDetectorGenConfig(true, true, true)))
|
||||
.toValue(
|
||||
new DynamicChangeDetection(new ChangeDetectorGenConfig(true, true, true, false)))
|
||||
]);
|
||||
|
||||
it('should reflect property values as attributes',
|
||||
|
|
|
@ -249,7 +249,7 @@ function setUpChangeDetection(changeDetection: ChangeDetection, iterations, obje
|
|||
var dispatcher = new DummyDispatcher();
|
||||
var parser = new Parser(new Lexer());
|
||||
|
||||
var genConfig = new ChangeDetectorGenConfig(false, false, false);
|
||||
var genConfig = new ChangeDetectorGenConfig(false, false, false, true);
|
||||
var parentProto = changeDetection.getProtoChangeDetector(
|
||||
"id", new ChangeDetectorDefinition('parent', null, [], [], [], [], genConfig));
|
||||
var parentCd = parentProto.instantiate(dispatcher);
|
||||
|
|
|
@ -58,7 +58,7 @@ Future<String> processTemplates(AssetReader reader, AssetId entryPoint,
|
|||
}
|
||||
if (generateChangeDetectors) {
|
||||
var saved = reflector.reflectionCapabilities;
|
||||
var genConfig = new ChangeDetectorGenConfig(assertionsEnabled(), assertionsEnabled(), reflectPropertiesAsAttributes);
|
||||
var genConfig = new ChangeDetectorGenConfig(assertionsEnabled(), assertionsEnabled(), reflectPropertiesAsAttributes, false);
|
||||
|
||||
reflector.reflectionCapabilities = const NullReflectionCapabilities();
|
||||
var defs = getChangeDetectorDefinitions(viewDefEntry.hostMetadata,
|
||||
|
|
Loading…
Reference in New Issue