refactor(change_detect): Share more codegen logic
Move more logic in our codegen into a shared util which is called by the Jit & Prege change detector code.
This commit is contained in:
parent
392de4af67
commit
c58b0ff787
|
@ -7,6 +7,7 @@ import {DirectiveIndex, DirectiveRecord} from './directive_record';
|
|||
|
||||
import {ProtoRecord, RecordType} from './proto_record';
|
||||
import {CodegenNameUtil, sanitizeName} from './codegen_name_util';
|
||||
import {CodegenLogicUtil} from './codegen_logic_util';
|
||||
|
||||
|
||||
/**
|
||||
|
@ -24,6 +25,7 @@ var IS_CHANGED_LOCAL = "isChanged";
|
|||
var CHANGES_LOCAL = "changes";
|
||||
|
||||
export class ChangeDetectorJITGenerator {
|
||||
_logic: CodegenLogicUtil;
|
||||
_names: CodegenNameUtil;
|
||||
_typeName: string;
|
||||
|
||||
|
@ -31,6 +33,7 @@ export class ChangeDetectorJITGenerator {
|
|||
public records: List<ProtoRecord>, public directiveRecords: List<any>,
|
||||
private generateCheckNoChanges: boolean) {
|
||||
this._names = new CodegenNameUtil(this.records, this.directiveRecords, UTIL);
|
||||
this._logic = new CodegenLogicUtil(this._names, UTIL);
|
||||
this._typeName = sanitizeName(`ChangeDetector_${this.id}`);
|
||||
}
|
||||
|
||||
|
@ -206,7 +209,7 @@ export class ChangeDetectorJITGenerator {
|
|||
var oldValue = this._names.getFieldName(r.selfIndex);
|
||||
var newValue = this._names.getLocalName(r.selfIndex);
|
||||
var read = `
|
||||
${this._genUpdateCurrentValue(r)}
|
||||
${this._logic.genUpdateCurrentValue(r)}
|
||||
`;
|
||||
|
||||
var check = `
|
||||
|
@ -232,80 +235,6 @@ export class ChangeDetectorJITGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
_genUpdateCurrentValue(r: ProtoRecord): string {
|
||||
var context = (r.contextIndex == -1) ? this._names.getDirectiveName(r.directiveIndex) :
|
||||
this._names.getLocalName(r.contextIndex);
|
||||
var newValue = this._names.getLocalName(r.selfIndex);
|
||||
var argString = r.args.map((arg) => this._names.getLocalName(arg)).join(", ");
|
||||
|
||||
var rhs;
|
||||
switch (r.mode) {
|
||||
case RecordType.SELF:
|
||||
rhs = context;
|
||||
break;
|
||||
|
||||
case RecordType.CONST:
|
||||
rhs = JSON.stringify(r.funcOrValue);
|
||||
break;
|
||||
|
||||
case RecordType.PROPERTY:
|
||||
rhs = `${context}.${r.name}`;
|
||||
break;
|
||||
|
||||
case RecordType.SAFE_PROPERTY:
|
||||
rhs = `${UTIL}.isValueBlank(${context}) ? null : ${context}.${r.name}`;
|
||||
break;
|
||||
|
||||
case RecordType.LOCAL:
|
||||
rhs = `${this._names.getLocalsAccessorName()}.get('${r.name}')`;
|
||||
break;
|
||||
|
||||
case RecordType.INVOKE_METHOD:
|
||||
rhs = `${context}.${r.name}(${argString})`;
|
||||
break;
|
||||
|
||||
case RecordType.SAFE_INVOKE_METHOD:
|
||||
rhs = `${UTIL}.isValueBlank(${context}) ? null : ${context}.${r.name}(${argString})`;
|
||||
break;
|
||||
|
||||
case RecordType.INVOKE_CLOSURE:
|
||||
rhs = `${context}(${argString})`;
|
||||
break;
|
||||
|
||||
case RecordType.PRIMITIVE_OP:
|
||||
rhs = `${UTIL}.${r.name}(${argString})`;
|
||||
break;
|
||||
|
||||
case RecordType.COLLECTION_LITERAL:
|
||||
rhs = `${UTIL}.${r.name}(${argString})`;
|
||||
break;
|
||||
|
||||
case RecordType.INTERPOLATE:
|
||||
rhs = this._genInterpolation(r);
|
||||
break;
|
||||
|
||||
case RecordType.KEYED_ACCESS:
|
||||
rhs = `${context}[${this._names.getLocalName(r.args[0])}]`;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new BaseException(`Unknown operation ${r.mode}`);
|
||||
}
|
||||
return `${newValue} = ${rhs}`;
|
||||
}
|
||||
|
||||
_genInterpolation(r: ProtoRecord): string {
|
||||
var res = "";
|
||||
for (var i = 0; i < r.args.length; ++i) {
|
||||
res += JSON.stringify(r.fixedArgs[i]);
|
||||
res += " + ";
|
||||
res += `${UTIL}.s(${this._names.getLocalName(r.args[i])})`;
|
||||
res += " + ";
|
||||
}
|
||||
res += JSON.stringify(r.fixedArgs[r.args.length]);
|
||||
return res;
|
||||
}
|
||||
|
||||
_genChangeMarker(r: ProtoRecord): string {
|
||||
return r.argumentToPureFunction ? `${this._names.getChangeName(r.selfIndex)} = true` : ``;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
library angular2.src.change_detection.codegen_facade;
|
||||
|
||||
import 'dart:convert' show JSON;
|
||||
|
||||
/// Converts `funcOrValue` to a string which can be used in generated code.
|
||||
String codify(funcOrValue) => JSON.encode(funcOrValue).replaceAll(r'$', r'\$');
|
||||
|
||||
/// Combine the strings of generated code into a single interpolated string.
|
||||
/// Each element of `vals` is expected to be a string literal or a codegen'd
|
||||
/// call to a method returning a string.
|
||||
/// The return format interpolates each value as an expression which reads
|
||||
/// poorly, but the resulting code is easily flattened by dart2js.
|
||||
String combineGeneratedStrings(List<String> vals) {
|
||||
return '"${vals.map((v) => '\${$v}').join('')}"';
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import {List} from 'angular2/src/facade/collection';
|
||||
|
||||
/**
|
||||
* Converts `funcOrValue` to a string which can be used in generated code.
|
||||
*/
|
||||
export function codify(obj: any): string {
|
||||
return JSON.stringify(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine the strings of generated code into a single interpolated string.
|
||||
* Each element of `vals` is expected to be a string literal or a codegen'd
|
||||
* call to a method returning a string.
|
||||
*/
|
||||
export function combineGeneratedStrings(vals: string[]): string {
|
||||
return vals.join(' + ');
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {BaseException, Json} from 'angular2/src/facade/lang';
|
||||
import {CodegenNameUtil} from './codegen_name_util';
|
||||
import {codify, combineGeneratedStrings} from './codegen_facade';
|
||||
import {ProtoRecord, RecordType} from './proto_record';
|
||||
|
||||
/**
|
||||
* Class responsible for providing change detection logic for chagne detector classes.
|
||||
*/
|
||||
export class CodegenLogicUtil {
|
||||
constructor(private _names: CodegenNameUtil, private _utilName: string) {}
|
||||
|
||||
/**
|
||||
* Generates a statement which updates the local variable representing `protoRec` with the current
|
||||
* value of the record.
|
||||
*/
|
||||
genUpdateCurrentValue(protoRec: ProtoRecord): string {
|
||||
var context = (protoRec.contextIndex == -1) ?
|
||||
this._names.getDirectiveName(protoRec.directiveIndex) :
|
||||
this._names.getLocalName(protoRec.contextIndex);
|
||||
var argString =
|
||||
ListWrapper.map(protoRec.args, (arg) => this._names.getLocalName(arg)).join(", ");
|
||||
|
||||
var rhs: string;
|
||||
switch (protoRec.mode) {
|
||||
case RecordType.SELF:
|
||||
rhs = context;
|
||||
break;
|
||||
|
||||
case RecordType.CONST:
|
||||
rhs = codify(protoRec.funcOrValue);
|
||||
break;
|
||||
|
||||
case RecordType.PROPERTY:
|
||||
rhs = `${context}.${protoRec.name}`;
|
||||
break;
|
||||
|
||||
case RecordType.SAFE_PROPERTY:
|
||||
rhs = `${this._utilName}.isValueBlank(${context}) ? null : ${context}.${protoRec.name}`;
|
||||
break;
|
||||
|
||||
case RecordType.LOCAL:
|
||||
rhs = `${this._names.getLocalsAccessorName()}.get('${protoRec.name}')`;
|
||||
break;
|
||||
|
||||
case RecordType.INVOKE_METHOD:
|
||||
rhs = `${context}.${protoRec.name}(${argString})`;
|
||||
break;
|
||||
|
||||
case RecordType.SAFE_INVOKE_METHOD:
|
||||
rhs =
|
||||
`${this._utilName}.isValueBlank(${context}) ? null : ${context}.${protoRec.name}(${argString})`;
|
||||
break;
|
||||
|
||||
case RecordType.INVOKE_CLOSURE:
|
||||
rhs = `${context}(${argString})`;
|
||||
break;
|
||||
|
||||
case RecordType.PRIMITIVE_OP:
|
||||
rhs = `${this._utilName}.${protoRec.name}(${argString})`;
|
||||
break;
|
||||
|
||||
case RecordType.COLLECTION_LITERAL:
|
||||
rhs = `${this._utilName}.${protoRec.name}(${argString})`;
|
||||
break;
|
||||
|
||||
case RecordType.INTERPOLATE:
|
||||
rhs = this._genInterpolation(protoRec);
|
||||
break;
|
||||
|
||||
case RecordType.KEYED_ACCESS:
|
||||
rhs = `${context}[${this._names.getLocalName(protoRec.args[0])}]`;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new BaseException(`Unknown operation ${protoRec.mode}`);
|
||||
}
|
||||
return `${this._names.getLocalName(protoRec.selfIndex)} = ${rhs};`;
|
||||
}
|
||||
|
||||
_genInterpolation(protoRec: ProtoRecord): string {
|
||||
var iVals = [];
|
||||
for (var i = 0; i < protoRec.args.length; ++i) {
|
||||
iVals.push(codify(protoRec.fixedArgs[i]));
|
||||
iVals.push(`${this._utilName}.s(${this._names.getLocalName(protoRec.args[i])})`);
|
||||
}
|
||||
iVals.push(codify(protoRec.fixedArgs[protoRec.args.length]));
|
||||
return combineGeneratedStrings(iVals);
|
||||
}
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
library angular2.transform.template_compiler.change_detector_codegen;
|
||||
|
||||
import 'dart:convert' show JSON;
|
||||
import 'package:angular2/src/change_detection/change_detection_util.dart';
|
||||
import 'package:angular2/src/change_detection/coalesce.dart';
|
||||
import 'package:angular2/src/change_detection/codegen_facade.dart';
|
||||
import 'package:angular2/src/change_detection/codegen_logic_util.dart';
|
||||
import 'package:angular2/src/change_detection/codegen_name_util.dart';
|
||||
import 'package:angular2/src/change_detection/directive_record.dart';
|
||||
import 'package:angular2/src/change_detection/interfaces.dart';
|
||||
|
@ -73,27 +74,26 @@ class _CodegenState {
|
|||
final String _changeDetectionMode;
|
||||
final List<ProtoRecord> _records;
|
||||
final List<DirectiveRecord> _directiveRecords;
|
||||
final CodegenLogicUtil _logic;
|
||||
final CodegenNameUtil _names;
|
||||
final bool _generateCheckNoChanges;
|
||||
|
||||
_CodegenState._(this._changeDetectorDefId, this._contextTypeName,
|
||||
this._changeDetectorTypeName, String changeDetectionStrategy,
|
||||
List<ProtoRecord> records, List<DirectiveRecord> directiveRecords,
|
||||
this._records, this._directiveRecords, this._logic, this._names,
|
||||
this._generateCheckNoChanges)
|
||||
: _records = records,
|
||||
_directiveRecords = directiveRecords,
|
||||
_names = new CodegenNameUtil(records, directiveRecords, _UTIL),
|
||||
_changeDetectionMode = ChangeDetectionUtil
|
||||
.changeDetectionMode(changeDetectionStrategy);
|
||||
: _changeDetectionMode = ChangeDetectionUtil
|
||||
.changeDetectionMode(changeDetectionStrategy);
|
||||
|
||||
factory _CodegenState(String typeName, String changeDetectorTypeName,
|
||||
ChangeDetectorDefinition def) {
|
||||
var protoRecords = new ProtoRecordBuilder();
|
||||
def.bindingRecords
|
||||
.forEach((rec) => protoRecords.add(rec, def.variableNames));
|
||||
var records = coalesce(protoRecords.records);
|
||||
var recBuilder = new ProtoRecordBuilder();
|
||||
def.bindingRecords.forEach((rec) => recBuilder.add(rec, def.variableNames));
|
||||
var protoRecords = coalesce(recBuilder.records);
|
||||
var names = new CodegenNameUtil(protoRecords, def.directiveRecords, _UTIL);
|
||||
var logic = new CodegenLogicUtil(names, _UTIL);
|
||||
return new _CodegenState._(def.id, typeName, changeDetectorTypeName,
|
||||
def.strategy, records, def.directiveRecords,
|
||||
def.strategy, protoRecords, def.directiveRecords, logic, names,
|
||||
def.generateCheckNoChanges);
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,7 @@ class _CodegenState {
|
|||
${_genDeclareFields()}
|
||||
|
||||
$_changeDetectorTypeName(dispatcher, protos, directiveRecords)
|
||||
: super(${_encodeValue(_changeDetectorDefId)},
|
||||
: super(${codify(_changeDetectorDefId)},
|
||||
dispatcher, protos, directiveRecords, '$_changeDetectionMode') {
|
||||
dehydrateDirectives(false);
|
||||
}
|
||||
|
@ -284,7 +284,7 @@ class _CodegenState {
|
|||
var oldValue = _names.getFieldName(r.selfIndex);
|
||||
var newValue = _names.getLocalName(r.selfIndex);
|
||||
var read = '''
|
||||
${_genUpdateCurrentValue(r)}
|
||||
${_logic.genUpdateCurrentValue(r)}
|
||||
''';
|
||||
|
||||
var check = '''
|
||||
|
@ -303,7 +303,7 @@ class _CodegenState {
|
|||
var condition = r.args.map((a) => _names.getChangeName(a)).join(' || ');
|
||||
if (r.isUsedByOtherRecord()) {
|
||||
return 'if ($condition) { $genCode } else { $newValue = $oldValue; }';
|
||||
} else {
|
||||
} else {
|
||||
return 'if ($condition) { $genCode }';
|
||||
}
|
||||
} else {
|
||||
|
@ -311,85 +311,10 @@ class _CodegenState {
|
|||
}
|
||||
}
|
||||
|
||||
String _genUpdateCurrentValue(ProtoRecord r) {
|
||||
var context = r.contextIndex == -1
|
||||
? _names.getDirectiveName(r.directiveIndex)
|
||||
: _names.getLocalName(r.contextIndex);
|
||||
|
||||
var newValue = _names.getLocalName(r.selfIndex);
|
||||
var argString = r.args.map((arg) => _names.getLocalName(arg)).join(', ');
|
||||
|
||||
var rhs;
|
||||
switch (r.mode) {
|
||||
case RecordType.SELF:
|
||||
rhs = context;
|
||||
break;
|
||||
|
||||
case RecordType.CONST:
|
||||
rhs = _encodeValue(r.funcOrValue);
|
||||
break;
|
||||
|
||||
case RecordType.PROPERTY:
|
||||
rhs = '$context.${r.name}';
|
||||
break;
|
||||
|
||||
case RecordType.SAFE_PROPERTY:
|
||||
rhs = '${_UTIL}.isValueBlank(${context}) ? null : ${context}.${r.name}';
|
||||
break;
|
||||
|
||||
case RecordType.LOCAL:
|
||||
rhs = '${_names.getLocalsAccessorName()}.get("${r.name}")';
|
||||
break;
|
||||
|
||||
case RecordType.INVOKE_METHOD:
|
||||
rhs = '$context.${r.name}($argString)';
|
||||
break;
|
||||
|
||||
case RecordType.SAFE_INVOKE_METHOD:
|
||||
rhs = '${_UTIL}.isValueBlank(${context}) '
|
||||
'? null : ${context}.${r.name}(${argString})';
|
||||
break;
|
||||
|
||||
case RecordType.INVOKE_CLOSURE:
|
||||
rhs = '$context($argString)';
|
||||
break;
|
||||
|
||||
case RecordType.PRIMITIVE_OP:
|
||||
rhs = '$_UTIL.${r.name}($argString)';
|
||||
break;
|
||||
|
||||
case RecordType.COLLECTION_LITERAL:
|
||||
rhs = '$_UTIL.${r.name}($argString)';
|
||||
break;
|
||||
|
||||
case RecordType.INTERPOLATE:
|
||||
rhs = _genInterpolation(r);
|
||||
break;
|
||||
|
||||
case RecordType.KEYED_ACCESS:
|
||||
rhs = '$context[${_names.getLocalName(r.args[0])}]';
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new FormatException(
|
||||
'Unknown operation ${r.mode}', r.expressionAsString);
|
||||
}
|
||||
return '$newValue = $rhs;';
|
||||
}
|
||||
|
||||
String _genInterpolation(ProtoRecord r) {
|
||||
var res = new StringBuffer();
|
||||
for (var i = 0; i < r.args.length; ++i) {
|
||||
var name = _names.getLocalName(r.args[i]);
|
||||
res.write(
|
||||
'${_encodeValue(r.fixedArgs[i])} "\$\{$name == null ? "" : $name\}" ');
|
||||
}
|
||||
res.write(_encodeValue(r.fixedArgs[r.args.length]));
|
||||
return '$res';
|
||||
}
|
||||
|
||||
String _genChangeMarker(ProtoRecord r) {
|
||||
return r.argumentToPureFunction ? "${this._names.getChangeName(r.selfIndex)} = true;" : "";
|
||||
return r.argumentToPureFunction
|
||||
? "${this._names.getChangeName(r.selfIndex)} = true;"
|
||||
: "";
|
||||
}
|
||||
|
||||
String _genUpdateDirectiveOrElement(ProtoRecord r) {
|
||||
|
@ -438,7 +363,9 @@ class _CodegenState {
|
|||
String _maybeFirstInBinding(ProtoRecord r) {
|
||||
var prev = ChangeDetectionUtil.protoByIndex(_records, r.selfIndex - 1);
|
||||
var firstInBindng = prev == null || prev.bindingRecord != r.bindingRecord;
|
||||
return firstInBindng ? "${_names.getFirstProtoInCurrentBinding()} = ${r.selfIndex};" : '';
|
||||
return firstInBindng
|
||||
? "${_names.getFirstProtoInCurrentBinding()} = ${r.selfIndex};"
|
||||
: '';
|
||||
}
|
||||
|
||||
String _genAddToChanges(ProtoRecord r) {
|
||||
|
@ -485,9 +412,6 @@ class _CodegenState {
|
|||
}
|
||||
''';
|
||||
}
|
||||
|
||||
String _encodeValue(funcOrValue) =>
|
||||
JSON.encode(funcOrValue).replaceAll(r'$', r'\$');
|
||||
}
|
||||
|
||||
const PROTO_CHANGE_DETECTOR_FACTORY_METHOD = 'newProtoChangeDetector';
|
||||
|
|
|
@ -51,7 +51,7 @@ class _MyComponent_ChangeDetector0
|
|||
}
|
||||
if (c_myNum0) {
|
||||
l_interpolate1 =
|
||||
"Salad: " "${l_myNum0 == null ? "" : l_myNum0}" " is awesome";
|
||||
"${"Salad: "}${_gen.ChangeDetectionUtil.s(l_myNum0)}${" is awesome"}";
|
||||
if (_gen.looseNotIdentical(l_interpolate1, this.interpolate1)) {
|
||||
if (throwOnChange) {
|
||||
this.throwOnChangeError(this.interpolate1, l_interpolate1);
|
||||
|
|
Loading…
Reference in New Issue