feat(ChangeDetection): convert Record.mode to a bit field
This commit is contained in:
parent
ead2769dd6
commit
69af7ea2c0
@ -5,12 +5,21 @@ import {ClosureMap} from 'change_detection/parser/closure_map';
|
|||||||
|
|
||||||
var _fresh = new Object();
|
var _fresh = new Object();
|
||||||
|
|
||||||
export const PROTO_RECORD_CONST = 'const';
|
const RECORD_TYPE_MASK = 0x000f;
|
||||||
export const PROTO_RECORD_PURE_FUNCTION = 'func';
|
export const RECORD_TYPE_CONST = 0x0000;
|
||||||
export const PROTO_RECORD_CLOSURE = 'closure';
|
export const RECORD_TYPE_INVOKE_CLOSURE = 0x0001;
|
||||||
export const PROTO_RECORD_FORMATTTER = 'formatter';
|
export const RECORD_TYPE_INVOKE_FORMATTER = 0x0002;
|
||||||
export const PROTO_RECORD_METHOD = 'method';
|
export const RECORD_TYPE_INVOKE_METHOD = 0x0003;
|
||||||
export const PROTO_RECORD_PROPERTY = 'property';
|
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
|
* 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('prev:ProtoRecord')
|
||||||
@FIELD('recordInConstruction:Record')
|
@FIELD('recordInConstruction:Record')
|
||||||
constructor(recordRange:ProtoRecordRange,
|
constructor(recordRange:ProtoRecordRange,
|
||||||
recordType:string,
|
mode:int,
|
||||||
funcOrValue,
|
funcOrValue,
|
||||||
arity:int,
|
arity:int,
|
||||||
dest) {
|
dest) {
|
||||||
|
|
||||||
this.recordRange = recordRange;
|
this.recordRange = recordRange;
|
||||||
this.recordType = recordType;
|
this.mode = mode;
|
||||||
this.funcOrValue = funcOrValue;
|
this.funcOrValue = funcOrValue;
|
||||||
this.arity = arity;
|
this.arity = arity;
|
||||||
this.dest = dest;
|
this.dest = dest;
|
||||||
|
|
||||||
this.next = null;
|
this.next = null;
|
||||||
this.prev = null;
|
this.prev = null;
|
||||||
|
// The concrete Record instantiated from this ProtoRecord
|
||||||
this.recordInConstruction = null;
|
this.recordInConstruction = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,56 +103,65 @@ export class Record {
|
|||||||
this.prev = null;
|
this.prev = null;
|
||||||
this.nextEnabled = null;
|
this.nextEnabled = null;
|
||||||
this.prevEnabled = null;
|
this.prevEnabled = null;
|
||||||
this.disabled = false;
|
|
||||||
this.dest = null;
|
this.dest = null;
|
||||||
|
|
||||||
this.previousValue = null;
|
this.previousValue = null;
|
||||||
this.currentValue = _fresh;
|
this.currentValue = _fresh;
|
||||||
|
|
||||||
this.mode = null;
|
|
||||||
this.context = null;
|
this.context = null;
|
||||||
this.funcOrValue = null;
|
this.funcOrValue = null;
|
||||||
this.args = null;
|
this.args = null;
|
||||||
|
|
||||||
if (isBlank(protoRecord)) {
|
if (isBlank(protoRecord)) {
|
||||||
this.mode = MODE_STATE_MARKER;
|
this._mode = RECORD_TYPE_MARKER | RECORD_FLAG_DISABLED;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var type = protoRecord.recordType;
|
this._mode = protoRecord.mode;
|
||||||
if (type === PROTO_RECORD_CONST) {
|
|
||||||
this.mode = MODE_STATE_CONST;
|
var type = this.type;
|
||||||
|
|
||||||
|
if (type === RECORD_TYPE_CONST) {
|
||||||
this.funcOrValue = protoRecord.funcOrValue;
|
this.funcOrValue = protoRecord.funcOrValue;
|
||||||
|
|
||||||
} else if (type === PROTO_RECORD_PURE_FUNCTION) {
|
} else if (type === RECORD_TYPE_INVOKE_PURE_FUNCTION) {
|
||||||
this.mode = MODE_STATE_INVOKE_PURE_FUNCTION;
|
|
||||||
this.funcOrValue = protoRecord.funcOrValue;
|
this.funcOrValue = protoRecord.funcOrValue;
|
||||||
this.args = ListWrapper.createFixedSize(protoRecord.arity);
|
this.args = ListWrapper.createFixedSize(protoRecord.arity);
|
||||||
|
|
||||||
} else if (type === PROTO_RECORD_FORMATTTER) {
|
} else if (type === RECORD_TYPE_INVOKE_FORMATTER) {
|
||||||
this.mode = MODE_STATE_INVOKE_PURE_FUNCTION;
|
|
||||||
this.funcOrValue = MapWrapper.get(formatters, protoRecord.funcOrValue);
|
this.funcOrValue = MapWrapper.get(formatters, protoRecord.funcOrValue);
|
||||||
this.args = ListWrapper.createFixedSize(protoRecord.arity);
|
this.args = ListWrapper.createFixedSize(protoRecord.arity);
|
||||||
|
|
||||||
} else if (type === PROTO_RECORD_METHOD) {
|
} else if (type === RECORD_TYPE_INVOKE_METHOD) {
|
||||||
this.mode = MODE_STATE_INVOKE_METHOD;
|
|
||||||
this.funcOrValue = protoRecord.funcOrValue;
|
this.funcOrValue = protoRecord.funcOrValue;
|
||||||
this.args = ListWrapper.createFixedSize(protoRecord.arity);
|
this.args = ListWrapper.createFixedSize(protoRecord.arity);
|
||||||
|
|
||||||
} else if (type === PROTO_RECORD_CLOSURE) {
|
} else if (type === RECORD_TYPE_INVOKE_CLOSURE) {
|
||||||
this.mode = MODE_STATE_INVOKE_CLOSURE;
|
|
||||||
this.args = ListWrapper.createFixedSize(protoRecord.arity);
|
this.args = ListWrapper.createFixedSize(protoRecord.arity);
|
||||||
|
|
||||||
} else if (type === PROTO_RECORD_PROPERTY) {
|
} else if (type === RECORD_TYPE_PROPERTY) {
|
||||||
this.mode = MODE_STATE_PROPERTY;
|
|
||||||
this.funcOrValue = protoRecord.funcOrValue;
|
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) {
|
static createMarker(rr:RecordRange) {
|
||||||
var r = new Record(rr, null, null);
|
return new Record(rr, null, null);
|
||||||
r.disabled = true;
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
check():boolean {
|
check():boolean {
|
||||||
@ -171,36 +189,37 @@ export class Record {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_calculateNewValue() {
|
_calculateNewValue() {
|
||||||
var state = this.mode;
|
var type = this.type;
|
||||||
switch (state) {
|
switch (type) {
|
||||||
case MODE_STATE_PROPERTY:
|
case RECORD_TYPE_PROPERTY:
|
||||||
return this.funcOrValue(this.context);
|
return this.funcOrValue(this.context);
|
||||||
|
|
||||||
case MODE_STATE_INVOKE_METHOD:
|
case RECORD_TYPE_INVOKE_METHOD:
|
||||||
return this.funcOrValue(this.context, this.args);
|
return this.funcOrValue(this.context, this.args);
|
||||||
|
|
||||||
case MODE_STATE_INVOKE_CLOSURE:
|
case RECORD_TYPE_INVOKE_CLOSURE:
|
||||||
return FunctionWrapper.apply(this.context, this.args);
|
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);
|
this.recordRange.disableRecord(this);
|
||||||
return FunctionWrapper.apply(this.funcOrValue, this.args);
|
return FunctionWrapper.apply(this.funcOrValue, this.args);
|
||||||
|
|
||||||
case MODE_STATE_CONST:
|
case RECORD_TYPE_CONST:
|
||||||
this.recordRange.disableRecord(this);
|
this.recordRange.disableRecord(this);
|
||||||
return this.funcOrValue;
|
return this.funcOrValue;
|
||||||
|
|
||||||
case MODE_STATE_MARKER:
|
case RECORD_TYPE_MARKER:
|
||||||
throw new BaseException('MODE_STATE_MARKER not implemented');
|
throw new BaseException('Marker not implemented');
|
||||||
|
|
||||||
case MODE_STATE_MAP:
|
case RECORD_TYPE_MAP:
|
||||||
throw new BaseException('MODE_STATE_MAP not implemented');
|
throw new BaseException('Map not implemented');
|
||||||
|
|
||||||
case MODE_STATE_LIST:
|
case RECORD_TYPE_LIST:
|
||||||
throw new BaseException('MODE_STATE_LIST not implemented');
|
throw new BaseException('List not implemented');
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new BaseException('DEFAULT not implemented');
|
throw new BaseException(`Unsupported record type ($type)`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,35 +236,10 @@ export class Record {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get isMarkerRecord() {
|
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) {
|
function isSame(a, b) {
|
||||||
if (a === b) return true;
|
if (a === b) return true;
|
||||||
if ((a !== a) && (b !== b)) return true;
|
if ((a !== a) && (b !== b)) return true;
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
import {ProtoRecord, Record, PROTO_RECORD_CONST, PROTO_RECORD_PURE_FUNCTION,
|
import {
|
||||||
PROTO_RECORD_PROPERTY, PROTO_RECORD_METHOD, PROTO_RECORD_CLOSURE, PROTO_RECORD_FORMATTTER} from './record';
|
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 {FIELD, IMPLEMENTS, isBlank, isPresent, int, toBool, autoConvertAdd, BaseException} from 'facade/lang';
|
||||||
import {ListWrapper, MapWrapper} from 'facade/collection';
|
import {ListWrapper, MapWrapper} from 'facade/collection';
|
||||||
import {AST, AccessMember, ImplicitReceiver, AstVisitor, LiteralPrimitive,
|
import {AST, AccessMember, ImplicitReceiver, AstVisitor, LiteralPrimitive,
|
||||||
@ -282,30 +291,31 @@ class ProtoRecordCreator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitLiteralPrimitive(ast:LiteralPrimitive, dest) {
|
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) {
|
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.left.visit(this, new Destination(record, 0));
|
||||||
ast.right.visit(this, new Destination(record, 1));
|
ast.right.visit(this, new Destination(record, 1));
|
||||||
this.add(record);
|
this.add(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitPrefixNot(ast:PrefixNot, dest) {
|
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));
|
ast.expression.visit(this, new Destination(record, 0));
|
||||||
this.add(record);
|
this.add(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitAccessMember(ast:AccessMember, dest) {
|
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));
|
ast.receiver.visit(this, new Destination(record, null));
|
||||||
this.add(record);
|
this.add(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitFormatter(ast:Formatter, dest) {
|
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) {
|
for (var i = 0; i < ast.allArgs.length; ++i) {
|
||||||
ast.allArgs[i].visit(this, new Destination(record, i));
|
ast.allArgs[i].visit(this, new Destination(record, i));
|
||||||
}
|
}
|
||||||
@ -313,7 +323,7 @@ class ProtoRecordCreator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitMethodCall(ast:MethodCall, dest) {
|
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));
|
ast.receiver.visit(this, new Destination(record, null));
|
||||||
for (var i = 0; i < ast.args.length; ++i) {
|
for (var i = 0; i < ast.args.length; ++i) {
|
||||||
ast.args[i].visit(this, new Destination(record, i));
|
ast.args[i].visit(this, new Destination(record, i));
|
||||||
@ -322,7 +332,7 @@ class ProtoRecordCreator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitFunctionCall(ast:FunctionCall, dest) {
|
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));
|
ast.target.visit(this, new Destination(record, null));
|
||||||
for (var i = 0; i < ast.args.length; ++i) {
|
for (var i = 0; i < ast.args.length; ++i) {
|
||||||
ast.args[i].visit(this, new Destination(record, i));
|
ast.args[i].visit(this, new Destination(record, i));
|
||||||
@ -331,7 +341,7 @@ class ProtoRecordCreator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitConditional(ast:Conditional, dest) {
|
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.condition.visit(this, new Destination(record, 0));
|
||||||
ast.trueExp.visit(this, new Destination(record, 1));
|
ast.trueExp.visit(this, new Destination(record, 1));
|
||||||
ast.falseExp.visit(this, new Destination(record, 2));
|
ast.falseExp.visit(this, new Destination(record, 2));
|
||||||
@ -342,7 +352,7 @@ class ProtoRecordCreator {
|
|||||||
|
|
||||||
visitLiteralArray(ast:LiteralArray, dest) {
|
visitLiteralArray(ast:LiteralArray, dest) {
|
||||||
var length = ast.expressions.length;
|
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) {
|
for (var i = 0; i < length; ++i) {
|
||||||
ast.expressions[i].visit(this, new Destination(record, i));
|
ast.expressions[i].visit(this, new Destination(record, i));
|
||||||
}
|
}
|
||||||
@ -351,18 +361,18 @@ class ProtoRecordCreator {
|
|||||||
|
|
||||||
visitLiteralMap(ast:LiteralMap, dest) {
|
visitLiteralMap(ast:LiteralMap, dest) {
|
||||||
var length = ast.values.length;
|
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) {
|
for (var i = 0; i < length; ++i) {
|
||||||
ast.values[i].visit(this, new Destination(record, i));
|
ast.values[i].visit(this, new Destination(record, i));
|
||||||
}
|
}
|
||||||
this.add(record);
|
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){
|
createRecordsFromAST(ast:AST, memento){
|
||||||
ast.visit(this, memento);
|
ast.visit(this, memento);
|
||||||
@ -382,7 +392,7 @@ class ProtoRecordCreator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsupported() {
|
_unsupported() {
|
||||||
throw new BaseException("Unsupported");
|
throw new BaseException("Unsupported");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ export function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createRecord(rr) {
|
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', () => {
|
describe('record range', () => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user