Skip to content

Commit a3cb09e

Browse files
committed
Use a map to back meta's cache.
This is a first step to making the baking store of `Meta`'s `readableCache` / `writableCache` swappable. The first implementation is using a simple array of key value pairs, but since we are using the `set`, `get`, `has`, `delete` interface we can change the backing data store at will (or even "upgrade" at runtime). The underlying implementation of the "lodash-stack" is mostly copied from lodash itself (with inline attribution), but in the longer term we should simply use this from lodash itself (and embed these modules from lodash-es in our build process).
1 parent d801dc3 commit a3cb09e

File tree

6 files changed

+116
-28
lines changed

6 files changed

+116
-28
lines changed

packages/ember-metal/lib/chains.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,8 @@ function lazyGet(obj, key) {
182182
// Otherwise attempt to get the cached value of the computed property
183183
} else {
184184
let cache = meta.readableCache();
185-
if (cache && key in cache) {
186-
return cache[key];
185+
if (cache && cache.has(key)) {
186+
return cache.get(key);
187187
}
188188
}
189189
}

packages/ember-metal/lib/computed.js

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -317,8 +317,8 @@ ComputedPropertyPrototype.didChange = function(obj, keyName) {
317317
}
318318

319319
let cache = meta.readableCache();
320-
if (cache && cache[keyName] !== undefined) {
321-
cache[keyName] = undefined;
320+
if (cache && cache.get(keyName) !== undefined) {
321+
cache.set(keyName, undefined);
322322
removeDependentKeys(this, obj, keyName, meta);
323323
}
324324
};
@@ -330,8 +330,8 @@ ComputedPropertyPrototype.get = function(obj, keyName) {
330330

331331
let meta = metaFor(obj);
332332
let cache = meta.writableCache();
333+
let result = cache.get(keyName);
333334

334-
let result = cache[keyName];
335335
if (result === UNDEFINED) {
336336
return undefined;
337337
} else if (result !== undefined) {
@@ -340,9 +340,9 @@ ComputedPropertyPrototype.get = function(obj, keyName) {
340340

341341
let ret = this._getter.call(obj, keyName);
342342
if (ret === undefined) {
343-
cache[keyName] = UNDEFINED;
343+
cache.set(keyName, UNDEFINED);
344344
} else {
345-
cache[keyName] = ret;
345+
cache.set(keyName, ret);
346346
}
347347

348348
let chainWatchers = meta.readableChainWatchers();
@@ -401,10 +401,12 @@ ComputedPropertyPrototype._set = function computedPropertySet(obj, keyName, valu
401401
// either there is a writable cache or we need one to update
402402
let cache = meta.writableCache();
403403
let hadCachedValue = false;
404+
let rawCachedValue = cache.get(keyName);
404405
let cachedValue;
405-
if (cache[keyName] !== undefined) {
406-
if (cache[keyName] !== UNDEFINED) {
407-
cachedValue = cache[keyName];
406+
407+
if (rawCachedValue !== undefined) {
408+
if (rawCachedValue !== UNDEFINED) {
409+
cachedValue = rawCachedValue;
408410
}
409411
hadCachedValue = true;
410412
}
@@ -419,17 +421,17 @@ ComputedPropertyPrototype._set = function computedPropertySet(obj, keyName, valu
419421
propertyWillChange(obj, keyName);
420422

421423
if (hadCachedValue) {
422-
cache[keyName] = undefined;
424+
cache.set(keyName, undefined);
423425
}
424426

425427
if (!hadCachedValue) {
426428
addDependentKeys(this, obj, keyName, meta);
427429
}
428430

429431
if (ret === undefined) {
430-
cache[keyName] = UNDEFINED;
432+
cache.set(keyName, UNDEFINED);
431433
} else {
432-
cache[keyName] = ret;
434+
cache.set(keyName, ret);
433435
}
434436

435437
propertyDidChange(obj, keyName);
@@ -444,9 +446,9 @@ ComputedPropertyPrototype.teardown = function(obj, keyName) {
444446
}
445447
let meta = metaFor(obj);
446448
let cache = meta.readableCache();
447-
if (cache && cache[keyName] !== undefined) {
449+
if (cache && cache.get(keyName) !== undefined) {
448450
removeDependentKeys(this, obj, keyName, meta);
449-
cache[keyName] = undefined;
451+
cache.set(keyName, undefined);
450452
}
451453
};
452454

@@ -569,7 +571,7 @@ export default function computed(func) {
569571
function cacheFor(obj, key) {
570572
var meta = peekMeta(obj);
571573
var cache = meta && meta.source === obj && meta.readableCache();
572-
var ret = cache && cache[key];
574+
var ret = cache && cache.get(key);
573575

574576
if (ret === UNDEFINED) {
575577
return undefined;
@@ -579,22 +581,22 @@ function cacheFor(obj, key) {
579581

580582
cacheFor.set = function(cache, key, value) {
581583
if (value === undefined) {
582-
cache[key] = UNDEFINED;
584+
cache.set(key, UNDEFINED);
583585
} else {
584-
cache[key] = value;
586+
cache.set(key, value);
585587
}
586588
};
587589

588590
cacheFor.get = function(cache, key) {
589-
var ret = cache[key];
591+
var ret = cache.get(key);
590592
if (ret === UNDEFINED) {
591593
return undefined;
592594
}
593595
return ret;
594596
};
595597

596598
cacheFor.remove = function(cache, key) {
597-
cache[key] = undefined;
599+
cache.set(key, undefined);
598600
};
599601

600602
export {

packages/ember-metal/lib/meta.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { protoMethods as listenerMethods } from 'ember-metal/meta_listeners';
77
import EmptyObject from 'ember-metal/empty_object';
88
import { lookupDescriptor } from 'ember-metal/utils';
99
import symbol from 'ember-metal/symbol';
10+
import LodashishStack from './utils/lodash-stack';
11+
1012

1113
/**
1214
@module ember-metal
@@ -92,7 +94,11 @@ function ownMap(name, Meta) {
9294
let key = memberProperty(name);
9395
let capitalized = capitalize(name);
9496
Meta.prototype['writable' + capitalized] = function() {
95-
return this._getOrCreateOwnMap(key);
97+
let ret = this[key];
98+
if (!ret) {
99+
ret = this[key] = new LodashishStack();
100+
}
101+
return ret;
96102
};
97103
Meta.prototype['readable' + capitalized] = function() { return this[key]; };
98104
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
From Lodash's private ListCache here:
3+
4+
https://github.com/lodash/lodash/blob/4.13.1/dist/lodash.js#L1790-L1900
5+
6+
The long term plan is to replace this and simply utilize lodash itself (via
7+
rollup or other) in the build itself.
8+
9+
***********************************************************************
10+
* @license
11+
* lodash <https://lodash.com/>
12+
* Copyright jQuery Foundation and other contributors <https://jquery.org/>
13+
* Released under MIT license <https://lodash.com/license>
14+
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
15+
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
16+
***********************************************************************
17+
18+
*/
19+
function assocIndexOf(array, key) {
20+
var length = array.length;
21+
while (length--) {
22+
if (array[length][0] === key) {
23+
return length;
24+
}
25+
}
26+
27+
return -1;
28+
}
29+
30+
export default class ListCache {
31+
constructor() {
32+
this.__data__ = [];
33+
}
34+
35+
clear() {
36+
this.__data__ = [];
37+
}
38+
39+
delete(key) {
40+
let data = this.__data__;
41+
let index = assocIndexOf(data, key);
42+
43+
if (index < 0) {
44+
return false;
45+
}
46+
var lastIndex = data.length - 1;
47+
if (index == lastIndex) { // jshint ignore:line
48+
data.pop();
49+
} else {
50+
data.splice(index, 1);
51+
}
52+
53+
return true;
54+
}
55+
56+
get(key) {
57+
let data = this.__data__;
58+
let index = assocIndexOf(data, key);
59+
60+
return index < 0 ? undefined : data[index][1];
61+
}
62+
63+
has(key) {
64+
return assocIndexOf(this.__data__, key) > -1;
65+
}
66+
67+
set(key, value) {
68+
let data = this.__data__;
69+
let index = assocIndexOf(data, key);
70+
71+
if (index < 0) {
72+
data.push([key, value]);
73+
} else {
74+
data[index][1] = value;
75+
}
76+
77+
return this;
78+
}
79+
}

packages/ember-metal/lib/weak_map.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,12 @@ WeakMap.prototype.get = function(obj) {
3939
if (meta) {
4040
let map = meta.readableWeak();
4141
if (map) {
42-
if (map[this._id] === UNDEFINED) {
42+
let value = map.get(this._id);
43+
if (value === UNDEFINED) {
4344
return undefined;
4445
}
4546

46-
return map[this._id];
47+
return value;
4748
}
4849
}
4950
};
@@ -64,7 +65,7 @@ WeakMap.prototype.set = function(obj, value) {
6465
value = UNDEFINED;
6566
}
6667

67-
metaFor(obj).writableWeak()[this._id] = value;
68+
metaFor(obj).writableWeak().set(this._id, value);
6869

6970
return this;
7071
};
@@ -79,7 +80,7 @@ WeakMap.prototype.has = function(obj) {
7980
if (meta) {
8081
let map = meta.readableWeak();
8182
if (map) {
82-
return map[this._id] !== undefined;
83+
return map.get(this._id) !== undefined;
8384
}
8485
}
8586

@@ -93,7 +94,7 @@ WeakMap.prototype.has = function(obj) {
9394
*/
9495
WeakMap.prototype.delete = function(obj) {
9596
if (this.has(obj)) {
96-
delete metaFor(obj).writableWeak()[this._id];
97+
metaFor(obj).writableWeak().delete(this._id);
9798
return true;
9899
} else {
99100
return false;

packages/ember-runtime/lib/system/core_object.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -936,8 +936,8 @@ CoreObject.reopen({
936936
if (value instanceof ComputedProperty) {
937937
var cache = meta(this.constructor).readableCache();
938938

939-
if (cache && cache._computedProperties !== undefined) {
940-
cache._computedProperties = undefined;
939+
if (cache && cache.get('_computedProperties') !== undefined) {
940+
cache.set('_computedProperties', undefined);
941941
}
942942
}
943943
}

0 commit comments

Comments
 (0)