From 69af7ea2c0edabf1e57958589a607085dec5d57b Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Mon, 24 Nov 2014 12:28:54 +0100 Subject: [PATCH] feat(ChangeDetection): convert Record.mode to a bit field --- modules/change_detection/src/record.js | 130 +++++++++--------- modules/change_detection/src/record_range.js | 44 +++--- .../test/record_range_spec.js | 4 +- 3 files changed, 91 insertions(+), 87 deletions(-) diff --git a/modules/change_detection/src/record.js b/modules/change_detection/src/record.js index d8e8f1df69..2911236ac0 100644 --- a/modules/change_detection/src/record.js +++ b/modules/change_detection/src/record.js @@ -5,12 +5,21 @@ import {ClosureMap} from 'change_detection/parser/closure_map'; var _fresh = new Object(); -export const PROTO_RECORD_CONST = 'const'; -export const PROTO_RECORD_PURE_FUNCTION = 'func'; -export const PROTO_RECORD_CLOSURE = 'closure'; -export const PROTO_RECORD_FORMATTTER = 'formatter'; -export const PROTO_RECORD_METHOD = 'method'; -export const PROTO_RECORD_PROPERTY = 'property'; +const RECORD_TYPE_MASK = 0x000f; +export const RECORD_TYPE_CONST = 0x0000; +export const RECORD_TYPE_INVOKE_CLOSURE = 0x0001; +export const RECORD_TYPE_INVOKE_FORMATTER = 0x0002; +export const RECORD_TYPE_INVOKE_METHOD = 0x0003; +export const RECORD_TYPE_INVOKE_PURE_FUNCTION = 0x0004; +export const RECORD_TYPE_LIST = 0x0005; +export const RECORD_TYPE_MAP = 0x0006; +export const RECORD_TYPE_MARKER = 0x0007; +export const RECORD_TYPE_PROPERTY = 0x0008; + +const RECORD_FLAG_DISABLED = 0x0100; +export const RECORD_FLAG_IMPLICIT_RECEIVER = 0x0200; + + /** * For now we are dropping expression coalescence. We can always add it later, but @@ -27,20 +36,20 @@ export class ProtoRecord { @FIELD('prev:ProtoRecord') @FIELD('recordInConstruction:Record') constructor(recordRange:ProtoRecordRange, - recordType:string, + mode:int, funcOrValue, arity:int, dest) { this.recordRange = recordRange; - this.recordType = recordType; + this.mode = mode; this.funcOrValue = funcOrValue; this.arity = arity; this.dest = dest; this.next = null; this.prev = null; - + // The concrete Record instantiated from this ProtoRecord this.recordInConstruction = null; } } @@ -94,56 +103,65 @@ export class Record { this.prev = null; this.nextEnabled = null; this.prevEnabled = null; - this.disabled = false; this.dest = null; this.previousValue = null; this.currentValue = _fresh; - this.mode = null; this.context = null; this.funcOrValue = null; this.args = null; if (isBlank(protoRecord)) { - this.mode = MODE_STATE_MARKER; + this._mode = RECORD_TYPE_MARKER | RECORD_FLAG_DISABLED; return; } - var type = protoRecord.recordType; - if (type === PROTO_RECORD_CONST) { - this.mode = MODE_STATE_CONST; + this._mode = protoRecord.mode; + + var type = this.type; + + if (type === RECORD_TYPE_CONST) { this.funcOrValue = protoRecord.funcOrValue; - } else if (type === PROTO_RECORD_PURE_FUNCTION) { - this.mode = MODE_STATE_INVOKE_PURE_FUNCTION; + } else if (type === RECORD_TYPE_INVOKE_PURE_FUNCTION) { this.funcOrValue = protoRecord.funcOrValue; this.args = ListWrapper.createFixedSize(protoRecord.arity); - } else if (type === PROTO_RECORD_FORMATTTER) { - this.mode = MODE_STATE_INVOKE_PURE_FUNCTION; + } else if (type === RECORD_TYPE_INVOKE_FORMATTER) { this.funcOrValue = MapWrapper.get(formatters, protoRecord.funcOrValue); this.args = ListWrapper.createFixedSize(protoRecord.arity); - } else if (type === PROTO_RECORD_METHOD) { - this.mode = MODE_STATE_INVOKE_METHOD; + } else if (type === RECORD_TYPE_INVOKE_METHOD) { this.funcOrValue = protoRecord.funcOrValue; this.args = ListWrapper.createFixedSize(protoRecord.arity); - } else if (type === PROTO_RECORD_CLOSURE) { - this.mode = MODE_STATE_INVOKE_CLOSURE; + } else if (type === RECORD_TYPE_INVOKE_CLOSURE) { this.args = ListWrapper.createFixedSize(protoRecord.arity); - } else if (type === PROTO_RECORD_PROPERTY) { - this.mode = MODE_STATE_PROPERTY; + } else if (type === RECORD_TYPE_PROPERTY) { this.funcOrValue = protoRecord.funcOrValue; } } + get type() { + return this._mode & RECORD_TYPE_MASK; + } + + get disabled() { + return (this._mode & RECORD_FLAG_DISABLED) === RECORD_FLAG_DISABLED; + } + + set disabled(value) { + if (value) { + this._mode |= RECORD_FLAG_DISABLED; + } else { + this._mode &= ~RECORD_FLAG_DISABLED; + } + } + static createMarker(rr:RecordRange) { - var r = new Record(rr, null, null); - r.disabled = true; - return r; + return new Record(rr, null, null); } check():boolean { @@ -171,36 +189,37 @@ export class Record { } _calculateNewValue() { - var state = this.mode; - switch (state) { - case MODE_STATE_PROPERTY: + var type = this.type; + switch (type) { + case RECORD_TYPE_PROPERTY: return this.funcOrValue(this.context); - case MODE_STATE_INVOKE_METHOD: + case RECORD_TYPE_INVOKE_METHOD: return this.funcOrValue(this.context, this.args); - case MODE_STATE_INVOKE_CLOSURE: + case RECORD_TYPE_INVOKE_CLOSURE: return FunctionWrapper.apply(this.context, this.args); - case MODE_STATE_INVOKE_PURE_FUNCTION: + case RECORD_TYPE_INVOKE_PURE_FUNCTION: + case RECORD_TYPE_INVOKE_FORMATTER: this.recordRange.disableRecord(this); return FunctionWrapper.apply(this.funcOrValue, this.args); - case MODE_STATE_CONST: + case RECORD_TYPE_CONST: this.recordRange.disableRecord(this); return this.funcOrValue; - case MODE_STATE_MARKER: - throw new BaseException('MODE_STATE_MARKER not implemented'); + case RECORD_TYPE_MARKER: + throw new BaseException('Marker not implemented'); - case MODE_STATE_MAP: - throw new BaseException('MODE_STATE_MAP not implemented'); + case RECORD_TYPE_MAP: + throw new BaseException('Map not implemented'); - case MODE_STATE_LIST: - throw new BaseException('MODE_STATE_LIST not implemented'); + case RECORD_TYPE_LIST: + throw new BaseException('List not implemented'); default: - throw new BaseException('DEFAULT not implemented'); + throw new BaseException(`Unsupported record type ($type)`); } } @@ -217,35 +236,10 @@ export class Record { } get isMarkerRecord() { - return this.mode == MODE_STATE_MARKER; + return this.type == RECORD_TYPE_MARKER; } } -// The mode is divided into two parts. Which notification mechanism -// to use and which dereference mode to execute. - -// We use dirty checking aka no notification -const MODE_MASK_NOTIFY = 0xFF00; -// Encodes the state of dereference -const MODE_MASK_STATE = 0x00FF; - -const MODE_PLUGIN_DIRTY_CHECK = 0x0000; -const MODE_STATE_MARKER = 0x0000; - -/// _context[_protoRecord.propname] => _getter(_context) -const MODE_STATE_PROPERTY = 0x0001; -const MODE_STATE_INVOKE_PURE_FUNCTION = 0x0002; -const MODE_STATE_INVOKE_METHOD = 0x0003; -const MODE_STATE_INVOKE_CLOSURE = 0x0004; - -/// _context is Map => _previousValue is MapChangeRecord -const MODE_STATE_MAP = 0x0005; -/// _context is Array/List/Iterable => _previousValue = ListChangeRecord -const MODE_STATE_LIST = 0x0006; - -/// _context is number/string -const MODE_STATE_CONST = 0x0007; - function isSame(a, b) { if (a === b) return true; if ((a !== a) && (b !== b)) return true; diff --git a/modules/change_detection/src/record_range.js b/modules/change_detection/src/record_range.js index d686c157c7..8de73aa024 100644 --- a/modules/change_detection/src/record_range.js +++ b/modules/change_detection/src/record_range.js @@ -1,5 +1,14 @@ -import {ProtoRecord, Record, PROTO_RECORD_CONST, PROTO_RECORD_PURE_FUNCTION, - PROTO_RECORD_PROPERTY, PROTO_RECORD_METHOD, PROTO_RECORD_CLOSURE, PROTO_RECORD_FORMATTTER} from './record'; +import { + ProtoRecord, + Record, + RECORD_TYPE_CONST, + RECORD_TYPE_INVOKE_CLOSURE, + RECORD_TYPE_INVOKE_FORMATTER, + RECORD_TYPE_INVOKE_METHOD, + RECORD_TYPE_INVOKE_PURE_FUNCTION, + RECORD_TYPE_PROPERTY +} from './record'; + import {FIELD, IMPLEMENTS, isBlank, isPresent, int, toBool, autoConvertAdd, BaseException} from 'facade/lang'; import {ListWrapper, MapWrapper} from 'facade/collection'; import {AST, AccessMember, ImplicitReceiver, AstVisitor, LiteralPrimitive, @@ -278,34 +287,35 @@ class ProtoRecordCreator { } visitImplicitReceiver(ast:ImplicitReceiver, args) { - //do nothing + // do nothing } visitLiteralPrimitive(ast:LiteralPrimitive, dest) { - this.add(this.construct(PROTO_RECORD_CONST, ast.value, 0, dest)); + this.add(this.construct(RECORD_TYPE_CONST, ast.value, 0, dest)); } visitBinary(ast:Binary, dest) { - var record = this.construct(PROTO_RECORD_PURE_FUNCTION, _operationToFunction(ast.operation), 2, dest); + var record = this.construct(RECORD_TYPE_INVOKE_PURE_FUNCTION, + _operationToFunction(ast.operation), 2, dest); ast.left.visit(this, new Destination(record, 0)); ast.right.visit(this, new Destination(record, 1)); this.add(record); } visitPrefixNot(ast:PrefixNot, dest) { - var record = this.construct(PROTO_RECORD_PURE_FUNCTION, _operation_negate, 1, dest); + var record = this.construct(RECORD_TYPE_INVOKE_PURE_FUNCTION, _operation_negate, 1, dest); ast.expression.visit(this, new Destination(record, 0)); this.add(record); } visitAccessMember(ast:AccessMember, dest) { - var record = this.construct(PROTO_RECORD_PROPERTY, ast.getter, 0, dest); + var record = this.construct(RECORD_TYPE_PROPERTY, ast.getter, 0, dest); ast.receiver.visit(this, new Destination(record, null)); this.add(record); } visitFormatter(ast:Formatter, dest) { - var record = this.construct(PROTO_RECORD_FORMATTTER, ast.name, ast.allArgs.length, dest); + var record = this.construct(RECORD_TYPE_INVOKE_FORMATTER, ast.name, ast.allArgs.length, dest); for (var i = 0; i < ast.allArgs.length; ++i) { ast.allArgs[i].visit(this, new Destination(record, i)); } @@ -313,7 +323,7 @@ class ProtoRecordCreator { } visitMethodCall(ast:MethodCall, dest) { - var record = this.construct(PROTO_RECORD_METHOD, ast.fn, ast.args.length, dest); + var record = this.construct(RECORD_TYPE_INVOKE_METHOD, ast.fn, ast.args.length, dest); ast.receiver.visit(this, new Destination(record, null)); for (var i = 0; i < ast.args.length; ++i) { ast.args[i].visit(this, new Destination(record, i)); @@ -322,7 +332,7 @@ class ProtoRecordCreator { } visitFunctionCall(ast:FunctionCall, dest) { - var record = this.construct(PROTO_RECORD_CLOSURE, null, ast.args.length, dest); + var record = this.construct(RECORD_TYPE_INVOKE_CLOSURE, null, ast.args.length, dest); ast.target.visit(this, new Destination(record, null)); for (var i = 0; i < ast.args.length; ++i) { ast.args[i].visit(this, new Destination(record, i)); @@ -331,7 +341,7 @@ class ProtoRecordCreator { } visitConditional(ast:Conditional, dest) { - var record = this.construct(PROTO_RECORD_PURE_FUNCTION, _cond, 3, dest); + var record = this.construct(RECORD_TYPE_INVOKE_PURE_FUNCTION, _cond, 3, dest); ast.condition.visit(this, new Destination(record, 0)); ast.trueExp.visit(this, new Destination(record, 1)); ast.falseExp.visit(this, new Destination(record, 2)); @@ -342,7 +352,7 @@ class ProtoRecordCreator { visitLiteralArray(ast:LiteralArray, dest) { var length = ast.expressions.length; - var record = this.construct(PROTO_RECORD_PURE_FUNCTION, _arrayFn(length), length, dest); + var record = this.construct(RECORD_TYPE_INVOKE_PURE_FUNCTION, _arrayFn(length), length, dest); for (var i = 0; i < length; ++i) { ast.expressions[i].visit(this, new Destination(record, i)); } @@ -351,18 +361,18 @@ class ProtoRecordCreator { visitLiteralMap(ast:LiteralMap, dest) { var length = ast.values.length; - var record = this.construct(PROTO_RECORD_PURE_FUNCTION, _mapFn(ast.keys, length), length, dest); + var record = this.construct(RECORD_TYPE_INVOKE_PURE_FUNCTION, _mapFn(ast.keys, length), length, dest); for (var i = 0; i < length; ++i) { ast.values[i].visit(this, new Destination(record, i)); } this.add(record); } - visitChain(ast:Chain, dest){this.unsupported();} + visitChain(ast:Chain, dest){this._unsupported();} - visitAssignment(ast:Assignment, dest) {this.unsupported();} + visitAssignment(ast:Assignment, dest) {this._unsupported();} - visitTemplateBindings(ast, dest) {this.unsupported();} + visitTemplateBindings(ast, dest) {this._unsupported();} createRecordsFromAST(ast:AST, memento){ ast.visit(this, memento); @@ -382,7 +392,7 @@ class ProtoRecordCreator { } } - unsupported() { + _unsupported() { throw new BaseException("Unsupported"); } } diff --git a/modules/change_detection/test/record_range_spec.js b/modules/change_detection/test/record_range_spec.js index e1f5ab64c9..19a01ee4b7 100644 --- a/modules/change_detection/test/record_range_spec.js +++ b/modules/change_detection/test/record_range_spec.js @@ -32,7 +32,7 @@ export function main() { } function createRecord(rr) { - return new Record(rr, new ProtoRecord(null, null, null, null, null), null); + return new Record(rr, new ProtoRecord(null, 0, null, null, null), null); } describe('record range', () => { @@ -262,4 +262,4 @@ export function main() { }); }); }); -} \ No newline at end of file +}