Skip to content

Commit 69af7ea

Browse files
committed
feat(ChangeDetection): convert Record.mode to a bit field
1 parent ead2769 commit 69af7ea

File tree

3 files changed

+91
-87
lines changed

3 files changed

+91
-87
lines changed

modules/change_detection/src/record.js

Lines changed: 62 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,21 @@ import {ClosureMap} from 'change_detection/parser/closure_map';
55

66
var _fresh = new Object();
77

8-
export const PROTO_RECORD_CONST = 'const';
9-
export const PROTO_RECORD_PURE_FUNCTION = 'func';
10-
export const PROTO_RECORD_CLOSURE = 'closure';
11-
export const PROTO_RECORD_FORMATTTER = 'formatter';
12-
export const PROTO_RECORD_METHOD = 'method';
13-
export const PROTO_RECORD_PROPERTY = 'property';
8+
const RECORD_TYPE_MASK = 0x000f;
9+
export const RECORD_TYPE_CONST = 0x0000;
10+
export const RECORD_TYPE_INVOKE_CLOSURE = 0x0001;
11+
export const RECORD_TYPE_INVOKE_FORMATTER = 0x0002;
12+
export const RECORD_TYPE_INVOKE_METHOD = 0x0003;
13+
export const RECORD_TYPE_INVOKE_PURE_FUNCTION = 0x0004;
14+
export const RECORD_TYPE_LIST = 0x0005;
15+
export const RECORD_TYPE_MAP = 0x0006;
16+
export const RECORD_TYPE_MARKER = 0x0007;
17+
export const RECORD_TYPE_PROPERTY = 0x0008;
18+
19+
const RECORD_FLAG_DISABLED = 0x0100;
20+
export const RECORD_FLAG_IMPLICIT_RECEIVER = 0x0200;
21+
22+
1423

1524
/**
1625
* For now we are dropping expression coalescence. We can always add it later, but
@@ -27,20 +36,20 @@ export class ProtoRecord {
2736
@FIELD('prev:ProtoRecord')
2837
@FIELD('recordInConstruction:Record')
2938
constructor(recordRange:ProtoRecordRange,
30-
recordType:string,
39+
mode:int,
3140
funcOrValue,
3241
arity:int,
3342
dest) {
3443

3544
this.recordRange = recordRange;
36-
this.recordType = recordType;
45+
this.mode = mode;
3746
this.funcOrValue = funcOrValue;
3847
this.arity = arity;
3948
this.dest = dest;
4049

4150
this.next = null;
4251
this.prev = null;
43-
52+
// The concrete Record instantiated from this ProtoRecord
4453
this.recordInConstruction = null;
4554
}
4655
}
@@ -94,56 +103,65 @@ export class Record {
94103
this.prev = null;
95104
this.nextEnabled = null;
96105
this.prevEnabled = null;
97-
this.disabled = false;
98106
this.dest = null;
99107

100108
this.previousValue = null;
101109
this.currentValue = _fresh;
102110

103-
this.mode = null;
104111
this.context = null;
105112
this.funcOrValue = null;
106113
this.args = null;
107114

108115
if (isBlank(protoRecord)) {
109-
this.mode = MODE_STATE_MARKER;
116+
this._mode = RECORD_TYPE_MARKER | RECORD_FLAG_DISABLED;
110117
return;
111118
}
112119

113-
var type = protoRecord.recordType;
114-
if (type === PROTO_RECORD_CONST) {
115-
this.mode = MODE_STATE_CONST;
120+
this._mode = protoRecord.mode;
121+
122+
var type = this.type;
123+
124+
if (type === RECORD_TYPE_CONST) {
116125
this.funcOrValue = protoRecord.funcOrValue;
117126

118-
} else if (type === PROTO_RECORD_PURE_FUNCTION) {
119-
this.mode = MODE_STATE_INVOKE_PURE_FUNCTION;
127+
} else if (type === RECORD_TYPE_INVOKE_PURE_FUNCTION) {
120128
this.funcOrValue = protoRecord.funcOrValue;
121129
this.args = ListWrapper.createFixedSize(protoRecord.arity);
122130

123-
} else if (type === PROTO_RECORD_FORMATTTER) {
124-
this.mode = MODE_STATE_INVOKE_PURE_FUNCTION;
131+
} else if (type === RECORD_TYPE_INVOKE_FORMATTER) {
125132
this.funcOrValue = MapWrapper.get(formatters, protoRecord.funcOrValue);
126133
this.args = ListWrapper.createFixedSize(protoRecord.arity);
127134

128-
} else if (type === PROTO_RECORD_METHOD) {
129-
this.mode = MODE_STATE_INVOKE_METHOD;
135+
} else if (type === RECORD_TYPE_INVOKE_METHOD) {
130136
this.funcOrValue = protoRecord.funcOrValue;
131137
this.args = ListWrapper.createFixedSize(protoRecord.arity);
132138

133-
} else if (type === PROTO_RECORD_CLOSURE) {
134-
this.mode = MODE_STATE_INVOKE_CLOSURE;
139+
} else if (type === RECORD_TYPE_INVOKE_CLOSURE) {
135140
this.args = ListWrapper.createFixedSize(protoRecord.arity);
136141

137-
} else if (type === PROTO_RECORD_PROPERTY) {
138-
this.mode = MODE_STATE_PROPERTY;
142+
} else if (type === RECORD_TYPE_PROPERTY) {
139143
this.funcOrValue = protoRecord.funcOrValue;
140144
}
141145
}
142146

147+
get type() {
148+
return this._mode & RECORD_TYPE_MASK;
149+
}
150+
151+
get disabled() {
152+
return (this._mode & RECORD_FLAG_DISABLED) === RECORD_FLAG_DISABLED;
153+
}
154+
155+
set disabled(value) {
156+
if (value) {
157+
this._mode |= RECORD_FLAG_DISABLED;
158+
} else {
159+
this._mode &= ~RECORD_FLAG_DISABLED;
160+
}
161+
}
162+
143163
static createMarker(rr:RecordRange) {
144-
var r = new Record(rr, null, null);
145-
r.disabled = true;
146-
return r;
164+
return new Record(rr, null, null);
147165
}
148166

149167
check():boolean {
@@ -171,36 +189,37 @@ export class Record {
171189
}
172190

173191
_calculateNewValue() {
174-
var state = this.mode;
175-
switch (state) {
176-
case MODE_STATE_PROPERTY:
192+
var type = this.type;
193+
switch (type) {
194+
case RECORD_TYPE_PROPERTY:
177195
return this.funcOrValue(this.context);
178196

179-
case MODE_STATE_INVOKE_METHOD:
197+
case RECORD_TYPE_INVOKE_METHOD:
180198
return this.funcOrValue(this.context, this.args);
181199

182-
case MODE_STATE_INVOKE_CLOSURE:
200+
case RECORD_TYPE_INVOKE_CLOSURE:
183201
return FunctionWrapper.apply(this.context, this.args);
184202

185-
case MODE_STATE_INVOKE_PURE_FUNCTION:
203+
case RECORD_TYPE_INVOKE_PURE_FUNCTION:
204+
case RECORD_TYPE_INVOKE_FORMATTER:
186205
this.recordRange.disableRecord(this);
187206
return FunctionWrapper.apply(this.funcOrValue, this.args);
188207

189-
case MODE_STATE_CONST:
208+
case RECORD_TYPE_CONST:
190209
this.recordRange.disableRecord(this);
191210
return this.funcOrValue;
192211

193-
case MODE_STATE_MARKER:
194-
throw new BaseException('MODE_STATE_MARKER not implemented');
212+
case RECORD_TYPE_MARKER:
213+
throw new BaseException('Marker not implemented');
195214

196-
case MODE_STATE_MAP:
197-
throw new BaseException('MODE_STATE_MAP not implemented');
215+
case RECORD_TYPE_MAP:
216+
throw new BaseException('Map not implemented');
198217

199-
case MODE_STATE_LIST:
200-
throw new BaseException('MODE_STATE_LIST not implemented');
218+
case RECORD_TYPE_LIST:
219+
throw new BaseException('List not implemented');
201220

202221
default:
203-
throw new BaseException('DEFAULT not implemented');
222+
throw new BaseException(`Unsupported record type ($type)`);
204223
}
205224
}
206225

@@ -217,35 +236,10 @@ export class Record {
217236
}
218237

219238
get isMarkerRecord() {
220-
return this.mode == MODE_STATE_MARKER;
239+
return this.type == RECORD_TYPE_MARKER;
221240
}
222241
}
223242

224-
// The mode is divided into two parts. Which notification mechanism
225-
// to use and which dereference mode to execute.
226-
227-
// We use dirty checking aka no notification
228-
const MODE_MASK_NOTIFY = 0xFF00;
229-
// Encodes the state of dereference
230-
const MODE_MASK_STATE = 0x00FF;
231-
232-
const MODE_PLUGIN_DIRTY_CHECK = 0x0000;
233-
const MODE_STATE_MARKER = 0x0000;
234-
235-
/// _context[_protoRecord.propname] => _getter(_context)
236-
const MODE_STATE_PROPERTY = 0x0001;
237-
const MODE_STATE_INVOKE_PURE_FUNCTION = 0x0002;
238-
const MODE_STATE_INVOKE_METHOD = 0x0003;
239-
const MODE_STATE_INVOKE_CLOSURE = 0x0004;
240-
241-
/// _context is Map => _previousValue is MapChangeRecord
242-
const MODE_STATE_MAP = 0x0005;
243-
/// _context is Array/List/Iterable => _previousValue = ListChangeRecord
244-
const MODE_STATE_LIST = 0x0006;
245-
246-
/// _context is number/string
247-
const MODE_STATE_CONST = 0x0007;
248-
249243
function isSame(a, b) {
250244
if (a === b) return true;
251245
if ((a !== a) && (b !== b)) return true;

modules/change_detection/src/record_range.js

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
1-
import {ProtoRecord, Record, PROTO_RECORD_CONST, PROTO_RECORD_PURE_FUNCTION,
2-
PROTO_RECORD_PROPERTY, PROTO_RECORD_METHOD, PROTO_RECORD_CLOSURE, PROTO_RECORD_FORMATTTER} from './record';
1+
import {
2+
ProtoRecord,
3+
Record,
4+
RECORD_TYPE_CONST,
5+
RECORD_TYPE_INVOKE_CLOSURE,
6+
RECORD_TYPE_INVOKE_FORMATTER,
7+
RECORD_TYPE_INVOKE_METHOD,
8+
RECORD_TYPE_INVOKE_PURE_FUNCTION,
9+
RECORD_TYPE_PROPERTY
10+
} from './record';
11+
312
import {FIELD, IMPLEMENTS, isBlank, isPresent, int, toBool, autoConvertAdd, BaseException} from 'facade/lang';
413
import {ListWrapper, MapWrapper} from 'facade/collection';
514
import {AST, AccessMember, ImplicitReceiver, AstVisitor, LiteralPrimitive,
@@ -278,42 +287,43 @@ class ProtoRecordCreator {
278287
}
279288

280289
visitImplicitReceiver(ast:ImplicitReceiver, args) {
281-
//do nothing
290+
// do nothing
282291
}
283292

284293
visitLiteralPrimitive(ast:LiteralPrimitive, dest) {
285-
this.add(this.construct(PROTO_RECORD_CONST, ast.value, 0, dest));
294+
this.add(this.construct(RECORD_TYPE_CONST, ast.value, 0, dest));
286295
}
287296

288297
visitBinary(ast:Binary, dest) {
289-
var record = this.construct(PROTO_RECORD_PURE_FUNCTION, _operationToFunction(ast.operation), 2, dest);
298+
var record = this.construct(RECORD_TYPE_INVOKE_PURE_FUNCTION,
299+
_operationToFunction(ast.operation), 2, dest);
290300
ast.left.visit(this, new Destination(record, 0));
291301
ast.right.visit(this, new Destination(record, 1));
292302
this.add(record);
293303
}
294304

295305
visitPrefixNot(ast:PrefixNot, dest) {
296-
var record = this.construct(PROTO_RECORD_PURE_FUNCTION, _operation_negate, 1, dest);
306+
var record = this.construct(RECORD_TYPE_INVOKE_PURE_FUNCTION, _operation_negate, 1, dest);
297307
ast.expression.visit(this, new Destination(record, 0));
298308
this.add(record);
299309
}
300310

301311
visitAccessMember(ast:AccessMember, dest) {
302-
var record = this.construct(PROTO_RECORD_PROPERTY, ast.getter, 0, dest);
312+
var record = this.construct(RECORD_TYPE_PROPERTY, ast.getter, 0, dest);
303313
ast.receiver.visit(this, new Destination(record, null));
304314
this.add(record);
305315
}
306316

307317
visitFormatter(ast:Formatter, dest) {
308-
var record = this.construct(PROTO_RECORD_FORMATTTER, ast.name, ast.allArgs.length, dest);
318+
var record = this.construct(RECORD_TYPE_INVOKE_FORMATTER, ast.name, ast.allArgs.length, dest);
309319
for (var i = 0; i < ast.allArgs.length; ++i) {
310320
ast.allArgs[i].visit(this, new Destination(record, i));
311321
}
312322
this.add(record);
313323
}
314324

315325
visitMethodCall(ast:MethodCall, dest) {
316-
var record = this.construct(PROTO_RECORD_METHOD, ast.fn, ast.args.length, dest);
326+
var record = this.construct(RECORD_TYPE_INVOKE_METHOD, ast.fn, ast.args.length, dest);
317327
ast.receiver.visit(this, new Destination(record, null));
318328
for (var i = 0; i < ast.args.length; ++i) {
319329
ast.args[i].visit(this, new Destination(record, i));
@@ -322,7 +332,7 @@ class ProtoRecordCreator {
322332
}
323333

324334
visitFunctionCall(ast:FunctionCall, dest) {
325-
var record = this.construct(PROTO_RECORD_CLOSURE, null, ast.args.length, dest);
335+
var record = this.construct(RECORD_TYPE_INVOKE_CLOSURE, null, ast.args.length, dest);
326336
ast.target.visit(this, new Destination(record, null));
327337
for (var i = 0; i < ast.args.length; ++i) {
328338
ast.args[i].visit(this, new Destination(record, i));
@@ -331,7 +341,7 @@ class ProtoRecordCreator {
331341
}
332342

333343
visitConditional(ast:Conditional, dest) {
334-
var record = this.construct(PROTO_RECORD_PURE_FUNCTION, _cond, 3, dest);
344+
var record = this.construct(RECORD_TYPE_INVOKE_PURE_FUNCTION, _cond, 3, dest);
335345
ast.condition.visit(this, new Destination(record, 0));
336346
ast.trueExp.visit(this, new Destination(record, 1));
337347
ast.falseExp.visit(this, new Destination(record, 2));
@@ -342,7 +352,7 @@ class ProtoRecordCreator {
342352

343353
visitLiteralArray(ast:LiteralArray, dest) {
344354
var length = ast.expressions.length;
345-
var record = this.construct(PROTO_RECORD_PURE_FUNCTION, _arrayFn(length), length, dest);
355+
var record = this.construct(RECORD_TYPE_INVOKE_PURE_FUNCTION, _arrayFn(length), length, dest);
346356
for (var i = 0; i < length; ++i) {
347357
ast.expressions[i].visit(this, new Destination(record, i));
348358
}
@@ -351,18 +361,18 @@ class ProtoRecordCreator {
351361

352362
visitLiteralMap(ast:LiteralMap, dest) {
353363
var length = ast.values.length;
354-
var record = this.construct(PROTO_RECORD_PURE_FUNCTION, _mapFn(ast.keys, length), length, dest);
364+
var record = this.construct(RECORD_TYPE_INVOKE_PURE_FUNCTION, _mapFn(ast.keys, length), length, dest);
355365
for (var i = 0; i < length; ++i) {
356366
ast.values[i].visit(this, new Destination(record, i));
357367
}
358368
this.add(record);
359369
}
360370

361-
visitChain(ast:Chain, dest){this.unsupported();}
371+
visitChain(ast:Chain, dest){this._unsupported();}
362372

363-
visitAssignment(ast:Assignment, dest) {this.unsupported();}
373+
visitAssignment(ast:Assignment, dest) {this._unsupported();}
364374

365-
visitTemplateBindings(ast, dest) {this.unsupported();}
375+
visitTemplateBindings(ast, dest) {this._unsupported();}
366376

367377
createRecordsFromAST(ast:AST, memento){
368378
ast.visit(this, memento);
@@ -382,7 +392,7 @@ class ProtoRecordCreator {
382392
}
383393
}
384394

385-
unsupported() {
395+
_unsupported() {
386396
throw new BaseException("Unsupported");
387397
}
388398
}

modules/change_detection/test/record_range_spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export function main() {
3232
}
3333

3434
function createRecord(rr) {
35-
return new Record(rr, new ProtoRecord(null, null, null, null, null), null);
35+
return new Record(rr, new ProtoRecord(null, 0, null, null, null), null);
3636
}
3737

3838
describe('record range', () => {
@@ -262,4 +262,4 @@ export function main() {
262262
});
263263
});
264264
});
265-
}
265+
}

0 commit comments

Comments
 (0)