diff --git a/packages/ember-metal/lib/alias.js b/packages/ember-metal/lib/alias.js index 2d6685294c2..5b4b177c63b 100644 --- a/packages/ember-metal/lib/alias.js +++ b/packages/ember-metal/lib/alias.js @@ -6,7 +6,7 @@ import { Descriptor, defineProperty } from './properties'; -import { ComputedProperty } from './computed'; +import { ComputedProperty, getCacheFor } from './computed'; import { meta as metaFor } from './meta'; import { addDependentKeys, @@ -52,9 +52,9 @@ export class AliasedProperty extends Descriptor { get(obj, keyName) { let ret = get(obj, this.altKey); let meta = metaFor(obj); - let cache = meta.writableCache(); - if (cache[keyName] !== CONSUMED) { - cache[keyName] = CONSUMED; + let cache = getCacheFor(obj); + if (cache.get(keyName) !== CONSUMED) { + cache.set(keyName, CONSUMED); addDependentKeys(this, obj, keyName, meta); } return ret; diff --git a/packages/ember-metal/lib/chains.js b/packages/ember-metal/lib/chains.js index e6165f036fd..4cdfdd32a7d 100644 --- a/packages/ember-metal/lib/chains.js +++ b/packages/ember-metal/lib/chains.js @@ -1,7 +1,7 @@ import { get } from './property_get'; import { descriptorFor, meta as metaFor, peekMeta } from './meta'; import { watchKey, unwatchKey } from './watch_key'; -import { cacheFor } from './computed'; +import { getCachedValueFor } from './computed'; import { eachProxyFor } from './each_proxy'; const FIRST_KEY = /^([^\.]+)/; @@ -333,10 +333,7 @@ function lazyGet(obj, key) { return get(obj, key); // Otherwise attempt to get the cached value of the computed property } else { - let cache = meta.readableCache(); - if (cache !== undefined) { - return cacheFor.get(cache, key); - } + return getCachedValueFor(obj, key); } } diff --git a/packages/ember-metal/lib/computed.js b/packages/ember-metal/lib/computed.js index a61e06a8af9..a5dde8b55b8 100644 --- a/packages/ember-metal/lib/computed.js +++ b/packages/ember-metal/lib/computed.js @@ -1,7 +1,7 @@ import { inspect } from 'ember-utils'; import { assert, warn, Error as EmberError } from 'ember-debug'; import { set } from './property_set'; -import { meta as metaFor, peekMeta, UNDEFINED } from './meta'; +import { meta as metaFor, peekMeta } from './meta'; import expandProperties from './expand_properties'; import { Descriptor, @@ -317,9 +317,8 @@ ComputedPropertyPrototype.didChange = function(obj, keyName) { return; } - let cache = meta.readableCache(); - if (cache !== undefined && cache[keyName] !== undefined) { - cache[keyName] = undefined; + let cache = peekCacheFor(obj); + if (cache !== undefined && cache.delete(keyName)) { removeDependentKeys(this, obj, keyName, meta); } }; @@ -329,20 +328,17 @@ ComputedPropertyPrototype.get = function(obj, keyName) { return this._getter.call(obj, keyName); } - let meta = metaFor(obj); - let cache = meta.writableCache(); + let cache = getCacheFor(obj); - let result = cache[keyName]; - if (result === UNDEFINED) { - return undefined; - } else if (result !== undefined) { - return result; + if (cache.has(keyName)) { + return cache.get(keyName); } let ret = this._getter.call(obj, keyName); - cache[keyName] = ret === undefined ? UNDEFINED : ret; + cache.set(keyName, ret); + let meta = metaFor(obj); let chainWatchers = meta.readableChainWatchers(); if (chainWatchers !== undefined) { chainWatchers.revalidate(keyName); @@ -373,7 +369,7 @@ ComputedPropertyPrototype._throwReadOnlyError = function computedPropertyThrowRe }; ComputedPropertyPrototype.clobberSet = function computedPropertyClobberSet(obj, keyName, value) { - let cachedValue = cacheFor(obj, keyName); + let cachedValue = getCachedValueFor(obj, keyName); defineProperty(obj, keyName, null, cachedValue); set(obj, keyName, value); return value; @@ -395,15 +391,9 @@ ComputedPropertyPrototype.setWithSuspend = function computedPropertySetWithSuspe ComputedPropertyPrototype._set = function computedPropertySet(obj, keyName, value) { let meta = metaFor(obj); - let cache = meta.writableCache(); - - let val = cache[keyName]; - let hadCachedValue = val !== undefined; - - let cachedValue; - if (hadCachedValue && val !== UNDEFINED) { - cachedValue = val; - } + let cache = getCacheFor(obj); + let hadCachedValue = cache.has(keyName); + let cachedValue = cache.get(keyName); let ret = this._setter.call(obj, keyName, value, cachedValue); @@ -416,7 +406,7 @@ ComputedPropertyPrototype._set = function computedPropertySet(obj, keyName, valu addDependentKeys(this, obj, keyName, meta); } - cache[keyName] = ret === undefined ? UNDEFINED : ret; + cache.set(keyName, ret); notifyPropertyChange(obj, keyName, meta); @@ -428,10 +418,9 @@ ComputedPropertyPrototype.teardown = function(obj, keyName, meta) { if (this._volatile) { return; } - let cache = meta.readableCache(); - if (cache !== undefined && cache[keyName] !== undefined) { + let cache = peekCacheFor(obj); + if (cache !== undefined && cache.delete(keyName)) { removeDependentKeys(this, obj, keyName, meta); - cache[keyName] = undefined; } }; @@ -535,6 +524,8 @@ export default function computed(...args) { return cp; } +const COMPUTED_PROPERTY_CACHED_VALUES = new WeakMap(); + /** Returns the cached value for a property, if one exists. This can be useful for peeking at the value of a computed @@ -550,39 +541,27 @@ export default function computed(...args) { @return {Object} the cached value @public */ -function cacheFor(obj, key) { - let meta = peekMeta(obj); - let cache = meta !== undefined ? meta.source === obj && meta.readableCache() : undefined; - let ret = cache !== undefined ? cache[key] : undefined; - - if (ret === UNDEFINED) { - return undefined; +export function getCacheFor(obj) { + let cache = COMPUTED_PROPERTY_CACHED_VALUES.get(obj); + if (cache === undefined) { + cache = new Map(); + COMPUTED_PROPERTY_CACHED_VALUES.set(obj, cache); } - return ret; + return cache; } -cacheFor.set = function(cache, key, value) { - if (value === undefined) { - cache[key] = UNDEFINED; - } else { - cache[key] = value; +export function getCachedValueFor(obj, key) { + let cache = COMPUTED_PROPERTY_CACHED_VALUES.get(obj); + if (cache !== undefined) { + return cache.get(key); } -}; - -cacheFor.get = function(cache, key) { - let ret = cache[key]; - if (ret === UNDEFINED) { - return undefined; - } - return ret; -}; +} -cacheFor.remove = function(cache, key) { - cache[key] = undefined; -}; +export function peekCacheFor(obj) { + return COMPUTED_PROPERTY_CACHED_VALUES.get(obj); +} export { ComputedProperty, - computed, - cacheFor + computed }; diff --git a/packages/ember-metal/lib/index.js b/packages/ember-metal/lib/index.js index 7eb4ec6f6cd..8e38b706864 100644 --- a/packages/ember-metal/lib/index.js +++ b/packages/ember-metal/lib/index.js @@ -2,7 +2,9 @@ export { default } from './core'; // reexports export { default as computed, - cacheFor, + getCacheFor, + getCachedValueFor, + peekCacheFor, ComputedProperty } from './computed'; export { default as alias } from './alias'; diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index e5083b88e46..d6db76bf0f2 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -45,8 +45,6 @@ export class Meta { counters.metaInstantiated++; } - this._cache = undefined; - if (EMBER_METAL_ES5_GETTERS) { this._descriptors = undefined; } @@ -266,9 +264,6 @@ export class Meta { } } - writableCache() { return this._getOrCreateOwnMap('_cache'); } - readableCache() { return this._cache; } - writableTags() { return this._getOrCreateOwnMap('_tags'); } readableTags() { return this._tags; } diff --git a/packages/ember-metal/lib/properties.js b/packages/ember-metal/lib/properties.js index 9a5aa53f021..abb7b6882f7 100644 --- a/packages/ember-metal/lib/properties.js +++ b/packages/ember-metal/lib/properties.js @@ -7,6 +7,7 @@ import { HAS_NATIVE_PROXY } from 'ember-utils'; import { descriptorFor, meta as metaFor, peekMeta, DESCRIPTOR, UNDEFINED } from './meta'; import { overrideChains } from './property_events'; import { DESCRIPTOR_TRAP, EMBER_METAL_ES5_GETTERS, MANDATORY_SETTER } from 'ember/features'; +import { peekCacheFor } from './computed'; // .......................................................... // DESCRIPTOR // @@ -338,9 +339,9 @@ export function _hasCachedComputedProperties() { function didDefineComputedProperty(constructor) { if (hasCachedComputedProperties === false) { return; } - let cache = metaFor(constructor).readableCache(); - if (cache && cache._computedProperties !== undefined) { - cache._computedProperties = undefined; + let cache = peekCacheFor(constructor); + if (cache !== undefined) { + cache.delete('_computedProperties'); } } diff --git a/packages/ember-metal/tests/computed_test.js b/packages/ember-metal/tests/computed_test.js index d0967dea168..ea3eb80373b 100644 --- a/packages/ember-metal/tests/computed_test.js +++ b/packages/ember-metal/tests/computed_test.js @@ -4,7 +4,7 @@ import { testBoth } from 'internal-test-helpers'; import { ComputedProperty, computed, - cacheFor, + getCachedValueFor, Descriptor, defineProperty, get, @@ -316,24 +316,24 @@ testBoth('inherited property should not pick up cache', function(get, set, asser assert.equal(get(objB, 'foo'), 'bar 2', 'objB third get'); }); -testBoth('cacheFor should return the cached value', function(get, set, assert) { - assert.equal(cacheFor(obj, 'foo'), undefined, 'should not yet be a cached value'); +testBoth('getCachedValueFor should return the cached value', function(get, set, assert) { + assert.equal(getCachedValueFor(obj, 'foo'), undefined, 'should not yet be a cached value'); get(obj, 'foo'); - assert.equal(cacheFor(obj, 'foo'), 'bar 1', 'should retrieve cached value'); + assert.equal(getCachedValueFor(obj, 'foo'), 'bar 1', 'should retrieve cached value'); }); -testBoth('cacheFor should return falsy cached values', function(get, set, assert) { +testBoth('getCachedValueFor should return falsy cached values', function(get, set, assert) { defineProperty(obj, 'falsy', computed(function() { return false; })); - assert.equal(cacheFor(obj, 'falsy'), undefined, 'should not yet be a cached value'); + assert.equal(getCachedValueFor(obj, 'falsy'), undefined, 'should not yet be a cached value'); get(obj, 'falsy'); - assert.equal(cacheFor(obj, 'falsy'), false, 'should retrieve cached value'); + assert.equal(getCachedValueFor(obj, 'falsy'), false, 'should retrieve cached value'); }); testBoth('setting a cached computed property passes the old value as the third argument', function(get, set, assert) { diff --git a/packages/ember-metal/tests/observer_test.js b/packages/ember-metal/tests/observer_test.js index 8bbdfea164f..5e22a081239 100644 --- a/packages/ember-metal/tests/observer_test.js +++ b/packages/ember-metal/tests/observer_test.js @@ -6,7 +6,7 @@ import { notifyPropertyChange, defineProperty, computed, - cacheFor, + getCachedValueFor, Mixin, mixin, observer, @@ -484,7 +484,7 @@ testBoth('depending on a chain with a computed property', function(get, set, ass changed++; }); - assert.equal(cacheFor(obj, 'computed'), undefined, 'addObserver should not compute CP'); + assert.equal(getCachedValueFor(obj, 'computed'), undefined, 'addObserver should not compute CP'); set(obj, 'computed.foo', 'baz'); diff --git a/packages/ember-runtime/lib/mixins/array.js b/packages/ember-runtime/lib/mixins/array.js index 62ddb46f91f..38131d20cca 100644 --- a/packages/ember-runtime/lib/mixins/array.js +++ b/packages/ember-runtime/lib/mixins/array.js @@ -22,7 +22,8 @@ import { eachProxyArrayWillChange, eachProxyArrayDidChange, beginPropertyChanges, - endPropertyChanges + endPropertyChanges, + peekCacheFor } from 'ember-metal'; import { assert, deprecate } from 'ember-debug'; import Enumerable from './enumerable'; @@ -105,7 +106,7 @@ export function arrayContentDidChange(array, startIdx, removeAmt, addAmt) { sendEvent(array, '@array:change', [array, startIdx, removeAmt, addAmt]); let meta = peekMeta(array); - let cache = meta !== undefined ? meta.readableCache() : undefined; + let cache = peekCacheFor(array); if (cache !== undefined) { let length = get(array, 'length'); let addedAmount = (addAmt === -1 ? 0 : addAmt); @@ -114,17 +115,17 @@ export function arrayContentDidChange(array, startIdx, removeAmt, addAmt) { let previousLength = length - delta; let normalStartIdx = startIdx < 0 ? previousLength + startIdx : startIdx; - if (cache.firstObject !== undefined && normalStartIdx === 0) { + if (cache.has('firstObject') && normalStartIdx === 0) { notifyPropertyChange(array, 'firstObject', meta); } - if (cache.lastObject !== undefined) { + if (cache.has('lastObject')) { let previousLastIndex = previousLength - 1; let lastAffectedIndex = normalStartIdx + removedAmount; if (previousLastIndex < lastAffectedIndex) { notifyPropertyChange(array, 'lastObject', meta); } - } + } } return array; diff --git a/packages/ember-runtime/lib/mixins/observable.js b/packages/ember-runtime/lib/mixins/observable.js index c931e4148ea..3776c4a69c7 100644 --- a/packages/ember-runtime/lib/mixins/observable.js +++ b/packages/ember-runtime/lib/mixins/observable.js @@ -17,7 +17,7 @@ import { endPropertyChanges, addObserver, removeObserver, - cacheFor, + getCachedValueFor, isNone } from 'ember-metal'; import { assert } from 'ember-debug'; @@ -476,6 +476,6 @@ export default Mixin.create({ @public */ cacheFor(keyName) { - return cacheFor(this, keyName); + return getCachedValueFor(this, keyName); }, }); diff --git a/packages/ember/lib/index.js b/packages/ember/lib/index.js index 78d87cd073a..aba5a5506d3 100644 --- a/packages/ember/lib/index.js +++ b/packages/ember/lib/index.js @@ -40,7 +40,7 @@ const computed = metal.computed; computed.alias = metal.alias; Ember.computed = computed; Ember.ComputedProperty = metal.ComputedProperty; -Ember.cacheFor = metal.cacheFor; +Ember.cacheFor = metal.getCachedValueFor; Ember.assert = EmberDebug.assert; Ember.warn = EmberDebug.warn; diff --git a/packages/ember/tests/reexports_test.js b/packages/ember/tests/reexports_test.js index eadf0fec202..04e623675a3 100644 --- a/packages/ember/tests/reexports_test.js +++ b/packages/ember/tests/reexports_test.js @@ -40,7 +40,7 @@ let allExports =[ ['computed', 'ember-metal'], ['computed.alias', 'ember-metal', 'alias'], ['ComputedProperty', 'ember-metal'], - ['cacheFor', 'ember-metal'], + ['cacheFor', 'ember-metal', 'getCachedValueFor'], ['merge', 'ember-metal'], ['instrument', 'ember-metal'], ['Instrumentation.instrument', 'ember-metal', 'instrument'],