feat(change_detection): generate checkNoChanges only in dev mode

This commit is contained in:
vsavkin 2015-07-27 13:16:20 -07:00
parent a2bb81c406
commit 71bb4b3ee5
11 changed files with 68 additions and 31 deletions

View File

@ -1,4 +1,4 @@
import {isPresent} from 'angular2/src/facade/lang'; import {isPresent, BaseException} from 'angular2/src/facade/lang';
import {List, ListWrapper} from 'angular2/src/facade/collection'; import {List, ListWrapper} from 'angular2/src/facade/collection';
import {ChangeDetectorRef} from './change_detector_ref'; import {ChangeDetectorRef} from './change_detector_ref';
import {ChangeDetector} from './interfaces'; import {ChangeDetector} from './interfaces';
@ -38,11 +38,11 @@ export class AbstractChangeDetector implements ChangeDetector {
remove(): void { this.parent.removeChild(this); } remove(): void { this.parent.removeChild(this); }
detectChanges(): void { this._detectChanges(false); } detectChanges(): void { this.runDetectChanges(false); }
checkNoChanges(): void { this._detectChanges(true); } checkNoChanges(): void { throw new BaseException("Not implemented"); }
_detectChanges(throwOnChange: boolean): void { runDetectChanges(throwOnChange: boolean): void {
if (this.mode === DETACHED || this.mode === CHECKED) return; if (this.mode === DETACHED || this.mode === CHECKED) return;
this.detectChangesInRecords(throwOnChange); this.detectChangesInRecords(throwOnChange);
@ -67,14 +67,14 @@ export class AbstractChangeDetector implements ChangeDetector {
_detectChangesInLightDomChildren(throwOnChange: boolean): void { _detectChangesInLightDomChildren(throwOnChange: boolean): void {
var c = this.lightDomChildren; var c = this.lightDomChildren;
for (var i = 0; i < c.length; ++i) { for (var i = 0; i < c.length; ++i) {
c[i]._detectChanges(throwOnChange); c[i].runDetectChanges(throwOnChange);
} }
} }
_detectChangesInShadowDomChildren(throwOnChange: boolean): void { _detectChangesInShadowDomChildren(throwOnChange: boolean): void {
var c = this.shadowDomChildren; var c = this.shadowDomChildren;
for (var i = 0; i < c.length; ++i) { for (var i = 0; i < c.length; ++i) {
c[i]._detectChanges(throwOnChange); c[i].runDetectChanges(throwOnChange);
} }
} }

View File

@ -35,7 +35,8 @@ export class ChangeDetectorJITGenerator {
_names: CodegenNameUtil; _names: CodegenNameUtil;
constructor(public id: string, public changeDetectionStrategy: string, constructor(public id: string, public changeDetectionStrategy: string,
public records: List<ProtoRecord>, public directiveRecords: List<any>) { public records: List<ProtoRecord>, public directiveRecords: List<any>,
private generateCheckNoChanges: boolean) {
this._names = new CodegenNameUtil(this.records, this.directiveRecords, 'this._', UTIL); this._names = new CodegenNameUtil(this.records, this.directiveRecords, 'this._', UTIL);
} }
@ -80,6 +81,8 @@ export class ChangeDetectorJITGenerator {
${ALREADY_CHECKED_ACCESSOR} = true; ${ALREADY_CHECKED_ACCESSOR} = true;
} }
${this._genCheckNoChanges(typeName)}
${typeName}.prototype.callOnAllChangesDone = function() { ${typeName}.prototype.callOnAllChangesDone = function() {
${this._genCallOnAllChangesDoneBody()} ${this._genCallOnAllChangesDoneBody()}
} }
@ -109,6 +112,7 @@ export class ChangeDetectorJITGenerator {
return new ${typeName}(dispatcher, protos, directiveRecords); return new ${typeName}(dispatcher, protos, directiveRecords);
} }
`; `;
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos', return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos',
'directiveRecords', classDefinition)( 'directiveRecords', classDefinition)(
AbstractChangeDetector, ChangeDetectionUtil, this.records, this.directiveRecords); AbstractChangeDetector, ChangeDetectionUtil, this.records, this.directiveRecords);
@ -330,11 +334,23 @@ export class ChangeDetectorJITGenerator {
} }
_genThrowOnChangeCheck(oldValue: string, newValue: string): string { _genThrowOnChangeCheck(oldValue: string, newValue: string): string {
return ` if (this.generateCheckNoChanges) {
if(throwOnChange) { return `
${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue})); if(throwOnChange) {
} ${UTIL}.throwOnChange(${CURRENT_PROTO}, ${UTIL}.simpleChange(${oldValue}, ${newValue}));
`; }
`;
} else {
return '';
}
}
_genCheckNoChanges(typeName: string): string {
if (this.generateCheckNoChanges) {
return `${typeName}.prototype.checkNoChanges = function() { this.runDetectChanges(true); }`;
} else {
return '';
}
} }
_genAddToChanges(r: ProtoRecord): string { _genAddToChanges(r: ProtoRecord): string {

View File

@ -65,6 +65,8 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
hydrated(): boolean { return this.values[0] !== null; } hydrated(): boolean { return this.values[0] !== null; }
checkNoChanges(): void { this.runDetectChanges(true); }
detectChangesInRecords(throwOnChange: boolean) { detectChangesInRecords(throwOnChange: boolean) {
if (!this.hydrated()) { if (!this.hydrated()) {
ChangeDetectionUtil.throwDehydrated(); ChangeDetectionUtil.throwDehydrated();

View File

@ -63,5 +63,6 @@ export interface ProtoChangeDetector { instantiate(dispatcher: any): ChangeDetec
export class ChangeDetectorDefinition { export class ChangeDetectorDefinition {
constructor(public id: string, public strategy: string, public variableNames: List<string>, constructor(public id: string, public strategy: string, public variableNames: List<string>,
public bindingRecords: List<BindingRecord>, public bindingRecords: List<BindingRecord>,
public directiveRecords: List<DirectiveRecord>) {} public directiveRecords: List<DirectiveRecord>,
public generateCheckNoChanges: boolean) {}
} }

View File

@ -23,7 +23,8 @@ export class JitProtoChangeDetector implements ProtoChangeDetector {
(b) => { recordBuilder.add(b, definition.variableNames); }); (b) => { recordBuilder.add(b, definition.variableNames); });
var records = coalesce(recordBuilder.records); var records = coalesce(recordBuilder.records);
return new ChangeDetectorJITGenerator(definition.id, definition.strategy, records, return new ChangeDetectorJITGenerator(definition.id, definition.strategy, records,
this.definition.directiveRecords) this.definition.directiveRecords,
this.definition.generateCheckNoChanges)
.generate(); .generate();
} }
} }

View File

@ -1,7 +1,7 @@
import {Injectable} from 'angular2/di'; import {Injectable} from 'angular2/di';
import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection'; import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang'; import {isPresent, isBlank, BaseException, assertionsEnabled} from 'angular2/src/facade/lang';
import {reflector} from 'angular2/src/reflection/reflection'; import {reflector} from 'angular2/src/reflection/reflection';
import { import {
@ -246,7 +246,7 @@ function _getChangeDetectorDefinitions(
var id = `${hostComponentMetadata.id}_${typeString}_${pvWithIndex.index}`; var id = `${hostComponentMetadata.id}_${typeString}_${pvWithIndex.index}`;
var variableNames = nestedPvVariableNames[pvWithIndex.index]; var variableNames = nestedPvVariableNames[pvWithIndex.index];
return new ChangeDetectorDefinition(id, strategyName, variableNames, bindingRecords, return new ChangeDetectorDefinition(id, strategyName, variableNames, bindingRecords,
directiveRecords); directiveRecords, assertionsEnabled());
}); });
} }

View File

@ -74,10 +74,11 @@ class _CodegenState {
final List<ProtoRecord> _records; final List<ProtoRecord> _records;
final List<DirectiveRecord> _directiveRecords; final List<DirectiveRecord> _directiveRecords;
final CodegenNameUtil _names; final CodegenNameUtil _names;
final bool _generateCheckNoChanges;
_CodegenState._(this._changeDetectorDefId, this._contextTypeName, _CodegenState._(this._changeDetectorDefId, this._contextTypeName,
this._changeDetectorTypeName, String changeDetectionStrategy, this._changeDetectorTypeName, String changeDetectionStrategy,
List<ProtoRecord> records, List<DirectiveRecord> directiveRecords) List<ProtoRecord> records, List<DirectiveRecord> directiveRecords, this._generateCheckNoChanges)
: _records = records, : _records = records,
_directiveRecords = directiveRecords, _directiveRecords = directiveRecords,
_names = new CodegenNameUtil(records, directiveRecords, '_', _UTIL), _names = new CodegenNameUtil(records, directiveRecords, '_', _UTIL),
@ -91,7 +92,7 @@ class _CodegenState {
.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._(def.id, typeName, changeDetectorTypeName, return new _CodegenState._(def.id, typeName, changeDetectorTypeName,
def.strategy, records, def.directiveRecords); def.strategy, records, def.directiveRecords, def.generateCheckNoChanges);
} }
void _writeToBuf(StringBuffer buf) { void _writeToBuf(StringBuffer buf) {
@ -138,6 +139,8 @@ class _CodegenState {
$_ALREADY_CHECKED_ACCESSOR = true; $_ALREADY_CHECKED_ACCESSOR = true;
} }
${_genCheckNoChanges()}
void callOnAllChangesDone() { void callOnAllChangesDone() {
${_getCallOnAllChangesDoneBody()} ${_getCallOnAllChangesDoneBody()}
} }
@ -393,12 +396,24 @@ class _CodegenState {
} }
String _genThrowOnChangeCheck(String oldValue, String newValue) { String _genThrowOnChangeCheck(String oldValue, String newValue) {
return ''' if (this._generateCheckNoChanges) {
if(throwOnChange) { return '''
$_UTIL.throwOnChange( if(throwOnChange) {
$_CURRENT_PROTO, $_UTIL.simpleChange(${oldValue}, ${newValue})); $_UTIL.throwOnChange(
} $_CURRENT_PROTO, $_UTIL.simpleChange(${oldValue}, ${newValue}));
'''; }
''';
} else {
return "";
}
}
String _genCheckNoChanges() {
if (this._generateCheckNoChanges) {
return 'void checkNoChanges() { this.runDetectChanges(true); }';
} else {
return '';
}
} }
String _genAddToChanges(ProtoRecord r) { String _genAddToChanges(ProtoRecord r) {

View File

@ -23,7 +23,7 @@ export function main() {
beforeEach(() => { beforeEach(() => {
proto = new SpyProtoChangeDetector(); proto = new SpyProtoChangeDetector();
def = new ChangeDetectorDefinition('id', null, [], [], []); def = new ChangeDetectorDefinition('id', null, [], [], [], true);
}); });
it("should return a proto change detector when one is available", () => { it("should return a proto change detector when one is available", () => {

View File

@ -69,7 +69,7 @@ export function getDefinition(id: string): TestDefinition {
var bindingRecords = _createBindingRecords(id); var bindingRecords = _createBindingRecords(id);
var directiveRecords = []; var directiveRecords = [];
let cdDef = new ChangeDetectorDefinition(id, strategy, variableBindings, bindingRecords, let cdDef = new ChangeDetectorDefinition(id, strategy, variableBindings, bindingRecords,
directiveRecords); directiveRecords, true);
testDef = new TestDefinition(id, cdDef, null); testDef = new TestDefinition(id, cdDef, null);
} }
if (isBlank(testDef)) { if (isBlank(testDef)) {
@ -107,7 +107,7 @@ class _ExpressionWithLocals {
var bindingRecords = _createBindingRecords(this._expression); var bindingRecords = _createBindingRecords(this._expression);
var directiveRecords = []; var directiveRecords = [];
return new ChangeDetectorDefinition('(empty id)', strategy, variableBindings, bindingRecords, return new ChangeDetectorDefinition('(empty id)', strategy, variableBindings, bindingRecords,
directiveRecords); directiveRecords, true);
} }
/** /**
@ -151,7 +151,7 @@ class _ExpressionWithMode {
directiveRecords = []; directiveRecords = [];
} }
return new ChangeDetectorDefinition('(empty id)', this._strategy, variableBindings, return new ChangeDetectorDefinition('(empty id)', this._strategy, variableBindings,
bindingRecords, directiveRecords); bindingRecords, directiveRecords, true);
} }
/** /**
@ -174,7 +174,7 @@ class _DirectiveUpdating {
var variableBindings = []; var variableBindings = [];
return new ChangeDetectorDefinition('(empty id)', strategy, variableBindings, return new ChangeDetectorDefinition('(empty id)', strategy, variableBindings,
this._bindingRecords, this._directiveRecords); this._bindingRecords, this._directiveRecords, true);
} }
static updateA(expression: string, dirRecord): BindingRecord { static updateA(expression: string, dirRecord): BindingRecord {

View File

@ -91,6 +91,8 @@ class _MyComponent_ChangeDetector0 extends _gen.AbstractChangeDetector {
_alreadyChecked = true; _alreadyChecked = true;
} }
void checkNoChanges() {this.runDetectChanges(true);}
void callOnAllChangesDone() { void callOnAllChangesDone() {
dispatcher.notifyOnAllChangesDone(); dispatcher.notifyOnAllChangesDone();
} }

View File

@ -249,7 +249,7 @@ function setUpChangeDetection(changeDetection: ChangeDetection, iterations, obje
var parser = new Parser(new Lexer()); var parser = new Parser(new Lexer());
var parentProto = changeDetection.createProtoChangeDetector( var parentProto = changeDetection.createProtoChangeDetector(
new ChangeDetectorDefinition('parent', null, [], [], [])); new ChangeDetectorDefinition('parent', null, [], [], [], false));
var parentCd = parentProto.instantiate(dispatcher); var parentCd = parentProto.instantiate(dispatcher);
var directiveRecord = new DirectiveRecord({directiveIndex: new DirectiveIndex(0, 0)}); var directiveRecord = new DirectiveRecord({directiveIndex: new DirectiveIndex(0, 0)});
@ -277,7 +277,7 @@ function setUpChangeDetection(changeDetection: ChangeDetection, iterations, obje
]; ];
var proto = changeDetection.createProtoChangeDetector( var proto = changeDetection.createProtoChangeDetector(
new ChangeDetectorDefinition("proto", null, [], bindings, [directiveRecord])); new ChangeDetectorDefinition("proto", null, [], bindings, [directiveRecord], false));
var targetObj = new Obj(); var targetObj = new Obj();
parentCd.hydrate(object, null, new FakeDirectives(targetObj), null); parentCd.hydrate(object, null, new FakeDirectives(targetObj), null);