From a5dcce876d40f2bce17886f6f7ecfad7dce9f7a2 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 14:04:26 -0400 Subject: [PATCH 01/39] DRY up meta() --- packages/ember-metal/lib/utils.js | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/packages/ember-metal/lib/utils.js b/packages/ember-metal/lib/utils.js index ac9f5f79109..9085a856955 100644 --- a/packages/ember-metal/lib/utils.js +++ b/packages/ember-metal/lib/utils.js @@ -328,27 +328,19 @@ function meta(obj, writable) { return ret || EMPTY_META; } - if (!ret) { - if (obj.__defineNonEnumerable) { - obj.__defineNonEnumerable(EMBER_META_PROPERTY); - } else { - Object.defineProperty(obj, '__ember_meta__', META_DESC); - } + if (obj.__defineNonEnumerable) { + obj.__defineNonEnumerable(EMBER_META_PROPERTY); + } else { + Object.defineProperty(obj, '__ember_meta__', META_DESC); + } + if (!ret) { ret = new Meta(obj); if (isEnabled('mandatory-setter')) { ret.values = {}; } - - obj.__ember_meta__ = ret; } else if (ret.source !== obj) { - if (obj.__defineNonEnumerable) { - obj.__defineNonEnumerable(EMBER_META_PROPERTY); - } else { - Object.defineProperty(obj, '__ember_meta__', META_DESC); - } - ret = Object.create(ret); ret.watching = Object.create(ret.watching); ret.cache = undefined; @@ -357,9 +349,8 @@ function meta(obj, writable) { if (isEnabled('mandatory-setter')) { ret.values = Object.create(ret.values); } - - obj['__ember_meta__'] = ret; } + obj.__ember_meta__ = ret; return ret; } From 8133291d3d1cc90297c490e20b711a0b90c693b5 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 15:04:29 -0400 Subject: [PATCH 02/39] moving meta into its own module --- packages/ember-metal/lib/main.js | 8 ++- packages/ember-metal/lib/meta.js | 93 +++++++++++++++++++++++++++++++ packages/ember-metal/lib/utils.js | 93 +------------------------------ 3 files changed, 101 insertions(+), 93 deletions(-) create mode 100644 packages/ember-metal/lib/meta.js diff --git a/packages/ember-metal/lib/main.js b/packages/ember-metal/lib/main.js index e8d7a25ed97..d4a4f12d1ad 100644 --- a/packages/ember-metal/lib/main.js +++ b/packages/ember-metal/lib/main.js @@ -14,9 +14,7 @@ import { unsubscribe as instrumentationUnsubscribe } from 'ember-metal/instrumentation'; import { - EMPTY_META, GUID_KEY, - META_DESC, apply, applyStr, canInvoke, @@ -24,12 +22,16 @@ import { guidFor, inspect, makeArray, - meta, deprecatedTryCatchFinally, tryInvoke, uuid, wrap } from 'ember-metal/utils'; +import { + EMPTY_META, + META_DESC, + meta +} from 'ember-metal/meta'; import EmberError from 'ember-metal/error'; import Cache from 'ember-metal/cache'; import Logger from 'ember-metal/logger'; diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js new file mode 100644 index 00000000000..60d75cf2780 --- /dev/null +++ b/packages/ember-metal/lib/meta.js @@ -0,0 +1,93 @@ +// Remove "use strict"; from transpiled module until +// https://bugs.webkit.org/show_bug.cgi?id=138038 is fixed +// +'REMOVE_USE_STRICT: true'; + +import isEnabled from 'ember-metal/features'; + +/** +@module ember-metal +*/ + +function Meta(obj) { + this.watching = {}; + this.cache = undefined; + this.source = obj; + this.deps = undefined; + this.listeners = undefined; + this.mixins = undefined; + this.bindings = undefined; + this.chains = undefined; + this.chainWatchers = undefined; + this.values = undefined; + this.proto = undefined; +} + +export var META_DESC = { + writable: true, + configurable: true, + enumerable: false, + value: null +}; + +var EMBER_META_PROPERTY = { + name: '__ember_meta__', + descriptor: META_DESC +}; + +// Placeholder for non-writable metas. +export var EMPTY_META = new Meta(null); + +if (isEnabled('mandatory-setter')) { + EMPTY_META.values = {}; +} + +/** + Retrieves the meta hash for an object. If `writable` is true ensures the + hash is writable for this object as well. + + The meta object contains information about computed property descriptors as + well as any watched properties and other information. You generally will + not access this information directly but instead work with higher level + methods that manipulate this hash indirectly. + + @method meta + @for Ember + @private + + @param {Object} obj The object to retrieve meta for + @param {Boolean} [writable=true] Pass `false` if you do not intend to modify + the meta hash, allowing the method to avoid making an unnecessary copy. + @return {Object} the meta hash for an object +*/ +export function meta(obj, writable) { + var ret = obj.__ember_meta__; + if (writable === false) { + return ret || EMPTY_META; + } + + if (obj.__defineNonEnumerable) { + obj.__defineNonEnumerable(EMBER_META_PROPERTY); + } else { + Object.defineProperty(obj, '__ember_meta__', META_DESC); + } + + if (!ret) { + ret = new Meta(obj); + + if (isEnabled('mandatory-setter')) { + ret.values = {}; + } + } else if (ret.source !== obj) { + ret = Object.create(ret); + ret.watching = Object.create(ret.watching); + ret.cache = undefined; + ret.source = obj; + + if (isEnabled('mandatory-setter')) { + ret.values = Object.create(ret.values); + } + } + obj.__ember_meta__ = ret; + return ret; +} diff --git a/packages/ember-metal/lib/utils.js b/packages/ember-metal/lib/utils.js index 9085a856955..c69d844e2c3 100644 --- a/packages/ember-metal/lib/utils.js +++ b/packages/ember-metal/lib/utils.js @@ -3,7 +3,7 @@ // 'REMOVE_USE_STRICT: true'; -import isEnabled from 'ember-metal/features'; +import { meta } from 'ember-metal/meta'; /** @module ember-metal @@ -139,18 +139,6 @@ var nullDescriptor = { value: null }; -var META_DESC = { - writable: true, - configurable: true, - enumerable: false, - value: null -}; - -export var EMBER_META_PROPERTY = { - name: '__ember_meta__', - descriptor: META_DESC -}; - export var GUID_KEY_PROPERTY = { name: GUID_KEY, descriptor: nullDescriptor @@ -280,79 +268,6 @@ export function guidFor(obj) { } } -// .......................................................... -// META -// -function Meta(obj) { - this.watching = {}; - this.cache = undefined; - this.source = obj; - this.deps = undefined; - this.listeners = undefined; - this.mixins = undefined; - this.bindings = undefined; - this.chains = undefined; - this.chainWatchers = undefined; - this.values = undefined; - this.proto = undefined; -} - -// Placeholder for non-writable metas. -var EMPTY_META = new Meta(null); - -if (isEnabled('mandatory-setter')) { - EMPTY_META.values = {}; -} - -/** - Retrieves the meta hash for an object. If `writable` is true ensures the - hash is writable for this object as well. - - The meta object contains information about computed property descriptors as - well as any watched properties and other information. You generally will - not access this information directly but instead work with higher level - methods that manipulate this hash indirectly. - - @method meta - @for Ember - @private - - @param {Object} obj The object to retrieve meta for - @param {Boolean} [writable=true] Pass `false` if you do not intend to modify - the meta hash, allowing the method to avoid making an unnecessary copy. - @return {Object} the meta hash for an object -*/ -function meta(obj, writable) { - var ret = obj.__ember_meta__; - if (writable === false) { - return ret || EMPTY_META; - } - - if (obj.__defineNonEnumerable) { - obj.__defineNonEnumerable(EMBER_META_PROPERTY); - } else { - Object.defineProperty(obj, '__ember_meta__', META_DESC); - } - - if (!ret) { - ret = new Meta(obj); - - if (isEnabled('mandatory-setter')) { - ret.values = {}; - } - } else if (ret.source !== obj) { - ret = Object.create(ret); - ret.watching = Object.create(ret.watching); - ret.cache = undefined; - ret.source = obj; - - if (isEnabled('mandatory-setter')) { - ret.values = Object.create(ret.values); - } - } - obj.__ember_meta__ = ret; - return ret; -} /** @@ -584,9 +499,7 @@ export function applyStr(t, m, a) { export { GUID_KEY, - META_DESC, - EMPTY_META, - meta, makeArray, - canInvoke + canInvoke, + meta // this is temporary until I can refactor all the import sites }; From 30445476e4070444aa1f4c742807e3a86c62177c Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 15:11:26 -0400 Subject: [PATCH 03/39] make exports consistent --- packages/ember-metal/lib/events.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/ember-metal/lib/events.js b/packages/ember-metal/lib/events.js index 81646951566..13623428963 100644 --- a/packages/ember-metal/lib/events.js +++ b/packages/ember-metal/lib/events.js @@ -154,7 +154,7 @@ export function addListener(obj, eventName, target, method, once) { @param {Function|String} method A function or the name of a function to be called on `target` @public */ -function removeListener(obj, eventName, target, method) { +export function removeListener(obj, eventName, target, method) { Ember.assert('You must pass at least an object and event name to Ember.removeListener', !!obj && !!eventName); if (!method && 'function' === typeof target) { @@ -412,7 +412,3 @@ export function on(...args) { func.__ember_listens__ = events; return func; } - -export { - removeListener -}; From ab2515359c51c701e7932a28e940c77c7bd335ca Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 16:33:18 -0400 Subject: [PATCH 04/39] notes-to-self on each meta property's access patterns --- packages/ember-metal/lib/meta.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index 60d75cf2780..3ca5f96ce94 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -10,16 +10,46 @@ import isEnabled from 'ember-metal/features'; */ function Meta(obj) { + // map from strings to integer, with plain prototypical inheritance, + // cloned at meta creation. this.watching = {}; + + // plain map with no inheritance this.cache = undefined; + + // used only internally by meta() to distinguish meta-from-prototype + // from instance's own meta this.source = obj; + + // map of maps, with two-level prototypical inheritance, cloned on access this.deps = undefined; + + // map from keys to lists. Has own __source__ property that's used + // to distinguish whether it is being inherited or not. Each list + // also has a __source__property. Both levels are inherited on + // demand with o_create. this.listeners = undefined; + + // o_create inherited on demand this.mixins = undefined; + + // o_create inherited on demand. May also get wiped out with a + // direct `m.bindings = {}` after they're handled. this.bindings = undefined; + + // instance of ChainNode, inherited on demand via ChainNode.copy this.chains = undefined; + + // instanceof ChainWatchers. Created on demand with a fresh new + // ChainWatchers at each level in prototype chain, by testing + // m.chainWatchers.obj. this.chainWatchers = undefined; + + // plain inheritance, cloned at meta creation this.values = undefined; + + // when meta(obj).proto === obj, the object is intended to be only a + // prototype and doesn't need to actually be observable itself this.proto = undefined; } From 807b963ed318c4d6a7890a3aa0cf21e7525a99cf Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 17:31:29 -0400 Subject: [PATCH 05/39] encapsulated meta.cache --- packages/ember-metal/lib/chains.js | 5 +- packages/ember-metal/lib/computed.js | 30 ++++----- packages/ember-metal/lib/meta.js | 64 +++++++++++++++++-- .../ember-runtime/lib/system/core_object.js | 2 +- 4 files changed, 76 insertions(+), 25 deletions(-) diff --git a/packages/ember-metal/lib/chains.js b/packages/ember-metal/lib/chains.js index 693de378a56..9884bc925ea 100644 --- a/packages/ember-metal/lib/chains.js +++ b/packages/ember-metal/lib/chains.js @@ -222,8 +222,9 @@ function lazyGet(obj, key) { return get(obj, key); // Otherwise attempt to get the cached value of the computed property } else { - if (meta.cache && key in meta.cache) { - return meta.cache[key]; + let cache = meta.getCache(); + if (cache && key in cache) { + return cache[key]; } } } diff --git a/packages/ember-metal/lib/computed.js b/packages/ember-metal/lib/computed.js index d16a8b675ba..e205b9e4d34 100644 --- a/packages/ember-metal/lib/computed.js +++ b/packages/ember-metal/lib/computed.js @@ -266,9 +266,10 @@ ComputedPropertyPrototype.didChange = function(obj, keyName) { // _suspended is set via a CP.set to ensure we don't clear // the cached value set by the setter if (this._cacheable && this._suspended !== obj) { - var meta = metaFor(obj); - if (meta.cache && meta.cache[keyName] !== undefined) { - meta.cache[keyName] = undefined; + let meta = metaFor(obj); + let cache = meta.getCache(); + if (cache && cache[keyName] !== undefined) { + cache[keyName] = undefined; removeDependentKeys(this, obj, keyName, meta); } } @@ -305,9 +306,9 @@ ComputedPropertyPrototype.get = function(obj, keyName) { var ret, cache, meta; if (this._cacheable) { meta = metaFor(obj); - cache = meta.cache; + cache = meta.getOrCreateCache(); - var result = cache && cache[keyName]; + var result = cache[keyName]; if (result === UNDEFINED) { return undefined; @@ -316,10 +317,7 @@ ComputedPropertyPrototype.get = function(obj, keyName) { } ret = this._getter.call(obj, keyName); - cache = meta.cache; - if (!cache) { - cache = meta.cache = {}; - } + if (ret === undefined) { cache[keyName] = UNDEFINED; } else { @@ -401,7 +399,7 @@ ComputedPropertyPrototype._set = function computedPropertySet(obj, keyName, valu var cacheable = this._cacheable; var setter = this._setter; var meta = metaFor(obj, cacheable); - var cache = meta.cache; + var cache = meta.getCache(); var hadCachedValue = false; var cachedValue, ret; @@ -441,7 +439,7 @@ ComputedPropertyPrototype._set = function computedPropertySet(obj, keyName, valu addDependentKeys(this, obj, keyName, meta); } if (!cache) { - cache = meta.cache = {}; + cache = meta.getOrCreateCache(); } if (ret === undefined) { cache[keyName] = UNDEFINED; @@ -460,13 +458,13 @@ ComputedPropertyPrototype._set = function computedPropertySet(obj, keyName, valu /* called before property is overridden */ ComputedPropertyPrototype.teardown = function(obj, keyName) { var meta = metaFor(obj); - - if (meta.cache) { - if (keyName in meta.cache) { + let cache = meta.getCache(); + if (cache) { + if (keyName in cache) { removeDependentKeys(this, obj, keyName, meta); } - if (this._cacheable) { delete meta.cache[keyName]; } + if (this._cacheable) { delete cache[keyName]; } } return null; // no value to restore @@ -560,7 +558,7 @@ export default function computed(func) { */ function cacheFor(obj, key) { var meta = obj['__ember_meta__']; - var cache = meta && meta.cache; + var cache = meta && meta.getCache(); var ret = cache && cache[key]; if (ret === UNDEFINED) { diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index 3ca5f96ce94..604dcc3cc1f 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -9,14 +9,22 @@ import isEnabled from 'ember-metal/features'; @module ember-metal */ -function Meta(obj) { +let members = { + cache: ownMap +}; + +let memberNames = Object.keys(members); + +function Meta(obj, parentMeta) { + // preallocate a slot for each member + for (let i = 0; i < memberNames.length; i++) { + this['_' + memberNames[i]] = undefined; + } + // map from strings to integer, with plain prototypical inheritance, // cloned at meta creation. this.watching = {}; - // plain map with no inheritance - this.cache = undefined; - // used only internally by meta() to distinguish meta-from-prototype // from instance's own meta this.source = obj; @@ -51,6 +59,44 @@ function Meta(obj) { // when meta(obj).proto === obj, the object is intended to be only a // prototype and doesn't need to actually be observable itself this.proto = undefined; + + // The next meta in our inheritance chain. We (will) track this + // explicitly instead of using prototypical inheritance because we + // have detailed knowledge of how each property should really be + // inherited, and we can optimize it much better than JS runtimes. + this.parent = parentMeta; +} + +(function setupMembers() { + for (let i = 0; i < memberNames.length; i++) { + let name = memberNames[i]; + let implementation = members[name]; + implementation(name, Meta); + } +})(); + + +// Implements a member that is a lazily created, non-inheritable +// POJO. For member `thing` you get methods `getThing` and +// `getOrCreateThing`. +function ownMap(name, Meta) { + // This underscored name is preallocated in our constructor, so + // don't go changing it without updating that too. + let key = '_' + name; + + let capitalized = name.replace(/^\w/, m => m.toUpperCase()); + + Meta.prototype['getOrCreate' + capitalized] = function() { + let ret = this[key]; + if (!ret) { + ret = this[key] = Object.create(null); + } + return ret; + }; + + Meta.prototype['get' + capitalized] = function() { + return this[key]; + }; } export var META_DESC = { @@ -109,9 +155,15 @@ export function meta(obj, writable) { ret.values = {}; } } else if (ret.source !== obj) { - ret = Object.create(ret); + // temporary dance until I can eliminate remaining uses of + // prototype chain + let newRet = Object.create(ret); + newRet.parentMeta = ret; + ret = newRet; + ret._cache = undefined; + // end temporary dance + ret.watching = Object.create(ret.watching); - ret.cache = undefined; ret.source = obj; if (isEnabled('mandatory-setter')) { diff --git a/packages/ember-runtime/lib/system/core_object.js b/packages/ember-runtime/lib/system/core_object.js index 63bfc980dea..cdc63df5cc0 100644 --- a/packages/ember-runtime/lib/system/core_object.js +++ b/packages/ember-runtime/lib/system/core_object.js @@ -859,7 +859,7 @@ CoreObject.reopen({ didDefineProperty(proto, key, value) { if (hasCachedComputedProperties === false) { return; } if (value instanceof Ember.ComputedProperty) { - var cache = Ember.meta(this.constructor).cache; + var cache = Ember.meta(this.constructor).getCache(); if (cache && cache._computedProperties !== undefined) { cache._computedProperties = undefined; From 31c16f0fffdb1caebd8ae86c5dd05db5ef1e58b2 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 17:33:01 -0400 Subject: [PATCH 06/39] generalize upgrade support --- packages/ember-metal/lib/meta.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index 604dcc3cc1f..d1119abd08b 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -160,7 +160,10 @@ export function meta(obj, writable) { let newRet = Object.create(ret); newRet.parentMeta = ret; ret = newRet; - ret._cache = undefined; + for (let i = 0; i < memberNames.length; i++) { + let name = memberNames[i]; + ret['_' + name] = undefined; + } // end temporary dance ret.watching = Object.create(ret.watching); From 4bb90091ec9544de26b64d857a9aa6feb860ce08 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 18:05:38 -0400 Subject: [PATCH 07/39] more progress on generic meta accessors --- packages/ember-metal/lib/meta.js | 53 ++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index d1119abd08b..bf897d83e8c 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -18,7 +18,7 @@ let memberNames = Object.keys(members); function Meta(obj, parentMeta) { // preallocate a slot for each member for (let i = 0; i < memberNames.length; i++) { - this['_' + memberNames[i]] = undefined; + this[memberProperty(memberNames[i])] = undefined; } // map from strings to integer, with plain prototypical inheritance, @@ -80,11 +80,8 @@ function Meta(obj, parentMeta) { // POJO. For member `thing` you get methods `getThing` and // `getOrCreateThing`. function ownMap(name, Meta) { - // This underscored name is preallocated in our constructor, so - // don't go changing it without updating that too. - let key = '_' + name; - - let capitalized = name.replace(/^\w/, m => m.toUpperCase()); + let key = memberProperty(name); + let capitalized = capitalize(name); Meta.prototype['getOrCreate' + capitalized] = function() { let ret = this[key]; @@ -99,6 +96,45 @@ function ownMap(name, Meta) { }; } +// Implements a member that is lazily created POJO with inheritable +// values. For member `thing` you get methods `getThing`, +// `getOrCreateThing`, and `peekThing`. +function inheritedMap(name, Meta) { + let key = memberProperty(name); + let capitalized = capitalize(name); + + let getOrCreate = Meta.prototype['getOrCreate' + capitalized] = function() { + let ret = this[key]; + if (!ret) { + if (this.parent && this.parent[key]) { + ret = this[key] = Object.create(this.parent[key]); + } else { + ret = this[key] = Object.create(null); + } + } + return ret; + }; + + Meta.prototype['get' + capitalized] = getOrCreate; + + Meta.prototype['peek' + capitalized] = function(subkey) { + let map = getOrCreate.apply(this); + if (map) { + return map[subkey]; + } + }; +} + +function memberProperty(name) { + return '_' + name; +} + +// there's a more general-purpose capitalize in ember-runtime, but we +// don't want to make ember-metal depend on ember-runtime. +function capitalize(name) { + return name.replace(/^\w/, m => m.toUpperCase()); +} + export var META_DESC = { writable: true, configurable: true, @@ -158,11 +194,10 @@ export function meta(obj, writable) { // temporary dance until I can eliminate remaining uses of // prototype chain let newRet = Object.create(ret); - newRet.parentMeta = ret; + newRet.parent = ret; ret = newRet; for (let i = 0; i < memberNames.length; i++) { - let name = memberNames[i]; - ret['_' + name] = undefined; + ret[memberProperty(memberNames[i])] = undefined; } // end temporary dance From d885f23999d1d2f45079dcd7860b77d8674f0d27 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 18:21:16 -0400 Subject: [PATCH 08/39] encapsulated meta.watching --- packages/ember-metal/lib/alias.js | 4 ++-- packages/ember-metal/lib/computed.js | 2 +- packages/ember-metal/lib/meta.js | 19 ++++++++----------- packages/ember-metal/lib/properties.js | 2 +- packages/ember-metal/lib/property_events.js | 4 ++-- packages/ember-metal/lib/property_get.js | 2 +- packages/ember-metal/lib/property_set.js | 2 +- packages/ember-metal/lib/watch_key.js | 8 ++++---- packages/ember-metal/lib/watch_path.js | 8 ++++---- packages/ember-metal/lib/watching.js | 2 +- .../ember-metal/tests/watching/watch_test.js | 8 ++++---- .../ember/tests/routing/query_params_test.js | 6 ++---- 12 files changed, 31 insertions(+), 36 deletions(-) diff --git a/packages/ember-metal/lib/alias.js b/packages/ember-metal/lib/alias.js index 77b77ffa2e2..29e4240260e 100644 --- a/packages/ember-metal/lib/alias.js +++ b/packages/ember-metal/lib/alias.js @@ -47,14 +47,14 @@ AliasedProperty.prototype.didUnwatch = function(obj, keyName) { AliasedProperty.prototype.setup = function(obj, keyName) { Ember.assert(`Setting alias '${keyName}' on self`, this.altKey !== keyName); var m = meta(obj); - if (m.watching[keyName]) { + if (m.peekWatching(keyName)) { addDependentKeys(this, obj, keyName, m); } }; AliasedProperty.prototype.teardown = function(obj, keyName) { var m = meta(obj); - if (m.watching[keyName]) { + if (m.peekWatching(keyName)) { removeDependentKeys(this, obj, keyName, m); } }; diff --git a/packages/ember-metal/lib/computed.js b/packages/ember-metal/lib/computed.js index e205b9e4d34..2ba3892f01e 100644 --- a/packages/ember-metal/lib/computed.js +++ b/packages/ember-metal/lib/computed.js @@ -425,7 +425,7 @@ ComputedPropertyPrototype._set = function computedPropertySet(obj, keyName, valu if (hadCachedValue && cachedValue === ret) { return; } - var watched = meta.watching[keyName]; + var watched = meta.peekWatching(keyName); if (watched) { propertyWillChange(obj, keyName); } diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index bf897d83e8c..2240255aed2 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -10,7 +10,8 @@ import isEnabled from 'ember-metal/features'; */ let members = { - cache: ownMap + cache: ownMap, + watching: inheritedMap }; let memberNames = Object.keys(members); @@ -21,10 +22,6 @@ function Meta(obj, parentMeta) { this[memberProperty(memberNames[i])] = undefined; } - // map from strings to integer, with plain prototypical inheritance, - // cloned at meta creation. - this.watching = {}; - // used only internally by meta() to distinguish meta-from-prototype // from instance's own meta this.source = obj; @@ -106,8 +103,8 @@ function inheritedMap(name, Meta) { let getOrCreate = Meta.prototype['getOrCreate' + capitalized] = function() { let ret = this[key]; if (!ret) { - if (this.parent && this.parent[key]) { - ret = this[key] = Object.create(this.parent[key]); + if (this.parent) { + ret = this[key] = Object.create(getOrCreate.apply(this.parent)); } else { ret = this[key] = Object.create(null); } @@ -186,7 +183,7 @@ export function meta(obj, writable) { if (!ret) { ret = new Meta(obj); - + //ret._watching = {}; //fixme if (isEnabled('mandatory-setter')) { ret.values = {}; } @@ -195,13 +192,13 @@ export function meta(obj, writable) { // prototype chain let newRet = Object.create(ret); newRet.parent = ret; - ret = newRet; for (let i = 0; i < memberNames.length; i++) { - ret[memberProperty(memberNames[i])] = undefined; + newRet[memberProperty(memberNames[i])] = undefined; } + //newRet._watching = Object.create(ret._watching); // fixme + ret = newRet; // end temporary dance - ret.watching = Object.create(ret.watching); ret.source = obj; if (isEnabled('mandatory-setter')) { diff --git a/packages/ember-metal/lib/properties.js b/packages/ember-metal/lib/properties.js index d5f7600ae11..2094b10dfbe 100644 --- a/packages/ember-metal/lib/properties.js +++ b/packages/ember-metal/lib/properties.js @@ -89,7 +89,7 @@ export function defineProperty(obj, keyName, desc, data, meta) { if (!meta) { meta = metaFor(obj); } - var watchEntry = meta.watching[keyName]; + var watchEntry = meta.peekWatching(keyName); possibleDesc = obj[keyName]; existingDesc = (possibleDesc !== null && typeof possibleDesc === 'object' && possibleDesc.isDescriptor) ? possibleDesc : undefined; diff --git a/packages/ember-metal/lib/property_events.js b/packages/ember-metal/lib/property_events.js index f46505f4f50..2a259bd0b74 100644 --- a/packages/ember-metal/lib/property_events.js +++ b/packages/ember-metal/lib/property_events.js @@ -36,7 +36,7 @@ var deferred = 0; */ function propertyWillChange(obj, keyName) { var m = obj['__ember_meta__']; - var watching = (m && m.watching[keyName] > 0) || keyName === 'length'; + var watching = (m && m.peekWatching(keyName) > 0) || keyName === 'length'; var proto = m && m.proto; var possibleDesc = obj[keyName]; var desc = (possibleDesc !== null && typeof possibleDesc === 'object' && possibleDesc.isDescriptor) ? possibleDesc : undefined; @@ -76,7 +76,7 @@ function propertyWillChange(obj, keyName) { */ function propertyDidChange(obj, keyName) { var m = obj['__ember_meta__']; - var watching = (m && m.watching[keyName] > 0) || keyName === 'length'; + var watching = (m && m.peekWatching(keyName) > 0) || keyName === 'length'; var proto = m && m.proto; var possibleDesc = obj[keyName]; var desc = (possibleDesc !== null && typeof possibleDesc === 'object' && possibleDesc.isDescriptor) ? possibleDesc : undefined; diff --git a/packages/ember-metal/lib/property_get.js b/packages/ember-metal/lib/property_get.js index a96fdcc7892..814288f7101 100644 --- a/packages/ember-metal/lib/property_get.js +++ b/packages/ember-metal/lib/property_get.js @@ -69,7 +69,7 @@ export function get(obj, keyName) { return desc.get(obj, keyName); } else { if (isEnabled('mandatory-setter')) { - if (meta && meta.watching[keyName] > 0) { + if (meta && meta.peekWatching(keyName) > 0) { ret = meta.values[keyName]; } else { ret = obj[keyName]; diff --git a/packages/ember-metal/lib/property_set.js b/packages/ember-metal/lib/property_set.js index 7275e4ec106..37c3d7bce13 100644 --- a/packages/ember-metal/lib/property_set.js +++ b/packages/ember-metal/lib/property_set.js @@ -63,7 +63,7 @@ export function set(obj, keyName, value, tolerant) { // `setUnknownProperty` method exists on the object if (isUnknown && 'function' === typeof obj.setUnknownProperty) { obj.setUnknownProperty(keyName, value); - } else if (meta && meta.watching[keyName] > 0) { + } else if (meta && meta.peekWatching(keyName) > 0) { if (meta.proto !== obj) { if (isEnabled('mandatory-setter')) { currentValue = meta.values[keyName]; diff --git a/packages/ember-metal/lib/watch_key.js b/packages/ember-metal/lib/watch_key.js index 473b49989a8..3d920295cac 100644 --- a/packages/ember-metal/lib/watch_key.js +++ b/packages/ember-metal/lib/watch_key.js @@ -12,7 +12,7 @@ export function watchKey(obj, keyName, meta) { if (keyName === 'length' && Array.isArray(obj)) { return; } var m = meta || metaFor(obj); - var watching = m.watching; + var watching = m.getOrCreateWatching(); // activate watching first time if (!watching[keyName]) { @@ -65,9 +65,9 @@ if (isEnabled('mandatory-setter')) { export function unwatchKey(obj, keyName, meta) { var m = meta || metaFor(obj); - var watching = m.watching; + var watching = m.getWatching(); - if (watching[keyName] === 1) { + if (watching && watching[keyName] === 1) { watching[keyName] = 0; var possibleDesc = obj[keyName]; @@ -97,7 +97,7 @@ export function unwatchKey(obj, keyName, meta) { }); } } - } else if (watching[keyName] > 1) { + } else if (watching && watching[keyName] > 1) { watching[keyName]--; } } diff --git a/packages/ember-metal/lib/watch_path.js b/packages/ember-metal/lib/watch_path.js index a5d67dc4f10..0244ee03b09 100644 --- a/packages/ember-metal/lib/watch_path.js +++ b/packages/ember-metal/lib/watch_path.js @@ -22,7 +22,7 @@ export function watchPath(obj, keyPath, meta) { if (keyPath === 'length' && Array.isArray(obj)) { return; } var m = meta || metaFor(obj); - var watching = m.watching; + var watching = m.getOrCreateWatching(); if (!watching[keyPath]) { // activate watching first time watching[keyPath] = 1; @@ -34,12 +34,12 @@ export function watchPath(obj, keyPath, meta) { export function unwatchPath(obj, keyPath, meta) { var m = meta || metaFor(obj); - var watching = m.watching; + var watching = m.getWatching(); - if (watching[keyPath] === 1) { + if (watching && watching[keyPath] === 1) { watching[keyPath] = 0; chainsFor(obj, m).remove(keyPath); - } else if (watching[keyPath] > 1) { + } else if (watching && watching[keyPath] > 1) { watching[keyPath]--; } } diff --git a/packages/ember-metal/lib/watching.js b/packages/ember-metal/lib/watching.js index ed21b727245..74c247d8033 100644 --- a/packages/ember-metal/lib/watching.js +++ b/packages/ember-metal/lib/watching.js @@ -46,7 +46,7 @@ export { watch }; export function isWatching(obj, key) { var meta = obj['__ember_meta__']; - return (meta && meta.watching[key]) > 0; + return (meta && meta.peekWatching(key)) > 0; } watch.flushPending = flushPendingChains; diff --git a/packages/ember-metal/tests/watching/watch_test.js b/packages/ember-metal/tests/watching/watch_test.js index 910a9d8e9c6..e4a6121d72b 100644 --- a/packages/ember-metal/tests/watching/watch_test.js +++ b/packages/ember-metal/tests/watching/watch_test.js @@ -205,12 +205,12 @@ QUnit.test('when watching a global object, destroy should remove chain watchers var meta_Global = Ember.meta(Global); var chainNode = Ember.meta(obj).chains._chains.Global._chains.foo; - equal(meta_Global.watching.foo, 1, 'should be watching foo'); + equal(meta_Global.peekWatching('foo'), 1, 'should be watching foo'); equal(meta_Global.chainWatchers.has('foo', chainNode), true, 'should have chain watcher'); destroy(obj); - equal(meta_Global.watching.foo, 0, 'should not be watching foo'); + equal(meta_Global.peekWatching('foo'), 0, 'should not be watching foo'); equal(meta_Global.chainWatchers.has('foo', chainNode), false, 'should not have chain watcher'); lookup['Global'] = Global = null; // reset @@ -227,12 +227,12 @@ QUnit.test('when watching another object, destroy should remove chain watchers f var meta_objB = Ember.meta(objB); var chainNode = Ember.meta(objA).chains._chains.b._chains.foo; - equal(meta_objB.watching.foo, 1, 'should be watching foo'); + equal(meta_objB.peekWatching('foo'), 1, 'should be watching foo'); equal(meta_objB.chainWatchers.has('foo', chainNode), true, 'should have chain watcher'); destroy(objA); - equal(meta_objB.watching.foo, 0, 'should not be watching foo'); + equal(meta_objB.peekWatching('foo'), 0, 'should not be watching foo'); equal(meta_objB.chainWatchers.has('foo', chainNode), false, 'should not have chain watcher'); }); diff --git a/packages/ember/tests/routing/query_params_test.js b/packages/ember/tests/routing/query_params_test.js index ce42493a938..02508274021 100644 --- a/packages/ember/tests/routing/query_params_test.js +++ b/packages/ember/tests/routing/query_params_test.js @@ -1811,7 +1811,7 @@ if (isEnabled('ember-routing-route-configured-query-params')) { App.OtherRoute = Ember.Route.extend({ model(p, trans) { var m = Ember.meta(trans.params.application); - ok(!m.watching.woot, 'A meta object isn\'t constructed for this params POJO'); + ok(!m.peekWatching('woot'), 'A meta object isn\'t constructed for this params POJO'); } }); @@ -2960,7 +2960,7 @@ if (isEnabled('ember-routing-route-configured-query-params')) { App.OtherRoute = Ember.Route.extend({ model(p, trans) { var m = Ember.meta(trans.params.application); - ok(!m.watching.woot, 'A meta object isn\'t constructed for this params POJO'); + ok(!m.peekWatching('woot'), 'A meta object isn\'t constructed for this params POJO'); } }); @@ -3102,5 +3102,3 @@ QUnit.test('handle routes names that clash with Object.prototype properties', fu var controller = container.lookup('controller:constructor'); equal(get(controller, 'foo'), '999'); }); - - From 542765bf69b4c47c5c48abd0251e50fb65098a0c Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 18:24:25 -0400 Subject: [PATCH 09/39] align semantics --- packages/ember-metal/lib/meta.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index 2240255aed2..a603e08f02b 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -112,10 +112,10 @@ function inheritedMap(name, Meta) { return ret; }; - Meta.prototype['get' + capitalized] = getOrCreate; + let getId = Meta.prototype['get' + capitalized] = getOrCreate; Meta.prototype['peek' + capitalized] = function(subkey) { - let map = getOrCreate.apply(this); + let map = getId.apply(this); if (map) { return map[subkey]; } From a4c9dd32a286da6a12b1ced163d65206877cca23 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 18:29:04 -0400 Subject: [PATCH 10/39] lazy watchers --- packages/ember-metal/lib/meta.js | 10 +++++++++- packages/ember-metal/lib/watch_key.js | 6 +++--- packages/ember-metal/lib/watch_path.js | 6 +++--- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index a603e08f02b..76fc31fabdf 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -112,7 +112,15 @@ function inheritedMap(name, Meta) { return ret; }; - let getId = Meta.prototype['get' + capitalized] = getOrCreate; + let getId = Meta.prototype['get' + capitalized] = function() { + let pointer = this; + while (pointer) { + if (pointer[key]) { + return pointer[key]; + } + pointer = pointer.parent; + } + }; Meta.prototype['peek' + capitalized] = function(subkey) { let map = getId.apply(this); diff --git a/packages/ember-metal/lib/watch_key.js b/packages/ember-metal/lib/watch_key.js index 3d920295cac..0b9e1310c08 100644 --- a/packages/ember-metal/lib/watch_key.js +++ b/packages/ember-metal/lib/watch_key.js @@ -65,9 +65,9 @@ if (isEnabled('mandatory-setter')) { export function unwatchKey(obj, keyName, meta) { var m = meta || metaFor(obj); - var watching = m.getWatching(); + var watching = m.getOrCreateWatching(); - if (watching && watching[keyName] === 1) { + if (watching[keyName] === 1) { watching[keyName] = 0; var possibleDesc = obj[keyName]; @@ -97,7 +97,7 @@ export function unwatchKey(obj, keyName, meta) { }); } } - } else if (watching && watching[keyName] > 1) { + } else if (watching[keyName] > 1) { watching[keyName]--; } } diff --git a/packages/ember-metal/lib/watch_path.js b/packages/ember-metal/lib/watch_path.js index 0244ee03b09..c40194b5af5 100644 --- a/packages/ember-metal/lib/watch_path.js +++ b/packages/ember-metal/lib/watch_path.js @@ -34,12 +34,12 @@ export function watchPath(obj, keyPath, meta) { export function unwatchPath(obj, keyPath, meta) { var m = meta || metaFor(obj); - var watching = m.getWatching(); + var watching = m.getOrCreateWatching(); - if (watching && watching[keyPath] === 1) { + if (watching[keyPath] === 1) { watching[keyPath] = 0; chainsFor(obj, m).remove(keyPath); - } else if (watching && watching[keyPath] > 1) { + } else if (watching[keyPath] > 1) { watching[keyPath]--; } } From 68895eca32a33f2d52011b932e07ae09f7af6670 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 18:36:05 -0400 Subject: [PATCH 11/39] encapsulated meta.mixins --- packages/ember-metal/lib/meta.js | 6 ++---- packages/ember-metal/lib/mixin.js | 20 +++++--------------- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index 76fc31fabdf..31d02d4f5c8 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -11,7 +11,8 @@ import isEnabled from 'ember-metal/features'; let members = { cache: ownMap, - watching: inheritedMap + watching: inheritedMap, + mixins: inheritedMap }; let memberNames = Object.keys(members); @@ -35,9 +36,6 @@ function Meta(obj, parentMeta) { // demand with o_create. this.listeners = undefined; - // o_create inherited on demand - this.mixins = undefined; - // o_create inherited on demand. May also get wiped out with a // direct `m.bindings = {}` after they're handled. this.bindings = undefined; diff --git a/packages/ember-metal/lib/mixin.js b/packages/ember-metal/lib/mixin.js index b7807b25cf7..494098d3d3a 100644 --- a/packages/ember-metal/lib/mixin.js +++ b/packages/ember-metal/lib/mixin.js @@ -74,14 +74,7 @@ superFunction.call(primer, 1, 2); superFunction.call(primer, 1, 2, 3); function mixinsMeta(obj) { - var m = metaFor(obj, true); - var ret = m.mixins; - if (!ret) { - ret = m.mixins = {}; - } else if (!m.hasOwnProperty('mixins')) { - ret = m.mixins = Object.create(ret); - } - return ret; + return metaFor(obj, true).getOrCreateMixins(); } function isMethod(obj) { @@ -667,12 +660,9 @@ function _detect(curMixin, targetMixin, seen) { MixinPrototype.detect = function(obj) { if (!obj) { return false; } if (obj instanceof Mixin) { return _detect(obj, this, {}); } - var m = obj['__ember_meta__']; - var mixins = m && m.mixins; - if (mixins) { - return !!mixins[guidFor(this)]; - } - return false; + var m = obj.__ember_meta__; + if (!m) { return false; } + return !!m.peekMixins(guidFor(this)); }; MixinPrototype.without = function(...args) { @@ -712,7 +702,7 @@ MixinPrototype.keys = function() { // TODO: Make Ember.mixin Mixin.mixins = function(obj) { var m = obj['__ember_meta__']; - var mixins = m && m.mixins; + var mixins = m && m.getMixins(); var ret = []; if (!mixins) { return ret; } From aab8f9a23aca8ea74467cdadeb60b5378ef7dfd1 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 18:41:47 -0400 Subject: [PATCH 12/39] encapsulate meta.bindings --- packages/ember-metal/lib/meta.js | 13 ++++++------- packages/ember-metal/lib/mixin.js | 12 +++--------- packages/ember-runtime/lib/system/core_object.js | 8 +------- 3 files changed, 10 insertions(+), 23 deletions(-) diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index 31d02d4f5c8..3f3a6317689 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -12,7 +12,8 @@ import isEnabled from 'ember-metal/features'; let members = { cache: ownMap, watching: inheritedMap, - mixins: inheritedMap + mixins: inheritedMap, + bindings: inheritedMap }; let memberNames = Object.keys(members); @@ -36,10 +37,6 @@ function Meta(obj, parentMeta) { // demand with o_create. this.listeners = undefined; - // o_create inherited on demand. May also get wiped out with a - // direct `m.bindings = {}` after they're handled. - this.bindings = undefined; - // instance of ChainNode, inherited on demand via ChainNode.copy this.chains = undefined; @@ -126,6 +123,10 @@ function inheritedMap(name, Meta) { return map[subkey]; } }; + + Meta.prototype['clear' + capitalized] = function() { + this[key] = Object.create(null); + }; } function memberProperty(name) { @@ -189,7 +190,6 @@ export function meta(obj, writable) { if (!ret) { ret = new Meta(obj); - //ret._watching = {}; //fixme if (isEnabled('mandatory-setter')) { ret.values = {}; } @@ -201,7 +201,6 @@ export function meta(obj, writable) { for (let i = 0; i < memberNames.length; i++) { newRet[memberProperty(memberNames[i])] = undefined; } - //newRet._watching = Object.create(ret._watching); // fixme ret = newRet; // end temporary dance diff --git a/packages/ember-metal/lib/mixin.js b/packages/ember-metal/lib/mixin.js index 494098d3d3a..3aba637b6f2 100644 --- a/packages/ember-metal/lib/mixin.js +++ b/packages/ember-metal/lib/mixin.js @@ -309,13 +309,7 @@ var IS_BINDING = /^.+Binding$/; function detectBinding(obj, key, value, m) { if (IS_BINDING.test(key)) { - var bindings = m.bindings; - if (!bindings) { - bindings = m.bindings = {}; - } else if (!m.hasOwnProperty('bindings')) { - bindings = m.bindings = Object.create(m.bindings); - } - bindings[key] = value; + m.getOrCreateBindings()[key] = value; } } @@ -346,7 +340,7 @@ function connectStreamBinding(obj, key, stream) { function connectBindings(obj, m) { // TODO Mixin.apply(instance) should disconnect binding if exists - var bindings = m.bindings; + var bindings = m.getBindings(); var key, binding, to; if (bindings) { for (key in bindings) { @@ -367,7 +361,7 @@ function connectBindings(obj, m) { } } // mark as applied - m.bindings = {}; + m.clearBindings(); } } diff --git a/packages/ember-runtime/lib/system/core_object.js b/packages/ember-runtime/lib/system/core_object.js index cdc63df5cc0..d2a8fef8168 100644 --- a/packages/ember-runtime/lib/system/core_object.js +++ b/packages/ember-runtime/lib/system/core_object.js @@ -104,13 +104,7 @@ function makeCtor() { var value = properties[keyName]; if (IS_BINDING.test(keyName)) { - var bindings = m.bindings; - if (!bindings) { - bindings = m.bindings = {}; - } else if (!m.hasOwnProperty('bindings')) { - bindings = m.bindings = Object.create(m.bindings); - } - bindings[keyName] = value; + m.getOrCreateBindings()[keyName] = value; } var possibleDesc = this[keyName]; From 0b96990950245f229f5ba68417430b3ea1863b7f Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 18:49:29 -0400 Subject: [PATCH 13/39] encapsulated meta.values --- packages/ember-metal/lib/meta.js | 14 ++++---------- packages/ember-metal/lib/properties.js | 4 ++-- packages/ember-metal/lib/property_get.js | 2 +- packages/ember-metal/lib/property_set.js | 4 ++-- packages/ember-metal/lib/watch_key.js | 4 ++-- .../tests/accessors/mandatory_setters_test.js | 8 ++++---- 6 files changed, 15 insertions(+), 21 deletions(-) diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index 3f3a6317689..eeb14f09505 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -13,7 +13,8 @@ let members = { cache: ownMap, watching: inheritedMap, mixins: inheritedMap, - bindings: inheritedMap + bindings: inheritedMap, + values: inheritedMap }; let memberNames = Object.keys(members); @@ -45,9 +46,6 @@ function Meta(obj, parentMeta) { // m.chainWatchers.obj. this.chainWatchers = undefined; - // plain inheritance, cloned at meta creation - this.values = undefined; - // when meta(obj).proto === obj, the object is intended to be only a // prototype and doesn't need to actually be observable itself this.proto = undefined; @@ -155,7 +153,7 @@ var EMBER_META_PROPERTY = { export var EMPTY_META = new Meta(null); if (isEnabled('mandatory-setter')) { - EMPTY_META.values = {}; + EMPTY_META.getOrCreateValues(); } /** @@ -191,7 +189,7 @@ export function meta(obj, writable) { if (!ret) { ret = new Meta(obj); if (isEnabled('mandatory-setter')) { - ret.values = {}; + ret.getOrCreateValues(); } } else if (ret.source !== obj) { // temporary dance until I can eliminate remaining uses of @@ -205,10 +203,6 @@ export function meta(obj, writable) { // end temporary dance ret.source = obj; - - if (isEnabled('mandatory-setter')) { - ret.values = Object.create(ret.values); - } } obj.__ember_meta__ = ret; return ret; diff --git a/packages/ember-metal/lib/properties.js b/packages/ember-metal/lib/properties.js index 2094b10dfbe..abb25b1f0cf 100644 --- a/packages/ember-metal/lib/properties.js +++ b/packages/ember-metal/lib/properties.js @@ -34,7 +34,7 @@ export function MANDATORY_SETTER_FUNCTION(name) { export function DEFAULT_GETTER_FUNCTION(name) { return function GETTER_FUNCTION() { var meta = this['__ember_meta__']; - return meta && meta.values[name]; + return meta && meta.peekValues(name); }; } @@ -122,7 +122,7 @@ export function defineProperty(obj, keyName, desc, data, meta) { if (isEnabled('mandatory-setter')) { if (watching) { - meta.values[keyName] = data; + meta.getOrCreateValues()[keyName] = data; Object.defineProperty(obj, keyName, { configurable: true, enumerable: true, diff --git a/packages/ember-metal/lib/property_get.js b/packages/ember-metal/lib/property_get.js index 814288f7101..26533b0f17a 100644 --- a/packages/ember-metal/lib/property_get.js +++ b/packages/ember-metal/lib/property_get.js @@ -70,7 +70,7 @@ export function get(obj, keyName) { } else { if (isEnabled('mandatory-setter')) { if (meta && meta.peekWatching(keyName) > 0) { - ret = meta.values[keyName]; + ret = meta.peekValues(keyName); } else { ret = obj[keyName]; } diff --git a/packages/ember-metal/lib/property_set.js b/packages/ember-metal/lib/property_set.js index 37c3d7bce13..2ebbafebd2c 100644 --- a/packages/ember-metal/lib/property_set.js +++ b/packages/ember-metal/lib/property_set.js @@ -66,7 +66,7 @@ export function set(obj, keyName, value, tolerant) { } else if (meta && meta.peekWatching(keyName) > 0) { if (meta.proto !== obj) { if (isEnabled('mandatory-setter')) { - currentValue = meta.values[keyName]; + currentValue = meta.peekValues(keyName); } else { currentValue = obj[keyName]; } @@ -81,7 +81,7 @@ export function set(obj, keyName, value, tolerant) { ) { defineProperty(obj, keyName, null, value); // setup mandatory setter } else { - meta.values[keyName] = value; + meta.getOrCreateValues()[keyName] = value; } } else { obj[keyName] = value; diff --git a/packages/ember-metal/lib/watch_key.js b/packages/ember-metal/lib/watch_key.js index 0b9e1310c08..873b82cd519 100644 --- a/packages/ember-metal/lib/watch_key.js +++ b/packages/ember-metal/lib/watch_key.js @@ -48,7 +48,7 @@ if (isEnabled('mandatory-setter')) { // this x in Y deopts, so keeping it in this function is better; if (configurable && isWritable && hasValue && keyName in obj) { - m.values[keyName] = obj[keyName]; + m.getOrCreateValues()[keyName] = obj[keyName]; Object.defineProperty(obj, keyName, { configurable: true, enumerable: Object.prototype.propertyIsEnumerable.call(obj, keyName), @@ -91,7 +91,7 @@ export function unwatchKey(obj, keyName, meta) { enumerable: true, value: val }); - delete m.values[keyName]; + delete m.getOrCreateValues()[keyName]; }, get: DEFAULT_GETTER_FUNCTION(keyName) }); diff --git a/packages/ember-metal/tests/accessors/mandatory_setters_test.js b/packages/ember-metal/tests/accessors/mandatory_setters_test.js index 993cbf4372d..21df14d4058 100644 --- a/packages/ember-metal/tests/accessors/mandatory_setters_test.js +++ b/packages/ember-metal/tests/accessors/mandatory_setters_test.js @@ -8,8 +8,8 @@ QUnit.module('mandatory-setters'); function hasMandatorySetter(object, property) { var meta = metaFor(object); - - return property in meta.values; + let values = meta.getValues(); + return values && property in values; } if (isEnabled('mandatory-setter')) { @@ -114,7 +114,7 @@ if (isEnabled('mandatory-setter')) { }); watch(obj, 'someProp'); - ok(!('someProp' in meta.values), 'blastix'); + ok(!('someProp' in meta.getValues()), 'blastix'); }); QUnit.test('sets up mandatory-setter if property comes from prototype', function() { @@ -131,7 +131,7 @@ if (isEnabled('mandatory-setter')) { watch(obj2, 'someProp'); var meta = metaFor(obj2); - ok(('someProp' in meta.values), 'mandatory setter has been setup'); + ok(('someProp' in meta.getValues()), 'mandatory setter has been setup'); expectAssertion(function() { obj2.someProp = 'foo-bar'; From bbfb0e701973587dd0f89518cd5d5fffeb0b3f34 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 19:04:01 -0400 Subject: [PATCH 14/39] typos --- packages/ember-metal/lib/meta.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index eeb14f09505..4a0bd2480b5 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -86,7 +86,7 @@ function ownMap(name, Meta) { }; } -// Implements a member that is lazily created POJO with inheritable +// Implements a member that is a lazily created POJO with inheritable // values. For member `thing` you get methods `getThing`, // `getOrCreateThing`, and `peekThing`. function inheritedMap(name, Meta) { @@ -105,7 +105,7 @@ function inheritedMap(name, Meta) { return ret; }; - let getId = Meta.prototype['get' + capitalized] = function() { + let getIt = Meta.prototype['get' + capitalized] = function() { let pointer = this; while (pointer) { if (pointer[key]) { @@ -116,7 +116,7 @@ function inheritedMap(name, Meta) { }; Meta.prototype['peek' + capitalized] = function(subkey) { - let map = getId.apply(this); + let map = getIt.apply(this); if (map) { return map[subkey]; } From c40512a349b56cce234e071f5365f51a8f4193ec Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 19:18:41 -0400 Subject: [PATCH 15/39] extra reusable bits --- packages/ember-metal/lib/meta.js | 64 ++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index 4a0bd2480b5..67160a0d593 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -72,18 +72,18 @@ function Meta(obj, parentMeta) { function ownMap(name, Meta) { let key = memberProperty(name); let capitalized = capitalize(name); - Meta.prototype['getOrCreate' + capitalized] = function() { - let ret = this[key]; - if (!ret) { - ret = this[key] = Object.create(null); - } - return ret; + return getOrCreateOwnMap.call(this, key); }; + Meta.prototype['get' + capitalized] = function() { return this[key]; }; +} - Meta.prototype['get' + capitalized] = function() { - return this[key]; - }; +function getOrCreateOwnMap(key) { + let ret = this[key]; + if (!ret) { + ret = this[key] = Object.create(null); + } + return ret; } // Implements a member that is a lazily created POJO with inheritable @@ -93,30 +93,16 @@ function inheritedMap(name, Meta) { let key = memberProperty(name); let capitalized = capitalize(name); - let getOrCreate = Meta.prototype['getOrCreate' + capitalized] = function() { - let ret = this[key]; - if (!ret) { - if (this.parent) { - ret = this[key] = Object.create(getOrCreate.apply(this.parent)); - } else { - ret = this[key] = Object.create(null); - } - } - return ret; + Meta.prototype['getOrCreate' + capitalized] = function() { + return getOrCreateInheritedMap.call(this, key); }; - let getIt = Meta.prototype['get' + capitalized] = function() { - let pointer = this; - while (pointer) { - if (pointer[key]) { - return pointer[key]; - } - pointer = pointer.parent; - } + Meta.prototype['get' + capitalized] = function() { + return getInheritedMap.call(this, key); }; Meta.prototype['peek' + capitalized] = function(subkey) { - let map = getIt.apply(this); + let map = getInheritedMap.call(this, key); if (map) { return map[subkey]; } @@ -127,6 +113,28 @@ function inheritedMap(name, Meta) { }; } +function getOrCreateInheritedMap(key) { + let ret = this[key]; + if (!ret) { + if (this.parent) { + ret = this[key] = Object.create(getOrCreateInheritedMap.call(this.parent, key)); + } else { + ret = this[key] = Object.create(null); + } + } + return ret; +} + +function getInheritedMap(key) { + let pointer = this; + while (pointer) { + if (pointer[key]) { + return pointer[key]; + } + pointer = pointer.parent; + } +} + function memberProperty(name) { return '_' + name; } From bab0092319fd950192ea8a8994fbe5da87e2b3fd Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 19:38:07 -0400 Subject: [PATCH 16/39] encapsulate meta.listeners --- packages/ember-metal/lib/events.js | 38 +++++------------------ packages/ember-metal/lib/meta.js | 38 ++++++++++++++++++----- packages/ember-metal/tests/events_test.js | 4 +-- 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/packages/ember-metal/lib/events.js b/packages/ember-metal/lib/events.js index 13623428963..2056239c24f 100644 --- a/packages/ember-metal/lib/events.js +++ b/packages/ember-metal/lib/events.js @@ -52,36 +52,12 @@ function indexOf(array, target, method) { } function actionsFor(obj, eventName) { - var meta = metaFor(obj, true); - var actions; - var listeners = meta.listeners; - - if (!listeners) { - listeners = meta.listeners = Object.create(null); - listeners.__source__ = obj; - } else if (listeners.__source__ !== obj) { - // setup inherited copy of the listeners object - listeners = meta.listeners = Object.create(listeners); - listeners.__source__ = obj; - } - - actions = listeners[eventName]; - - // if there are actions, but the eventName doesn't exist in our listeners, then copy them from the prototype - if (actions && actions.__source__ !== obj) { - actions = listeners[eventName] = listeners[eventName].slice(); - actions.__source__ = obj; - } else if (!actions) { - actions = listeners[eventName] = []; - actions.__source__ = obj; - } - - return actions; + return metaFor(obj, true).getOrCreateListeners(eventName); } export function accumulateListeners(obj, eventName, otherActions) { var meta = obj['__ember_meta__']; - var actions = meta && meta.listeners && meta.listeners[eventName]; + var actions = meta && meta.getListeners(eventName); if (!actions) { return; } @@ -180,7 +156,7 @@ export function removeListener(obj, eventName, target, method) { _removeListener(target, method); } else { var meta = obj['__ember_meta__']; - var actions = meta && meta.listeners && meta.listeners[eventName]; + var actions = meta && meta.getListeners(eventName); if (!actions) { return; } for (var i = actions.length - 3; i >= 0; i -= 3) { @@ -282,7 +258,7 @@ export function suspendListeners(obj, eventNames, target, method, callback) { @param obj */ export function watchedEvents(obj) { - var listeners = obj['__ember_meta__'].listeners; + var listeners = obj['__ember_meta__'].getAllListeners(); var ret = []; if (listeners) { @@ -314,7 +290,7 @@ export function watchedEvents(obj) { export function sendEvent(obj, eventName, params, actions) { if (!actions) { var meta = obj['__ember_meta__']; - actions = meta && meta.listeners && meta.listeners[eventName]; + actions = meta && meta.getListeners(eventName); } if (!actions) { return; } @@ -354,7 +330,7 @@ export function sendEvent(obj, eventName, params, actions) { */ export function hasListeners(obj, eventName) { var meta = obj['__ember_meta__']; - var actions = meta && meta.listeners && meta.listeners[eventName]; + var actions = meta && meta.getListeners(eventName); return !!(actions && actions.length); } @@ -369,7 +345,7 @@ export function hasListeners(obj, eventName) { export function listenersFor(obj, eventName) { var ret = []; var meta = obj['__ember_meta__']; - var actions = meta && meta.listeners && meta.listeners[eventName]; + var actions = meta && meta.getListeners(eventName); if (!actions) { return ret; } diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index 67160a0d593..9a2301bdbc7 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -14,7 +14,8 @@ let members = { watching: inheritedMap, mixins: inheritedMap, bindings: inheritedMap, - values: inheritedMap + values: inheritedMap, + listeners: inheritedMapOfLists }; let memberNames = Object.keys(members); @@ -32,12 +33,6 @@ function Meta(obj, parentMeta) { // map of maps, with two-level prototypical inheritance, cloned on access this.deps = undefined; - // map from keys to lists. Has own __source__ property that's used - // to distinguish whether it is being inherited or not. Each list - // also has a __source__property. Both levels are inherited on - // demand with o_create. - this.listeners = undefined; - // instance of ChainNode, inherited on demand via ChainNode.copy this.chains = undefined; @@ -135,6 +130,35 @@ function getInheritedMap(key) { } } +// Implements a member that provides a lazily created mapping from +// keys to lists, with inheritance at both levels. +function inheritedMapOfLists(name, Meta) { + let key = memberProperty(name); + let capitalized = capitalize(name); + + Meta.prototype['getOrCreate' + capitalized] = function(subkey) { + let map = getOrCreateInheritedMap.call(this, key); + let list = map[subkey]; + if (!list) { + list = map[subkey] = []; + } else if (!Object.hasOwnProperty.call(map, subkey)) { + list = map[subkey] = list.slice(); + } + return list; + }; + + Meta.prototype['get' + capitalized] = function(subkey) { + let map = getInheritedMap.call(this, key); + if (map) { + return map[subkey]; + } + }; + + Meta.prototype['getAll' + capitalized] = function() { + return getInheritedMap.call(this, key); + }; +} + function memberProperty(name) { return '_' + name; } diff --git a/packages/ember-metal/tests/events_test.js b/packages/ember-metal/tests/events_test.js index d53334fa804..f118d9b653c 100644 --- a/packages/ember-metal/tests/events_test.js +++ b/packages/ember-metal/tests/events_test.js @@ -205,7 +205,7 @@ QUnit.test('while suspended, it should not be possible to add a duplicate listen suspendListener(obj, 'event!', target, target.method, callback); equal(target.count, 1, 'should invoke'); - equal(meta(obj).listeners['event!'].length, 3, 'a duplicate listener wasn\'t added'); + equal(meta(obj).getListeners('event!').length, 3, 'a duplicate listener wasn\'t added'); // now test suspendListeners... @@ -214,7 +214,7 @@ QUnit.test('while suspended, it should not be possible to add a duplicate listen suspendListeners(obj, ['event!'], target, target.method, callback); equal(target.count, 2, 'should have invoked again'); - equal(meta(obj).listeners['event!'].length, 3, 'a duplicate listener wasn\'t added'); + equal(meta(obj).getListeners('event!').length, 3, 'a duplicate listener wasn\'t added'); }); QUnit.test('a listener can be added as part of a mixin', function() { From 9b51c9ef0baa59be07898ba7091d89bdf5a47f00 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 19:50:00 -0400 Subject: [PATCH 17/39] encapsulate meta.deps --- packages/ember-metal/lib/dependent_keys.js | 41 ++------------------- packages/ember-metal/lib/meta.js | 36 ++++++++++++++++-- packages/ember-metal/lib/property_events.js | 6 +-- packages/ember-metal/tests/alias_test.js | 4 +- 4 files changed, 41 insertions(+), 46 deletions(-) diff --git a/packages/ember-metal/lib/dependent_keys.js b/packages/ember-metal/lib/dependent_keys.js index 9d93ed650f4..9976c8251be 100644 --- a/packages/ember-metal/lib/dependent_keys.js +++ b/packages/ember-metal/lib/dependent_keys.js @@ -17,50 +17,19 @@ import { // DEPENDENT KEYS // -// data structure: -// meta.deps = { -// 'depKey': { -// 'keyName': count, -// } -// } - -/* - This function returns a map of unique dependencies for a - given object and key. -*/ -function keysForDep(depsMeta, depKey) { - var keys = depsMeta[depKey]; - if (!keys) { - // if there are no dependencies yet for a the given key - // create a new empty list of dependencies for the key - keys = depsMeta[depKey] = {}; - } else if (!depsMeta.hasOwnProperty(depKey)) { - // otherwise if the dependency list is inherited from - // a superclass, clone the hash - keys = depsMeta[depKey] = Object.create(keys); - } - return keys; -} - -function metaForDeps(meta) { - return keysForDep(meta, 'deps'); -} - export function addDependentKeys(desc, obj, keyName, meta) { // the descriptor has a list of dependent keys, so // add all of its dependent keys. - var depsMeta, idx, len, depKey, keys; + var idx, len, depKey, keys; var depKeys = desc._dependentKeys; if (!depKeys) { return; } - depsMeta = metaForDeps(meta); - for (idx = 0, len = depKeys.length; idx < len; idx++) { depKey = depKeys[idx]; // Lookup keys meta for depKey - keys = keysForDep(depsMeta, depKey); + keys = meta.getOrCreateDeps(depKey); // Increment the number of times depKey depends on keyName. keys[keyName] = (keys[keyName] || 0) + 1; // Watch the depKey @@ -72,17 +41,15 @@ export function removeDependentKeys(desc, obj, keyName, meta) { // the descriptor has a list of dependent keys, so // remove all of its dependent keys. var depKeys = desc._dependentKeys; - var depsMeta, idx, len, depKey, keys; + var idx, len, depKey, keys; if (!depKeys) { return; } - depsMeta = metaForDeps(meta); - for (idx = 0, len = depKeys.length; idx < len; idx++) { depKey = depKeys[idx]; // Lookup keys meta for depKey - keys = keysForDep(depsMeta, depKey); + keys = meta.getOrCreateDeps(depKey); // Decrement the number of times depKey depends on keyName. keys[keyName] = (keys[keyName] || 0) - 1; // Unwatch the depKey diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index 9a2301bdbc7..154c543e22a 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -15,7 +15,8 @@ let members = { mixins: inheritedMap, bindings: inheritedMap, values: inheritedMap, - listeners: inheritedMapOfLists + listeners: inheritedMapOfLists, + deps: inheritedMapOfMaps }; let memberNames = Object.keys(members); @@ -30,9 +31,6 @@ function Meta(obj, parentMeta) { // from instance's own meta this.source = obj; - // map of maps, with two-level prototypical inheritance, cloned on access - this.deps = undefined; - // instance of ChainNode, inherited on demand via ChainNode.copy this.chains = undefined; @@ -159,6 +157,36 @@ function inheritedMapOfLists(name, Meta) { }; } + +// Implements a member that provides a lazily created map of maps, +// with inheritance at both levels. +function inheritedMapOfMaps(name, Meta) { + let key = memberProperty(name); + let capitalized = capitalize(name); + + Meta.prototype['getOrCreate' + capitalized] = function(subkey) { + let outerMap = getOrCreateInheritedMap.call(this, key); + let innerMap = outerMap[subkey]; + if (!innerMap) { + innerMap = outerMap[subkey] = Object.create(null); + } else if (!Object.hasOwnProperty.call(outerMap, subkey)) { + innerMap = outerMap[subkey] = Object.create(innerMap); + } + return innerMap; + }; + + Meta.prototype['get' + capitalized] = function(subkey) { + let map = getInheritedMap.call(this, key); + if (map) { + return map[subkey]; + } + }; + + Meta.prototype['getAll' + capitalized] = function() { + return getInheritedMap.call(this, key); + }; +} + function memberProperty(name) { return '_' + name; } diff --git a/packages/ember-metal/lib/property_events.js b/packages/ember-metal/lib/property_events.js index 2a259bd0b74..f588def99e5 100644 --- a/packages/ember-metal/lib/property_events.js +++ b/packages/ember-metal/lib/property_events.js @@ -98,7 +98,7 @@ function propertyDidChange(obj, keyName) { return; } - if (m && m.deps && m.deps[keyName]) { + if (m && m.getDeps(keyName)) { dependentKeysDidChange(obj, keyName, m); } @@ -112,7 +112,7 @@ function dependentKeysWillChange(obj, depKey, meta) { if (obj.isDestroying) { return; } var deps; - if (meta && meta.deps && (deps = meta.deps[depKey])) { + if (meta && (deps = meta.getDeps(depKey))) { var seen = WILL_SEEN; var top = !seen; @@ -133,7 +133,7 @@ function dependentKeysDidChange(obj, depKey, meta) { if (obj.isDestroying) { return; } var deps; - if (meta && meta.deps && (deps = meta.deps[depKey])) { + if (meta && (deps = meta.getDeps(depKey))) { var seen = DID_SEEN; var top = !seen; diff --git a/packages/ember-metal/tests/alias_test.js b/packages/ember-metal/tests/alias_test.js index 4efe77b35b6..8078e6ef0d3 100644 --- a/packages/ember-metal/tests/alias_test.js +++ b/packages/ember-metal/tests/alias_test.js @@ -37,9 +37,9 @@ QUnit.test('basic lifecycle', function() { defineProperty(obj, 'bar', alias('foo.faz')); var m = meta(obj); addObserver(obj, 'bar', incrementCount); - equal(m.deps['foo.faz'].bar, 1); + equal(m.getDeps('foo.faz').bar, 1); removeObserver(obj, 'bar', incrementCount); - equal(m.deps['foo.faz'].bar, 0); + equal(m.getDeps('foo.faz').bar, 0); }); QUnit.test('begins watching alt key as soon as alias is watched', function() { From 1fd3eb531c513f4677c8c168708691096abf0d57 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 20:22:56 -0400 Subject: [PATCH 18/39] encapsulate meta.chainWatchers --- packages/ember-metal/lib/chains.js | 15 +++------- packages/ember-metal/lib/computed.js | 5 ++-- packages/ember-metal/lib/meta.js | 28 +++++++++++++------ packages/ember-metal/lib/property_events.js | 20 ++++++------- .../ember-metal/tests/watching/watch_test.js | 8 +++--- 5 files changed, 40 insertions(+), 36 deletions(-) diff --git a/packages/ember-metal/lib/chains.js b/packages/ember-metal/lib/chains.js index 9884bc925ea..93ff0ce094f 100644 --- a/packages/ember-metal/lib/chains.js +++ b/packages/ember-metal/lib/chains.js @@ -137,13 +137,7 @@ function addChainWatcher(obj, keyName, node) { } let m = metaFor(obj); - - if (m.chainWatchers === undefined || m.chainWatchers.obj !== obj) { - m.chainWatchers = new ChainWatchers(obj); - } - - m.chainWatchers.add(keyName, node); - + m.getOrCreateChainWatchers(ChainWatchers).add(keyName, node); watchKey(obj, keyName, m); } @@ -154,15 +148,14 @@ function removeChainWatcher(obj, keyName, node) { let m = obj.__ember_meta__; - if (!m || - m.chainWatchers === undefined || m.chainWatchers.obj !== obj) { + if (!m || !m.getChainWatchers()) { return; } // make meta writable m = metaFor(obj); - m.chainWatchers.remove(keyName, node); + m.getChainWatchers().remove(keyName, node); unwatchKey(obj, keyName, m); } @@ -425,7 +418,7 @@ export function finishChains(obj) { let m = obj.__ember_meta__; if (m) { // finish any current chains node watchers that reference obj - let chainWatchers = m.chainWatchers; + let chainWatchers = m.getChainWatchers(); if (chainWatchers) { chainWatchers.revalidateAll(); } diff --git a/packages/ember-metal/lib/computed.js b/packages/ember-metal/lib/computed.js index 2ba3892f01e..e32240b7b0c 100644 --- a/packages/ember-metal/lib/computed.js +++ b/packages/ember-metal/lib/computed.js @@ -324,8 +324,9 @@ ComputedPropertyPrototype.get = function(obj, keyName) { cache[keyName] = ret; } - if (meta.chainWatchers) { - meta.chainWatchers.revalidate(keyName); + let chainWatchers = meta.getChainWatchers(); + if (chainWatchers) { + chainWatchers.revalidate(keyName); } addDependentKeys(this, obj, keyName, meta); } else { diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index 154c543e22a..8852f368fd7 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -16,7 +16,8 @@ let members = { bindings: inheritedMap, values: inheritedMap, listeners: inheritedMapOfLists, - deps: inheritedMapOfMaps + deps: inheritedMapOfMaps, + chainWatchers: ownCustomObject }; let memberNames = Object.keys(members); @@ -27,18 +28,12 @@ function Meta(obj, parentMeta) { this[memberProperty(memberNames[i])] = undefined; } - // used only internally by meta() to distinguish meta-from-prototype - // from instance's own meta + // used only internally this.source = obj; // instance of ChainNode, inherited on demand via ChainNode.copy this.chains = undefined; - // instanceof ChainWatchers. Created on demand with a fresh new - // ChainWatchers at each level in prototype chain, by testing - // m.chainWatchers.obj. - this.chainWatchers = undefined; - // when meta(obj).proto === obj, the object is intended to be only a // prototype and doesn't need to actually be observable itself this.proto = undefined; @@ -187,6 +182,23 @@ function inheritedMapOfMaps(name, Meta) { }; } +// Implements a member that provides a non-heritable, lazily-created +// object using the class you provide. +function ownCustomObject(name, Meta) { + let key = memberProperty(name); + let capitalized = capitalize(name); + Meta.prototype['getOrCreate' + capitalized] = function(Klass) { + let ret = this[key]; + if (!ret) { + ret = this[key] = new Klass(this.source); + } + return ret; + }; + Meta.prototype['get' + capitalized] = function() { + return this[key]; + }; +} + function memberProperty(name) { return '_' + name; } diff --git a/packages/ember-metal/lib/property_events.js b/packages/ember-metal/lib/property_events.js index f588def99e5..42af019fd05 100644 --- a/packages/ember-metal/lib/property_events.js +++ b/packages/ember-metal/lib/property_events.js @@ -191,26 +191,24 @@ function iterDeps(method, obj, deps, depKey, seen, meta) { } function chainsWillChange(obj, keyName, m) { - if (m.chainWatchers === undefined || m.chainWatchers.obj !== obj) { - return; + let c = m.getChainWatchers(); + if (c) { + c.notify(keyName, false, propertyWillChange); } - - m.chainWatchers.notify(keyName, false, propertyWillChange); } function chainsDidChange(obj, keyName, m) { - if (m.chainWatchers === undefined || m.chainWatchers.obj !== obj) { - return; + let c = m.getChainWatchers(); + if (c) { + c.notify(keyName, true, propertyDidChange); } - - m.chainWatchers.notify(keyName, true, propertyDidChange); } function overrideChains(obj, keyName, m) { - if (m.chainWatchers === undefined || m.chainWatchers.obj !== obj) { - return; + let c = m.getChainWatchers(); + if (c) { + c.revalidate(keyName); } - m.chainWatchers.revalidate(keyName); } /** diff --git a/packages/ember-metal/tests/watching/watch_test.js b/packages/ember-metal/tests/watching/watch_test.js index e4a6121d72b..5b0e16ef436 100644 --- a/packages/ember-metal/tests/watching/watch_test.js +++ b/packages/ember-metal/tests/watching/watch_test.js @@ -206,12 +206,12 @@ QUnit.test('when watching a global object, destroy should remove chain watchers var chainNode = Ember.meta(obj).chains._chains.Global._chains.foo; equal(meta_Global.peekWatching('foo'), 1, 'should be watching foo'); - equal(meta_Global.chainWatchers.has('foo', chainNode), true, 'should have chain watcher'); + equal(meta_Global.getChainWatchers().has('foo', chainNode), true, 'should have chain watcher'); destroy(obj); equal(meta_Global.peekWatching('foo'), 0, 'should not be watching foo'); - equal(meta_Global.chainWatchers.has('foo', chainNode), false, 'should not have chain watcher'); + equal(meta_Global.getChainWatchers().has('foo', chainNode), false, 'should not have chain watcher'); lookup['Global'] = Global = null; // reset }); @@ -228,12 +228,12 @@ QUnit.test('when watching another object, destroy should remove chain watchers f var chainNode = Ember.meta(objA).chains._chains.b._chains.foo; equal(meta_objB.peekWatching('foo'), 1, 'should be watching foo'); - equal(meta_objB.chainWatchers.has('foo', chainNode), true, 'should have chain watcher'); + equal(meta_objB.getChainWatchers().has('foo', chainNode), true, 'should have chain watcher'); destroy(objA); equal(meta_objB.peekWatching('foo'), 0, 'should not be watching foo'); - equal(meta_objB.chainWatchers.has('foo', chainNode), false, 'should not have chain watcher'); + equal(meta_objB.getChainWatchers().has('foo', chainNode), false, 'should not have chain watcher'); }); // TESTS for length property From a3f86cc1cc6c4724a04d7264c823cf060c315947 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 20:53:58 -0400 Subject: [PATCH 19/39] encapsulate meta.chains --- packages/ember-metal/lib/chains.js | 17 ++++--- packages/ember-metal/lib/meta.js | 49 +++++++++++++------ packages/ember-metal/lib/watch_path.js | 13 ++--- packages/ember-metal/lib/watching.js | 2 +- packages/ember-metal/tests/chains_test.js | 3 +- .../ember-metal/tests/watching/watch_test.js | 4 +- 6 files changed, 55 insertions(+), 33 deletions(-) diff --git a/packages/ember-metal/lib/chains.js b/packages/ember-metal/lib/chains.js index 93ff0ce094f..80b3f9c720b 100644 --- a/packages/ember-metal/lib/chains.js +++ b/packages/ember-metal/lib/chains.js @@ -131,13 +131,17 @@ export function flushPendingChains() { ); } +function makeChainWatcher(obj) { + return new ChainWatchers(obj); +} + function addChainWatcher(obj, keyName, node) { if (!isObject(obj)) { return; } let m = metaFor(obj); - m.getOrCreateChainWatchers(ChainWatchers).add(keyName, node); + m.getOrCreateChainWatchers(makeChainWatcher).add(keyName, node); watchKey(obj, keyName, m); } @@ -417,16 +421,17 @@ export function finishChains(obj) { // We only create meta if we really have to let m = obj.__ember_meta__; if (m) { + m = metaFor(obj); + // finish any current chains node watchers that reference obj let chainWatchers = m.getChainWatchers(); if (chainWatchers) { chainWatchers.revalidateAll(); } - // copy chains from prototype - let chains = m.chains; - if (chains && chains.value() !== obj) { - // need to check if meta is writable - metaFor(obj).chains = chains.copy(obj); + // ensure that if we have inherited any chains they have been + // copied onto our own meta. + if (m.getChains()) { + m.getOrCreateChains(); } } } diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index 8852f368fd7..727c63c9766 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -17,7 +17,8 @@ let members = { values: inheritedMap, listeners: inheritedMapOfLists, deps: inheritedMapOfMaps, - chainWatchers: ownCustomObject + chainWatchers: ownCustomObject, + chains: inheritedCustomObject }; let memberNames = Object.keys(members); @@ -31,9 +32,6 @@ function Meta(obj, parentMeta) { // used only internally this.source = obj; - // instance of ChainNode, inherited on demand via ChainNode.copy - this.chains = undefined; - // when meta(obj).proto === obj, the object is intended to be only a // prototype and doesn't need to actually be observable itself this.proto = undefined; @@ -86,11 +84,11 @@ function inheritedMap(name, Meta) { }; Meta.prototype['get' + capitalized] = function() { - return getInheritedMap.call(this, key); + return getInherited.call(this, key); }; Meta.prototype['peek' + capitalized] = function(subkey) { - let map = getInheritedMap.call(this, key); + let map = getInherited.call(this, key); if (map) { return map[subkey]; } @@ -113,7 +111,7 @@ function getOrCreateInheritedMap(key) { return ret; } -function getInheritedMap(key) { +function getInherited(key) { let pointer = this; while (pointer) { if (pointer[key]) { @@ -141,14 +139,14 @@ function inheritedMapOfLists(name, Meta) { }; Meta.prototype['get' + capitalized] = function(subkey) { - let map = getInheritedMap.call(this, key); + let map = getInherited.call(this, key); if (map) { return map[subkey]; } }; Meta.prototype['getAll' + capitalized] = function() { - return getInheritedMap.call(this, key); + return getInherited.call(this, key); }; } @@ -171,26 +169,26 @@ function inheritedMapOfMaps(name, Meta) { }; Meta.prototype['get' + capitalized] = function(subkey) { - let map = getInheritedMap.call(this, key); + let map = getInherited.call(this, key); if (map) { return map[subkey]; } }; Meta.prototype['getAll' + capitalized] = function() { - return getInheritedMap.call(this, key); + return getInherited.call(this, key); }; } // Implements a member that provides a non-heritable, lazily-created -// object using the class you provide. +// object using the method you provide. function ownCustomObject(name, Meta) { let key = memberProperty(name); let capitalized = capitalize(name); - Meta.prototype['getOrCreate' + capitalized] = function(Klass) { + Meta.prototype['getOrCreate' + capitalized] = function(create) { let ret = this[key]; if (!ret) { - ret = this[key] = new Klass(this.source); + ret = this[key] = create(this.source); } return ret; }; @@ -199,6 +197,29 @@ function ownCustomObject(name, Meta) { }; } +// Implements a member that provides an inheritable, lazily-created +// object using the method you provide. We will derived children from +// their parents by calling your object's `copy()` method. +function inheritedCustomObject(name, Meta) { + let key = memberProperty(name); + let capitalized = capitalize(name); + let getOrCreate = Meta.prototype['getOrCreate' + capitalized] = function(create) { + let ret = this[key]; + if (!ret) { + if (this.parent) { + ret = this[key] = getOrCreate.call(this.parent, create).copy(this.source); + } else { + ret = this[key] = create(this.source); + } + } + return ret; + }; + Meta.prototype['get' + capitalized] = function() { + return getInherited.call(this, key); + }; +} + + function memberProperty(name) { return '_' + name; } diff --git a/packages/ember-metal/lib/watch_path.js b/packages/ember-metal/lib/watch_path.js index c40194b5af5..7773793b3f5 100644 --- a/packages/ember-metal/lib/watch_path.js +++ b/packages/ember-metal/lib/watch_path.js @@ -7,14 +7,11 @@ import { ChainNode } from 'ember-metal/chains'; // chains inherited from the proto they will be cloned and reconfigured for // the current object. function chainsFor(obj, meta) { - var m = meta || metaFor(obj); - var ret = m.chains; - if (!ret) { - ret = m.chains = new ChainNode(null, null, obj); - } else if (ret.value() !== obj) { - ret = m.chains = ret.copy(obj); - } - return ret; + return (meta || metaFor(obj)).getOrCreateChains(makeChainNode); +} + +function makeChainNode(obj) { + return new ChainNode(null, null, obj); } export function watchPath(obj, keyPath, meta) { diff --git a/packages/ember-metal/lib/watching.js b/packages/ember-metal/lib/watching.js index 74c247d8033..1a470b9fa55 100644 --- a/packages/ember-metal/lib/watching.js +++ b/packages/ember-metal/lib/watching.js @@ -81,7 +81,7 @@ export function destroy(obj) { if (meta) { obj['__ember_meta__'] = null; // remove chainWatchers to remove circular references that would prevent GC - node = meta.chains; + node = meta.getChains(); if (node) { NODE_STACK.push(node); // process tree diff --git a/packages/ember-metal/tests/chains_test.js b/packages/ember-metal/tests/chains_test.js index 774b62c1472..446612f0bb1 100644 --- a/packages/ember-metal/tests/chains_test.js +++ b/packages/ember-metal/tests/chains_test.js @@ -14,8 +14,7 @@ QUnit.test('finishChains should properly copy chains from prototypes to instance var childObj = Object.create(obj); finishChains(childObj); - - ok(obj['__ember_meta__'].chains !== childObj['__ember_meta__'].chains, 'The chains object is copied'); + ok(obj['__ember_meta__'].getChains() !== childObj['__ember_meta__'].getChains(), 'The chains object is copied'); }); diff --git a/packages/ember-metal/tests/watching/watch_test.js b/packages/ember-metal/tests/watching/watch_test.js index 5b0e16ef436..199a130232b 100644 --- a/packages/ember-metal/tests/watching/watch_test.js +++ b/packages/ember-metal/tests/watching/watch_test.js @@ -203,7 +203,7 @@ QUnit.test('when watching a global object, destroy should remove chain watchers watch(obj, 'Global.foo'); var meta_Global = Ember.meta(Global); - var chainNode = Ember.meta(obj).chains._chains.Global._chains.foo; + var chainNode = Ember.meta(obj).getChains()._chains.Global._chains.foo; equal(meta_Global.peekWatching('foo'), 1, 'should be watching foo'); equal(meta_Global.getChainWatchers().has('foo', chainNode), true, 'should have chain watcher'); @@ -225,7 +225,7 @@ QUnit.test('when watching another object, destroy should remove chain watchers f watch(objA, 'b.foo'); var meta_objB = Ember.meta(objB); - var chainNode = Ember.meta(objA).chains._chains.b._chains.foo; + var chainNode = Ember.meta(objA).getChains()._chains.b._chains.foo; equal(meta_objB.peekWatching('foo'), 1, 'should be watching foo'); equal(meta_objB.getChainWatchers().has('foo', chainNode), true, 'should have chain watcher'); From 93743c1de5324d2dc5c7ad9e82a75c9adc7ef908 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 20:58:19 -0400 Subject: [PATCH 20/39] break the meta prototype chain! --- packages/ember-metal/lib/meta.js | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index 727c63c9766..f5a31717cd2 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -285,17 +285,7 @@ export function meta(obj, writable) { ret.getOrCreateValues(); } } else if (ret.source !== obj) { - // temporary dance until I can eliminate remaining uses of - // prototype chain - let newRet = Object.create(ret); - newRet.parent = ret; - for (let i = 0; i < memberNames.length; i++) { - newRet[memberProperty(memberNames[i])] = undefined; - } - ret = newRet; - // end temporary dance - - ret.source = obj; + ret = new Meta(obj, ret); } obj.__ember_meta__ = ret; return ret; From d51a492e0b26a3d7f1fb90a9bd0c4ffb84eb458f Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 21:09:32 -0400 Subject: [PATCH 21/39] better naming --- packages/ember-metal/lib/chains.js | 2 +- packages/ember-metal/lib/computed.js | 12 ++++++------ packages/ember-metal/lib/meta.js | 7 +++---- packages/ember-runtime/lib/system/core_object.js | 2 +- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/ember-metal/lib/chains.js b/packages/ember-metal/lib/chains.js index 80b3f9c720b..d4bbfe62e53 100644 --- a/packages/ember-metal/lib/chains.js +++ b/packages/ember-metal/lib/chains.js @@ -219,7 +219,7 @@ function lazyGet(obj, key) { return get(obj, key); // Otherwise attempt to get the cached value of the computed property } else { - let cache = meta.getCache(); + let cache = meta.readableCache(); if (cache && key in cache) { return cache[key]; } diff --git a/packages/ember-metal/lib/computed.js b/packages/ember-metal/lib/computed.js index e32240b7b0c..29b7d1e6b75 100644 --- a/packages/ember-metal/lib/computed.js +++ b/packages/ember-metal/lib/computed.js @@ -267,7 +267,7 @@ ComputedPropertyPrototype.didChange = function(obj, keyName) { // the cached value set by the setter if (this._cacheable && this._suspended !== obj) { let meta = metaFor(obj); - let cache = meta.getCache(); + let cache = meta.readableCache(); if (cache && cache[keyName] !== undefined) { cache[keyName] = undefined; removeDependentKeys(this, obj, keyName, meta); @@ -306,7 +306,7 @@ ComputedPropertyPrototype.get = function(obj, keyName) { var ret, cache, meta; if (this._cacheable) { meta = metaFor(obj); - cache = meta.getOrCreateCache(); + cache = meta.writableCache(); var result = cache[keyName]; @@ -400,7 +400,7 @@ ComputedPropertyPrototype._set = function computedPropertySet(obj, keyName, valu var cacheable = this._cacheable; var setter = this._setter; var meta = metaFor(obj, cacheable); - var cache = meta.getCache(); + var cache = meta.readableCache(); var hadCachedValue = false; var cachedValue, ret; @@ -440,7 +440,7 @@ ComputedPropertyPrototype._set = function computedPropertySet(obj, keyName, valu addDependentKeys(this, obj, keyName, meta); } if (!cache) { - cache = meta.getOrCreateCache(); + cache = meta.writableCache(); } if (ret === undefined) { cache[keyName] = UNDEFINED; @@ -459,7 +459,7 @@ ComputedPropertyPrototype._set = function computedPropertySet(obj, keyName, valu /* called before property is overridden */ ComputedPropertyPrototype.teardown = function(obj, keyName) { var meta = metaFor(obj); - let cache = meta.getCache(); + let cache = meta.readableCache(); if (cache) { if (keyName in cache) { removeDependentKeys(this, obj, keyName, meta); @@ -559,7 +559,7 @@ export default function computed(func) { */ function cacheFor(obj, key) { var meta = obj['__ember_meta__']; - var cache = meta && meta.getCache(); + var cache = meta && meta.readableCache(); var ret = cache && cache[key]; if (ret === UNDEFINED) { diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index f5a31717cd2..c399ce7faa5 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -53,15 +53,14 @@ function Meta(obj, parentMeta) { // Implements a member that is a lazily created, non-inheritable -// POJO. For member `thing` you get methods `getThing` and -// `getOrCreateThing`. +// POJO. function ownMap(name, Meta) { let key = memberProperty(name); let capitalized = capitalize(name); - Meta.prototype['getOrCreate' + capitalized] = function() { + Meta.prototype['writable' + capitalized] = function() { return getOrCreateOwnMap.call(this, key); }; - Meta.prototype['get' + capitalized] = function() { return this[key]; }; + Meta.prototype['readable' + capitalized] = function() { return this[key]; }; } function getOrCreateOwnMap(key) { diff --git a/packages/ember-runtime/lib/system/core_object.js b/packages/ember-runtime/lib/system/core_object.js index d2a8fef8168..9b328b8026e 100644 --- a/packages/ember-runtime/lib/system/core_object.js +++ b/packages/ember-runtime/lib/system/core_object.js @@ -853,7 +853,7 @@ CoreObject.reopen({ didDefineProperty(proto, key, value) { if (hasCachedComputedProperties === false) { return; } if (value instanceof Ember.ComputedProperty) { - var cache = Ember.meta(this.constructor).getCache(); + var cache = Ember.meta(this.constructor).readableCache(); if (cache && cache._computedProperties !== undefined) { cache._computedProperties = undefined; From f216afccb12919bd7dcd60d0b4686f074c12919b Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 21:15:26 -0400 Subject: [PATCH 22/39] better naming for inheritedMap accessors --- packages/ember-metal/lib/meta.js | 8 ++++---- packages/ember-metal/lib/mixin.js | 8 ++++---- packages/ember-metal/lib/properties.js | 2 +- packages/ember-metal/lib/property_set.js | 2 +- packages/ember-metal/lib/watch_key.js | 8 ++++---- packages/ember-metal/lib/watch_path.js | 4 ++-- .../ember-metal/tests/accessors/mandatory_setters_test.js | 6 +++--- packages/ember-runtime/lib/system/core_object.js | 2 +- 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index c399ce7faa5..80925ccce38 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -78,11 +78,11 @@ function inheritedMap(name, Meta) { let key = memberProperty(name); let capitalized = capitalize(name); - Meta.prototype['getOrCreate' + capitalized] = function() { + Meta.prototype['writable' + capitalized] = function() { return getOrCreateInheritedMap.call(this, key); }; - Meta.prototype['get' + capitalized] = function() { + Meta.prototype['readable' + capitalized] = function() { return getInherited.call(this, key); }; @@ -245,7 +245,7 @@ var EMBER_META_PROPERTY = { export var EMPTY_META = new Meta(null); if (isEnabled('mandatory-setter')) { - EMPTY_META.getOrCreateValues(); + EMPTY_META.writableValues(); } /** @@ -281,7 +281,7 @@ export function meta(obj, writable) { if (!ret) { ret = new Meta(obj); if (isEnabled('mandatory-setter')) { - ret.getOrCreateValues(); + ret.writableValues(); } } else if (ret.source !== obj) { ret = new Meta(obj, ret); diff --git a/packages/ember-metal/lib/mixin.js b/packages/ember-metal/lib/mixin.js index 3aba637b6f2..1b986b562b3 100644 --- a/packages/ember-metal/lib/mixin.js +++ b/packages/ember-metal/lib/mixin.js @@ -74,7 +74,7 @@ superFunction.call(primer, 1, 2); superFunction.call(primer, 1, 2, 3); function mixinsMeta(obj) { - return metaFor(obj, true).getOrCreateMixins(); + return metaFor(obj, true).writableMixins(); } function isMethod(obj) { @@ -309,7 +309,7 @@ var IS_BINDING = /^.+Binding$/; function detectBinding(obj, key, value, m) { if (IS_BINDING.test(key)) { - m.getOrCreateBindings()[key] = value; + m.writableBindings()[key] = value; } } @@ -340,7 +340,7 @@ function connectStreamBinding(obj, key, stream) { function connectBindings(obj, m) { // TODO Mixin.apply(instance) should disconnect binding if exists - var bindings = m.getBindings(); + var bindings = m.readableBindings(); var key, binding, to; if (bindings) { for (key in bindings) { @@ -696,7 +696,7 @@ MixinPrototype.keys = function() { // TODO: Make Ember.mixin Mixin.mixins = function(obj) { var m = obj['__ember_meta__']; - var mixins = m && m.getMixins(); + var mixins = m && m.readableMixins(); var ret = []; if (!mixins) { return ret; } diff --git a/packages/ember-metal/lib/properties.js b/packages/ember-metal/lib/properties.js index abb25b1f0cf..cd7e8b14ec4 100644 --- a/packages/ember-metal/lib/properties.js +++ b/packages/ember-metal/lib/properties.js @@ -122,7 +122,7 @@ export function defineProperty(obj, keyName, desc, data, meta) { if (isEnabled('mandatory-setter')) { if (watching) { - meta.getOrCreateValues()[keyName] = data; + meta.writableValues()[keyName] = data; Object.defineProperty(obj, keyName, { configurable: true, enumerable: true, diff --git a/packages/ember-metal/lib/property_set.js b/packages/ember-metal/lib/property_set.js index 2ebbafebd2c..16d591324ed 100644 --- a/packages/ember-metal/lib/property_set.js +++ b/packages/ember-metal/lib/property_set.js @@ -81,7 +81,7 @@ export function set(obj, keyName, value, tolerant) { ) { defineProperty(obj, keyName, null, value); // setup mandatory setter } else { - meta.getOrCreateValues()[keyName] = value; + meta.writableValues()[keyName] = value; } } else { obj[keyName] = value; diff --git a/packages/ember-metal/lib/watch_key.js b/packages/ember-metal/lib/watch_key.js index 873b82cd519..2773a61efef 100644 --- a/packages/ember-metal/lib/watch_key.js +++ b/packages/ember-metal/lib/watch_key.js @@ -12,7 +12,7 @@ export function watchKey(obj, keyName, meta) { if (keyName === 'length' && Array.isArray(obj)) { return; } var m = meta || metaFor(obj); - var watching = m.getOrCreateWatching(); + var watching = m.writableWatching(); // activate watching first time if (!watching[keyName]) { @@ -48,7 +48,7 @@ if (isEnabled('mandatory-setter')) { // this x in Y deopts, so keeping it in this function is better; if (configurable && isWritable && hasValue && keyName in obj) { - m.getOrCreateValues()[keyName] = obj[keyName]; + m.writableValues()[keyName] = obj[keyName]; Object.defineProperty(obj, keyName, { configurable: true, enumerable: Object.prototype.propertyIsEnumerable.call(obj, keyName), @@ -65,7 +65,7 @@ if (isEnabled('mandatory-setter')) { export function unwatchKey(obj, keyName, meta) { var m = meta || metaFor(obj); - var watching = m.getOrCreateWatching(); + var watching = m.writableWatching(); if (watching[keyName] === 1) { watching[keyName] = 0; @@ -91,7 +91,7 @@ export function unwatchKey(obj, keyName, meta) { enumerable: true, value: val }); - delete m.getOrCreateValues()[keyName]; + delete m.writableValues()[keyName]; }, get: DEFAULT_GETTER_FUNCTION(keyName) }); diff --git a/packages/ember-metal/lib/watch_path.js b/packages/ember-metal/lib/watch_path.js index 7773793b3f5..4e9957f9325 100644 --- a/packages/ember-metal/lib/watch_path.js +++ b/packages/ember-metal/lib/watch_path.js @@ -19,7 +19,7 @@ export function watchPath(obj, keyPath, meta) { if (keyPath === 'length' && Array.isArray(obj)) { return; } var m = meta || metaFor(obj); - var watching = m.getOrCreateWatching(); + var watching = m.writableWatching(); if (!watching[keyPath]) { // activate watching first time watching[keyPath] = 1; @@ -31,7 +31,7 @@ export function watchPath(obj, keyPath, meta) { export function unwatchPath(obj, keyPath, meta) { var m = meta || metaFor(obj); - var watching = m.getOrCreateWatching(); + var watching = m.writableWatching(); if (watching[keyPath] === 1) { watching[keyPath] = 0; diff --git a/packages/ember-metal/tests/accessors/mandatory_setters_test.js b/packages/ember-metal/tests/accessors/mandatory_setters_test.js index 21df14d4058..3bafd745ae5 100644 --- a/packages/ember-metal/tests/accessors/mandatory_setters_test.js +++ b/packages/ember-metal/tests/accessors/mandatory_setters_test.js @@ -8,7 +8,7 @@ QUnit.module('mandatory-setters'); function hasMandatorySetter(object, property) { var meta = metaFor(object); - let values = meta.getValues(); + let values = meta.readableValues(); return values && property in values; } @@ -114,7 +114,7 @@ if (isEnabled('mandatory-setter')) { }); watch(obj, 'someProp'); - ok(!('someProp' in meta.getValues()), 'blastix'); + ok(!('someProp' in meta.readableValues()), 'blastix'); }); QUnit.test('sets up mandatory-setter if property comes from prototype', function() { @@ -131,7 +131,7 @@ if (isEnabled('mandatory-setter')) { watch(obj2, 'someProp'); var meta = metaFor(obj2); - ok(('someProp' in meta.getValues()), 'mandatory setter has been setup'); + ok(('someProp' in meta.readableValues()), 'mandatory setter has been setup'); expectAssertion(function() { obj2.someProp = 'foo-bar'; diff --git a/packages/ember-runtime/lib/system/core_object.js b/packages/ember-runtime/lib/system/core_object.js index 9b328b8026e..09e4c0ce43f 100644 --- a/packages/ember-runtime/lib/system/core_object.js +++ b/packages/ember-runtime/lib/system/core_object.js @@ -104,7 +104,7 @@ function makeCtor() { var value = properties[keyName]; if (IS_BINDING.test(keyName)) { - m.getOrCreateBindings()[keyName] = value; + m.writableBindings()[keyName] = value; } var possibleDesc = this[keyName]; From a4a48544d1b887f8c0547278035d65d65220afd9 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 21:17:44 -0400 Subject: [PATCH 23/39] better naming for inheritMapOfLists accessors --- packages/ember-metal/lib/events.js | 12 ++++++------ packages/ember-metal/lib/meta.js | 4 ++-- packages/ember-metal/tests/events_test.js | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/ember-metal/lib/events.js b/packages/ember-metal/lib/events.js index 2056239c24f..fa9a217cc8e 100644 --- a/packages/ember-metal/lib/events.js +++ b/packages/ember-metal/lib/events.js @@ -52,12 +52,12 @@ function indexOf(array, target, method) { } function actionsFor(obj, eventName) { - return metaFor(obj, true).getOrCreateListeners(eventName); + return metaFor(obj, true).writableListeners(eventName); } export function accumulateListeners(obj, eventName, otherActions) { var meta = obj['__ember_meta__']; - var actions = meta && meta.getListeners(eventName); + var actions = meta && meta.readableListeners(eventName); if (!actions) { return; } @@ -156,7 +156,7 @@ export function removeListener(obj, eventName, target, method) { _removeListener(target, method); } else { var meta = obj['__ember_meta__']; - var actions = meta && meta.getListeners(eventName); + var actions = meta && meta.readableListeners(eventName); if (!actions) { return; } for (var i = actions.length - 3; i >= 0; i -= 3) { @@ -290,7 +290,7 @@ export function watchedEvents(obj) { export function sendEvent(obj, eventName, params, actions) { if (!actions) { var meta = obj['__ember_meta__']; - actions = meta && meta.getListeners(eventName); + actions = meta && meta.readableListeners(eventName); } if (!actions) { return; } @@ -330,7 +330,7 @@ export function sendEvent(obj, eventName, params, actions) { */ export function hasListeners(obj, eventName) { var meta = obj['__ember_meta__']; - var actions = meta && meta.getListeners(eventName); + var actions = meta && meta.readableListeners(eventName); return !!(actions && actions.length); } @@ -345,7 +345,7 @@ export function hasListeners(obj, eventName) { export function listenersFor(obj, eventName) { var ret = []; var meta = obj['__ember_meta__']; - var actions = meta && meta.getListeners(eventName); + var actions = meta && meta.readableListeners(eventName); if (!actions) { return ret; } diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index 80925ccce38..b9078ced634 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -126,7 +126,7 @@ function inheritedMapOfLists(name, Meta) { let key = memberProperty(name); let capitalized = capitalize(name); - Meta.prototype['getOrCreate' + capitalized] = function(subkey) { + Meta.prototype['writable' + capitalized] = function(subkey) { let map = getOrCreateInheritedMap.call(this, key); let list = map[subkey]; if (!list) { @@ -137,7 +137,7 @@ function inheritedMapOfLists(name, Meta) { return list; }; - Meta.prototype['get' + capitalized] = function(subkey) { + Meta.prototype['readable' + capitalized] = function(subkey) { let map = getInherited.call(this, key); if (map) { return map[subkey]; diff --git a/packages/ember-metal/tests/events_test.js b/packages/ember-metal/tests/events_test.js index f118d9b653c..ead7da29c05 100644 --- a/packages/ember-metal/tests/events_test.js +++ b/packages/ember-metal/tests/events_test.js @@ -205,7 +205,7 @@ QUnit.test('while suspended, it should not be possible to add a duplicate listen suspendListener(obj, 'event!', target, target.method, callback); equal(target.count, 1, 'should invoke'); - equal(meta(obj).getListeners('event!').length, 3, 'a duplicate listener wasn\'t added'); + equal(meta(obj).readableListeners('event!').length, 3, 'a duplicate listener wasn\'t added'); // now test suspendListeners... @@ -214,7 +214,7 @@ QUnit.test('while suspended, it should not be possible to add a duplicate listen suspendListeners(obj, ['event!'], target, target.method, callback); equal(target.count, 2, 'should have invoked again'); - equal(meta(obj).getListeners('event!').length, 3, 'a duplicate listener wasn\'t added'); + equal(meta(obj).readableListeners('event!').length, 3, 'a duplicate listener wasn\'t added'); }); QUnit.test('a listener can be added as part of a mixin', function() { From 2d70d56c3da1b0910d6592dfffc4c380afa19e49 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 21:19:56 -0400 Subject: [PATCH 24/39] better naming for inheritedMapOfMaps accessors --- packages/ember-metal/lib/dependent_keys.js | 4 ++-- packages/ember-metal/lib/meta.js | 4 ++-- packages/ember-metal/lib/property_events.js | 6 +++--- packages/ember-metal/tests/alias_test.js | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/ember-metal/lib/dependent_keys.js b/packages/ember-metal/lib/dependent_keys.js index 9976c8251be..6a7990b8fcd 100644 --- a/packages/ember-metal/lib/dependent_keys.js +++ b/packages/ember-metal/lib/dependent_keys.js @@ -29,7 +29,7 @@ export function addDependentKeys(desc, obj, keyName, meta) { for (idx = 0, len = depKeys.length; idx < len; idx++) { depKey = depKeys[idx]; // Lookup keys meta for depKey - keys = meta.getOrCreateDeps(depKey); + keys = meta.writableDeps(depKey); // Increment the number of times depKey depends on keyName. keys[keyName] = (keys[keyName] || 0) + 1; // Watch the depKey @@ -49,7 +49,7 @@ export function removeDependentKeys(desc, obj, keyName, meta) { for (idx = 0, len = depKeys.length; idx < len; idx++) { depKey = depKeys[idx]; // Lookup keys meta for depKey - keys = meta.getOrCreateDeps(depKey); + keys = meta.writableDeps(depKey); // Decrement the number of times depKey depends on keyName. keys[keyName] = (keys[keyName] || 0) - 1; // Unwatch the depKey diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index b9078ced634..21e31b2e767 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -156,7 +156,7 @@ function inheritedMapOfMaps(name, Meta) { let key = memberProperty(name); let capitalized = capitalize(name); - Meta.prototype['getOrCreate' + capitalized] = function(subkey) { + Meta.prototype['writable' + capitalized] = function(subkey) { let outerMap = getOrCreateInheritedMap.call(this, key); let innerMap = outerMap[subkey]; if (!innerMap) { @@ -167,7 +167,7 @@ function inheritedMapOfMaps(name, Meta) { return innerMap; }; - Meta.prototype['get' + capitalized] = function(subkey) { + Meta.prototype['readable' + capitalized] = function(subkey) { let map = getInherited.call(this, key); if (map) { return map[subkey]; diff --git a/packages/ember-metal/lib/property_events.js b/packages/ember-metal/lib/property_events.js index 42af019fd05..77030a9a7b7 100644 --- a/packages/ember-metal/lib/property_events.js +++ b/packages/ember-metal/lib/property_events.js @@ -98,7 +98,7 @@ function propertyDidChange(obj, keyName) { return; } - if (m && m.getDeps(keyName)) { + if (m && m.readableDeps(keyName)) { dependentKeysDidChange(obj, keyName, m); } @@ -112,7 +112,7 @@ function dependentKeysWillChange(obj, depKey, meta) { if (obj.isDestroying) { return; } var deps; - if (meta && (deps = meta.getDeps(depKey))) { + if (meta && (deps = meta.readableDeps(depKey))) { var seen = WILL_SEEN; var top = !seen; @@ -133,7 +133,7 @@ function dependentKeysDidChange(obj, depKey, meta) { if (obj.isDestroying) { return; } var deps; - if (meta && (deps = meta.getDeps(depKey))) { + if (meta && (deps = meta.readableDeps(depKey))) { var seen = DID_SEEN; var top = !seen; diff --git a/packages/ember-metal/tests/alias_test.js b/packages/ember-metal/tests/alias_test.js index 8078e6ef0d3..546fac0c451 100644 --- a/packages/ember-metal/tests/alias_test.js +++ b/packages/ember-metal/tests/alias_test.js @@ -37,9 +37,9 @@ QUnit.test('basic lifecycle', function() { defineProperty(obj, 'bar', alias('foo.faz')); var m = meta(obj); addObserver(obj, 'bar', incrementCount); - equal(m.getDeps('foo.faz').bar, 1); + equal(m.readableDeps('foo.faz').bar, 1); removeObserver(obj, 'bar', incrementCount); - equal(m.getDeps('foo.faz').bar, 0); + equal(m.readableDeps('foo.faz').bar, 0); }); QUnit.test('begins watching alt key as soon as alias is watched', function() { From d74887952e9899f216f660f1ef0c9e5e77a89f33 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 21:22:07 -0400 Subject: [PATCH 25/39] better naming for ownCustomObject accessors --- packages/ember-metal/lib/chains.js | 8 ++++---- packages/ember-metal/lib/computed.js | 2 +- packages/ember-metal/lib/meta.js | 4 ++-- packages/ember-metal/lib/property_events.js | 6 +++--- packages/ember-metal/tests/watching/watch_test.js | 8 ++++---- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/ember-metal/lib/chains.js b/packages/ember-metal/lib/chains.js index d4bbfe62e53..2b5225046ec 100644 --- a/packages/ember-metal/lib/chains.js +++ b/packages/ember-metal/lib/chains.js @@ -141,7 +141,7 @@ function addChainWatcher(obj, keyName, node) { } let m = metaFor(obj); - m.getOrCreateChainWatchers(makeChainWatcher).add(keyName, node); + m.writableChainWatchers(makeChainWatcher).add(keyName, node); watchKey(obj, keyName, m); } @@ -152,14 +152,14 @@ function removeChainWatcher(obj, keyName, node) { let m = obj.__ember_meta__; - if (!m || !m.getChainWatchers()) { + if (!m || !m.readableChainWatchers()) { return; } // make meta writable m = metaFor(obj); - m.getChainWatchers().remove(keyName, node); + m.readableChainWatchers().remove(keyName, node); unwatchKey(obj, keyName, m); } @@ -424,7 +424,7 @@ export function finishChains(obj) { m = metaFor(obj); // finish any current chains node watchers that reference obj - let chainWatchers = m.getChainWatchers(); + let chainWatchers = m.readableChainWatchers(); if (chainWatchers) { chainWatchers.revalidateAll(); } diff --git a/packages/ember-metal/lib/computed.js b/packages/ember-metal/lib/computed.js index 29b7d1e6b75..00b9b1e1944 100644 --- a/packages/ember-metal/lib/computed.js +++ b/packages/ember-metal/lib/computed.js @@ -324,7 +324,7 @@ ComputedPropertyPrototype.get = function(obj, keyName) { cache[keyName] = ret; } - let chainWatchers = meta.getChainWatchers(); + let chainWatchers = meta.readableChainWatchers(); if (chainWatchers) { chainWatchers.revalidate(keyName); } diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index 21e31b2e767..a89be764295 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -184,14 +184,14 @@ function inheritedMapOfMaps(name, Meta) { function ownCustomObject(name, Meta) { let key = memberProperty(name); let capitalized = capitalize(name); - Meta.prototype['getOrCreate' + capitalized] = function(create) { + Meta.prototype['writable' + capitalized] = function(create) { let ret = this[key]; if (!ret) { ret = this[key] = create(this.source); } return ret; }; - Meta.prototype['get' + capitalized] = function() { + Meta.prototype['readable' + capitalized] = function() { return this[key]; }; } diff --git a/packages/ember-metal/lib/property_events.js b/packages/ember-metal/lib/property_events.js index 77030a9a7b7..68a7caf1e33 100644 --- a/packages/ember-metal/lib/property_events.js +++ b/packages/ember-metal/lib/property_events.js @@ -191,21 +191,21 @@ function iterDeps(method, obj, deps, depKey, seen, meta) { } function chainsWillChange(obj, keyName, m) { - let c = m.getChainWatchers(); + let c = m.readableChainWatchers(); if (c) { c.notify(keyName, false, propertyWillChange); } } function chainsDidChange(obj, keyName, m) { - let c = m.getChainWatchers(); + let c = m.readableChainWatchers(); if (c) { c.notify(keyName, true, propertyDidChange); } } function overrideChains(obj, keyName, m) { - let c = m.getChainWatchers(); + let c = m.readableChainWatchers(); if (c) { c.revalidate(keyName); } diff --git a/packages/ember-metal/tests/watching/watch_test.js b/packages/ember-metal/tests/watching/watch_test.js index 199a130232b..12caede4c19 100644 --- a/packages/ember-metal/tests/watching/watch_test.js +++ b/packages/ember-metal/tests/watching/watch_test.js @@ -206,12 +206,12 @@ QUnit.test('when watching a global object, destroy should remove chain watchers var chainNode = Ember.meta(obj).getChains()._chains.Global._chains.foo; equal(meta_Global.peekWatching('foo'), 1, 'should be watching foo'); - equal(meta_Global.getChainWatchers().has('foo', chainNode), true, 'should have chain watcher'); + equal(meta_Global.readableChainWatchers().has('foo', chainNode), true, 'should have chain watcher'); destroy(obj); equal(meta_Global.peekWatching('foo'), 0, 'should not be watching foo'); - equal(meta_Global.getChainWatchers().has('foo', chainNode), false, 'should not have chain watcher'); + equal(meta_Global.readableChainWatchers().has('foo', chainNode), false, 'should not have chain watcher'); lookup['Global'] = Global = null; // reset }); @@ -228,12 +228,12 @@ QUnit.test('when watching another object, destroy should remove chain watchers f var chainNode = Ember.meta(objA).getChains()._chains.b._chains.foo; equal(meta_objB.peekWatching('foo'), 1, 'should be watching foo'); - equal(meta_objB.getChainWatchers().has('foo', chainNode), true, 'should have chain watcher'); + equal(meta_objB.readableChainWatchers().has('foo', chainNode), true, 'should have chain watcher'); destroy(objA); equal(meta_objB.peekWatching('foo'), 0, 'should not be watching foo'); - equal(meta_objB.getChainWatchers().has('foo', chainNode), false, 'should not have chain watcher'); + equal(meta_objB.readableChainWatchers().has('foo', chainNode), false, 'should not have chain watcher'); }); // TESTS for length property From dbc712cfd19a8bfa6ede3730ba3642eb199a0bd4 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 21:25:04 -0400 Subject: [PATCH 26/39] better naming for inheritedCustomObject accessors --- packages/ember-metal/lib/chains.js | 4 ++-- packages/ember-metal/lib/meta.js | 6 +++--- packages/ember-metal/lib/watch_path.js | 2 +- packages/ember-metal/lib/watching.js | 2 +- packages/ember-metal/tests/chains_test.js | 2 +- packages/ember-metal/tests/watching/watch_test.js | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/ember-metal/lib/chains.js b/packages/ember-metal/lib/chains.js index 2b5225046ec..b65db575ac2 100644 --- a/packages/ember-metal/lib/chains.js +++ b/packages/ember-metal/lib/chains.js @@ -430,8 +430,8 @@ export function finishChains(obj) { } // ensure that if we have inherited any chains they have been // copied onto our own meta. - if (m.getChains()) { - m.getOrCreateChains(); + if (m.readableChains()) { + m.writableChains(); } } } diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index a89be764295..64937c37762 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -202,18 +202,18 @@ function ownCustomObject(name, Meta) { function inheritedCustomObject(name, Meta) { let key = memberProperty(name); let capitalized = capitalize(name); - let getOrCreate = Meta.prototype['getOrCreate' + capitalized] = function(create) { + let writable = Meta.prototype['writable' + capitalized] = function(create) { let ret = this[key]; if (!ret) { if (this.parent) { - ret = this[key] = getOrCreate.call(this.parent, create).copy(this.source); + ret = this[key] = writable.call(this.parent, create).copy(this.source); } else { ret = this[key] = create(this.source); } } return ret; }; - Meta.prototype['get' + capitalized] = function() { + Meta.prototype['readable' + capitalized] = function() { return getInherited.call(this, key); }; } diff --git a/packages/ember-metal/lib/watch_path.js b/packages/ember-metal/lib/watch_path.js index 4e9957f9325..53685bd18b9 100644 --- a/packages/ember-metal/lib/watch_path.js +++ b/packages/ember-metal/lib/watch_path.js @@ -7,7 +7,7 @@ import { ChainNode } from 'ember-metal/chains'; // chains inherited from the proto they will be cloned and reconfigured for // the current object. function chainsFor(obj, meta) { - return (meta || metaFor(obj)).getOrCreateChains(makeChainNode); + return (meta || metaFor(obj)).writableChains(makeChainNode); } function makeChainNode(obj) { diff --git a/packages/ember-metal/lib/watching.js b/packages/ember-metal/lib/watching.js index 1a470b9fa55..301df33ab00 100644 --- a/packages/ember-metal/lib/watching.js +++ b/packages/ember-metal/lib/watching.js @@ -81,7 +81,7 @@ export function destroy(obj) { if (meta) { obj['__ember_meta__'] = null; // remove chainWatchers to remove circular references that would prevent GC - node = meta.getChains(); + node = meta.readableChains(); if (node) { NODE_STACK.push(node); // process tree diff --git a/packages/ember-metal/tests/chains_test.js b/packages/ember-metal/tests/chains_test.js index 446612f0bb1..ec3518078bd 100644 --- a/packages/ember-metal/tests/chains_test.js +++ b/packages/ember-metal/tests/chains_test.js @@ -14,7 +14,7 @@ QUnit.test('finishChains should properly copy chains from prototypes to instance var childObj = Object.create(obj); finishChains(childObj); - ok(obj['__ember_meta__'].getChains() !== childObj['__ember_meta__'].getChains(), 'The chains object is copied'); + ok(obj['__ember_meta__'].readableChains() !== childObj['__ember_meta__'].readableChains(), 'The chains object is copied'); }); diff --git a/packages/ember-metal/tests/watching/watch_test.js b/packages/ember-metal/tests/watching/watch_test.js index 12caede4c19..7926003a6d8 100644 --- a/packages/ember-metal/tests/watching/watch_test.js +++ b/packages/ember-metal/tests/watching/watch_test.js @@ -203,7 +203,7 @@ QUnit.test('when watching a global object, destroy should remove chain watchers watch(obj, 'Global.foo'); var meta_Global = Ember.meta(Global); - var chainNode = Ember.meta(obj).getChains()._chains.Global._chains.foo; + var chainNode = Ember.meta(obj).readableChains()._chains.Global._chains.foo; equal(meta_Global.peekWatching('foo'), 1, 'should be watching foo'); equal(meta_Global.readableChainWatchers().has('foo', chainNode), true, 'should have chain watcher'); @@ -225,7 +225,7 @@ QUnit.test('when watching another object, destroy should remove chain watchers f watch(objA, 'b.foo'); var meta_objB = Ember.meta(objB); - var chainNode = Ember.meta(objA).getChains()._chains.b._chains.foo; + var chainNode = Ember.meta(objA).readableChains()._chains.b._chains.foo; equal(meta_objB.peekWatching('foo'), 1, 'should be watching foo'); equal(meta_objB.readableChainWatchers().has('foo', chainNode), true, 'should have chain watcher'); From 8886eb7f689b7415e1a89da7e1e8a925278555e2 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 21:31:40 -0400 Subject: [PATCH 27/39] commenting --- packages/ember-metal/lib/meta.js | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index 64937c37762..07c263017d3 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -9,16 +9,26 @@ import isEnabled from 'ember-metal/features'; @module ember-metal */ +/* + This declares the members on the Meta class. All access to Meta needs + to go through them. + + In general, the `readable` variants will give you an object (if it + already exists) that you can read but should not modify. The + `writable` variants will give you a mutable object, and they will + create it if it didn't already exist. + +*/ let members = { - cache: ownMap, - watching: inheritedMap, - mixins: inheritedMap, - bindings: inheritedMap, - values: inheritedMap, - listeners: inheritedMapOfLists, - deps: inheritedMapOfMaps, - chainWatchers: ownCustomObject, - chains: inheritedCustomObject + cache: ownMap, // writableCache, readableCache + watching: inheritedMap, // writableWatching, readableWatching, peakWatching, clearWatching + mixins: inheritedMap, // writableMixins, readableMixins, peakMixins, clearMixins + bindings: inheritedMap, // writableBindings, readableBindings, peakBindings, clearBindings + values: inheritedMap, // writableValues, readableValues, peakValues, clearValues + listeners: inheritedMapOfLists, // writableListeners, readableListeners, getAllListeners + deps: inheritedMapOfMaps, // writableDeps, readableDeps, getAllDeps + chainWatchers: ownCustomObject, // writableChainWatchers, readableChainWatchers + chains: inheritedCustomObject // writableChains, readableChains }; let memberNames = Object.keys(members); From 45d73c117273bc87813d87017b08db3e84575d4a Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Sun, 2 Aug 2015 21:32:01 -0400 Subject: [PATCH 28/39] spelling, how does it work? --- packages/ember-metal/lib/meta.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index 07c263017d3..9ddcd09d698 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -21,10 +21,10 @@ import isEnabled from 'ember-metal/features'; */ let members = { cache: ownMap, // writableCache, readableCache - watching: inheritedMap, // writableWatching, readableWatching, peakWatching, clearWatching - mixins: inheritedMap, // writableMixins, readableMixins, peakMixins, clearMixins - bindings: inheritedMap, // writableBindings, readableBindings, peakBindings, clearBindings - values: inheritedMap, // writableValues, readableValues, peakValues, clearValues + watching: inheritedMap, // writableWatching, readableWatching, peekWatching, clearWatching + mixins: inheritedMap, // writableMixins, readableMixins, peekMixins, clearMixins + bindings: inheritedMap, // writableBindings, readableBindings, peekBindings, clearBindings + values: inheritedMap, // writableValues, readableValues, peekValues, clearValues listeners: inheritedMapOfLists, // writableListeners, readableListeners, getAllListeners deps: inheritedMapOfMaps, // writableDeps, readableDeps, getAllDeps chainWatchers: ownCustomObject, // writableChainWatchers, readableChainWatchers From 26008e045cc6ed24ca6150b993b4e9e20708f0db Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Mon, 3 Aug 2015 02:33:21 -0400 Subject: [PATCH 29/39] starting some new meta tests --- .../tests/{utils => }/meta_test.js | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) rename packages/ember-metal/tests/{utils => }/meta_test.js (51%) diff --git a/packages/ember-metal/tests/utils/meta_test.js b/packages/ember-metal/tests/meta_test.js similarity index 51% rename from packages/ember-metal/tests/utils/meta_test.js rename to packages/ember-metal/tests/meta_test.js index 49982786100..9eac8165980 100644 --- a/packages/ember-metal/tests/utils/meta_test.js +++ b/packages/ember-metal/tests/meta_test.js @@ -1,6 +1,6 @@ import { meta -} from 'ember-metal/utils'; +} from 'ember-metal/meta'; QUnit.module('Ember.meta'); @@ -12,8 +12,6 @@ QUnit.test('should return the same hash for an object', function() { equal(meta(obj).foo, 'bar', 'returns same hash with multiple calls to Ember.meta()'); }); -QUnit.module('Ember.meta enumerable'); - QUnit.test('meta is not enumerable', function () { var proto, obj, props, prop; proto = { foo: 'bar' }; @@ -55,3 +53,32 @@ QUnit.test('meta is not enumerable', function () { } } }); + +QUnit.skip('meta.listeners basics', function(assert) { + let t = {}; + let m = meta({}); + m.addToListeners({ eventName: 'hello', target: t, method: 'm', flags: 0 }); + let matching = m.matchingListeners(e => e.eventName === 'hello'); + assert.equal(matching.length, 1); + assert.equal(matching[0].target, t); + m.removeFromListeners({ eventName: 'hello', target: t, method: 'm'}); + matching = m.matchingListeners(e => e.eventName === 'hello'); + assert.equal(matching.length, 0); +}); + +QUnit.skip('meta.listeners inheritance', function(assert) { + let target = {}; + let parent = {}; + let parentMeta = meta(parent); + parentMeta.addToListeners({ eventName: 'hello', target, method: 'm', flags: 0 }); + + let child = Object.create(parent); + let m = meta(child); + + let matching = m.matchingListeners(e => e.eventName === 'hello'); + assert.equal(matching.length, 1); + assert.equal(matching[0].target, target); + m.removeFromListeners({ eventName: 'hello', target, method: 'm'}); + matching = m.matchingListeners(e => e.eventName === 'hello'); + assert.equal(matching.length, 0); +}); From c4b3bceaf6d94a73d1c251bbacc5682474ec989c Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Mon, 3 Aug 2015 03:15:31 -0400 Subject: [PATCH 30/39] point everything at the new meta module --- packages/ember-metal/lib/alias.js | 6 +-- packages/ember-metal/lib/chains.js | 2 +- packages/ember-metal/lib/computed.js | 6 +-- packages/ember-metal/lib/events.js | 2 +- packages/ember-metal/lib/meta.js | 38 ++++++++++++------- packages/ember-metal/lib/mixin.js | 2 +- packages/ember-metal/lib/properties.js | 2 +- packages/ember-metal/lib/utils.js | 5 +-- packages/ember-metal/lib/watch_key.js | 2 +- packages/ember-metal/lib/watch_path.js | 2 +- .../tests/accessors/mandatory_setters_test.js | 2 +- packages/ember-metal/tests/alias_test.js | 2 +- packages/ember-metal/tests/events_test.js | 2 +- packages/ember-metal/tests/meta_test.js | 4 +- packages/ember-runtime/lib/mixins/-proxy.js | 2 +- .../ember-runtime/lib/system/core_object.js | 2 +- 16 files changed, 43 insertions(+), 38 deletions(-) diff --git a/packages/ember-metal/lib/alias.js b/packages/ember-metal/lib/alias.js index 29e4240260e..78f6dcbde93 100644 --- a/packages/ember-metal/lib/alias.js +++ b/packages/ember-metal/lib/alias.js @@ -7,10 +7,8 @@ import { defineProperty } from 'ember-metal/properties'; import { ComputedProperty } from 'ember-metal/computed'; -import { - meta, - inspect -} from 'ember-metal/utils'; +import { inspect } from 'ember-metal/utils'; +import { meta } from 'ember-metal/meta'; import { addDependentKeys, removeDependentKeys diff --git a/packages/ember-metal/lib/chains.js b/packages/ember-metal/lib/chains.js index b65db575ac2..63bc024e5a9 100644 --- a/packages/ember-metal/lib/chains.js +++ b/packages/ember-metal/lib/chains.js @@ -1,6 +1,6 @@ import Ember from 'ember-metal/core'; // warn, assert, etc; import { get, normalizeTuple } from 'ember-metal/property_get'; -import { meta as metaFor } from 'ember-metal/utils'; +import { meta as metaFor } from 'ember-metal/meta'; import { watchKey, unwatchKey } from 'ember-metal/watch_key'; var FIRST_KEY = /^([^\.]+)/; diff --git a/packages/ember-metal/lib/computed.js b/packages/ember-metal/lib/computed.js index 00b9b1e1944..46a5a35d564 100644 --- a/packages/ember-metal/lib/computed.js +++ b/packages/ember-metal/lib/computed.js @@ -1,9 +1,7 @@ import Ember from 'ember-metal/core'; import { set } from 'ember-metal/property_set'; -import { - meta, - inspect -} from 'ember-metal/utils'; +import { inspect } from 'ember-metal/utils'; +import { meta } from 'ember-metal/meta'; import expandProperties from 'ember-metal/expand_properties'; import EmberError from 'ember-metal/error'; import { diff --git a/packages/ember-metal/lib/events.js b/packages/ember-metal/lib/events.js index fa9a217cc8e..353781f0cd5 100644 --- a/packages/ember-metal/lib/events.js +++ b/packages/ember-metal/lib/events.js @@ -9,10 +9,10 @@ */ import Ember from 'ember-metal/core'; import { - meta as metaFor, apply, applyStr } from 'ember-metal/utils'; +import { meta as metaFor } from 'ember-metal/meta'; /* listener flags */ var ONCE = 1; diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index 9ddcd09d698..9e6e2b9dacc 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -18,17 +18,29 @@ import isEnabled from 'ember-metal/features'; `writable` variants will give you a mutable object, and they will create it if it didn't already exist. + The following methods will get generated metaprogrammatically, and + I'm including them here for greppability: + + writableCache, readableCache, writableWatching, readableWatching, + peekWatching, clearWatching, writableMixins, readableMixins, + peekMixins, clearMixins, writableBindings, readableBindings, + peekBindings, clearBindings, writableValues, readableValues, + peekValues, clearValues, writableListeners, readableListeners, + getAllListeners, writableDeps, readableDeps, getAllDeps + writableChainWatchers, readableChainWatchers, writableChains, + readableChains + */ let members = { - cache: ownMap, // writableCache, readableCache - watching: inheritedMap, // writableWatching, readableWatching, peekWatching, clearWatching - mixins: inheritedMap, // writableMixins, readableMixins, peekMixins, clearMixins - bindings: inheritedMap, // writableBindings, readableBindings, peekBindings, clearBindings - values: inheritedMap, // writableValues, readableValues, peekValues, clearValues - listeners: inheritedMapOfLists, // writableListeners, readableListeners, getAllListeners - deps: inheritedMapOfMaps, // writableDeps, readableDeps, getAllDeps - chainWatchers: ownCustomObject, // writableChainWatchers, readableChainWatchers - chains: inheritedCustomObject // writableChains, readableChains + cache: ownMap, + watching: inheritedMap, + mixins: inheritedMap, + bindings: inheritedMap, + values: inheritedMap, + listeners: inheritedMapOfLists, + deps: inheritedMapOfMaps, + chainWatchers: ownCustomObject, + chains: inheritedCustomObject }; let memberNames = Object.keys(members); @@ -76,7 +88,7 @@ function ownMap(name, Meta) { function getOrCreateOwnMap(key) { let ret = this[key]; if (!ret) { - ret = this[key] = Object.create(null); + ret = this[key] = {}; } return ret; } @@ -104,7 +116,7 @@ function inheritedMap(name, Meta) { }; Meta.prototype['clear' + capitalized] = function() { - this[key] = Object.create(null); + this[key] = {}; }; } @@ -114,7 +126,7 @@ function getOrCreateInheritedMap(key) { if (this.parent) { ret = this[key] = Object.create(getOrCreateInheritedMap.call(this.parent, key)); } else { - ret = this[key] = Object.create(null); + ret = this[key] = {}; } } return ret; @@ -170,7 +182,7 @@ function inheritedMapOfMaps(name, Meta) { let outerMap = getOrCreateInheritedMap.call(this, key); let innerMap = outerMap[subkey]; if (!innerMap) { - innerMap = outerMap[subkey] = Object.create(null); + innerMap = outerMap[subkey] = {}; } else if (!Object.hasOwnProperty.call(outerMap, subkey)) { innerMap = outerMap[subkey] = Object.create(innerMap); } diff --git a/packages/ember-metal/lib/mixin.js b/packages/ember-metal/lib/mixin.js index 1b986b562b3..874b8d77a49 100644 --- a/packages/ember-metal/lib/mixin.js +++ b/packages/ember-metal/lib/mixin.js @@ -14,10 +14,10 @@ import { get } from 'ember-metal/property_get'; import { set, trySet } from 'ember-metal/property_set'; import { guidFor, - meta as metaFor, wrap, makeArray } from 'ember-metal/utils'; +import { meta as metaFor } from 'ember-metal/meta'; import expandProperties from 'ember-metal/expand_properties'; import { Descriptor, diff --git a/packages/ember-metal/lib/properties.js b/packages/ember-metal/lib/properties.js index cd7e8b14ec4..3fd5f3f705e 100644 --- a/packages/ember-metal/lib/properties.js +++ b/packages/ember-metal/lib/properties.js @@ -4,7 +4,7 @@ import Ember from 'ember-metal/core'; import isEnabled from 'ember-metal/features'; -import { meta as metaFor } from 'ember-metal/utils'; +import { meta as metaFor } from 'ember-metal/meta'; import { overrideChains } from 'ember-metal/property_events'; // .......................................................... // DESCRIPTOR diff --git a/packages/ember-metal/lib/utils.js b/packages/ember-metal/lib/utils.js index c69d844e2c3..86f6a17095c 100644 --- a/packages/ember-metal/lib/utils.js +++ b/packages/ember-metal/lib/utils.js @@ -3,8 +3,6 @@ // 'REMOVE_USE_STRICT: true'; -import { meta } from 'ember-metal/meta'; - /** @module ember-metal */ @@ -500,6 +498,5 @@ export function applyStr(t, m, a) { export { GUID_KEY, makeArray, - canInvoke, - meta // this is temporary until I can refactor all the import sites + canInvoke }; diff --git a/packages/ember-metal/lib/watch_key.js b/packages/ember-metal/lib/watch_key.js index 2773a61efef..803ea869f92 100644 --- a/packages/ember-metal/lib/watch_key.js +++ b/packages/ember-metal/lib/watch_key.js @@ -1,7 +1,7 @@ import isEnabled from 'ember-metal/features'; import { meta as metaFor -} from 'ember-metal/utils'; +} from 'ember-metal/meta'; import { MANDATORY_SETTER_FUNCTION, DEFAULT_GETTER_FUNCTION diff --git a/packages/ember-metal/lib/watch_path.js b/packages/ember-metal/lib/watch_path.js index 53685bd18b9..d6f6d8a75f5 100644 --- a/packages/ember-metal/lib/watch_path.js +++ b/packages/ember-metal/lib/watch_path.js @@ -1,6 +1,6 @@ import { meta as metaFor -} from 'ember-metal/utils'; +} from 'ember-metal/meta'; import { ChainNode } from 'ember-metal/chains'; // get the chains for the current object. If the current object has diff --git a/packages/ember-metal/tests/accessors/mandatory_setters_test.js b/packages/ember-metal/tests/accessors/mandatory_setters_test.js index 3bafd745ae5..bf3a12e55ff 100644 --- a/packages/ember-metal/tests/accessors/mandatory_setters_test.js +++ b/packages/ember-metal/tests/accessors/mandatory_setters_test.js @@ -2,7 +2,7 @@ import isEnabled from 'ember-metal/features'; import { get } from 'ember-metal/property_get'; import { set } from 'ember-metal/property_set'; import { watch } from 'ember-metal/watching'; -import { meta as metaFor } from 'ember-metal/utils'; +import { meta as metaFor } from 'ember-metal/meta'; QUnit.module('mandatory-setters'); diff --git a/packages/ember-metal/tests/alias_test.js b/packages/ember-metal/tests/alias_test.js index 546fac0c451..e432314dcc5 100644 --- a/packages/ember-metal/tests/alias_test.js +++ b/packages/ember-metal/tests/alias_test.js @@ -2,7 +2,7 @@ import alias from 'ember-metal/alias'; import { defineProperty } from 'ember-metal/properties'; import { get } from 'ember-metal/property_get'; import { set } from 'ember-metal/property_set'; -import { meta } from 'ember-metal/utils'; +import { meta } from 'ember-metal/meta'; import { isWatching } from 'ember-metal/watching'; import { addObserver, removeObserver } from 'ember-metal/observer'; diff --git a/packages/ember-metal/tests/events_test.js b/packages/ember-metal/tests/events_test.js index ead7da29c05..d09713b226a 100644 --- a/packages/ember-metal/tests/events_test.js +++ b/packages/ember-metal/tests/events_test.js @@ -1,5 +1,5 @@ import { Mixin } from 'ember-metal/mixin'; -import { meta } from 'ember-metal/utils'; +import { meta } from 'ember-metal/meta'; import { on, diff --git a/packages/ember-metal/tests/meta_test.js b/packages/ember-metal/tests/meta_test.js index 9eac8165980..721523fcee8 100644 --- a/packages/ember-metal/tests/meta_test.js +++ b/packages/ember-metal/tests/meta_test.js @@ -61,7 +61,7 @@ QUnit.skip('meta.listeners basics', function(assert) { let matching = m.matchingListeners(e => e.eventName === 'hello'); assert.equal(matching.length, 1); assert.equal(matching[0].target, t); - m.removeFromListeners({ eventName: 'hello', target: t, method: 'm'}); + m.removeFromListeners({ eventName: 'hello', target: t, method: 'm' }); matching = m.matchingListeners(e => e.eventName === 'hello'); assert.equal(matching.length, 0); }); @@ -78,7 +78,7 @@ QUnit.skip('meta.listeners inheritance', function(assert) { let matching = m.matchingListeners(e => e.eventName === 'hello'); assert.equal(matching.length, 1); assert.equal(matching[0].target, target); - m.removeFromListeners({ eventName: 'hello', target, method: 'm'}); + m.removeFromListeners({ eventName: 'hello', target, method: 'm' }); matching = m.matchingListeners(e => e.eventName === 'hello'); assert.equal(matching.length, 0); }); diff --git a/packages/ember-runtime/lib/mixins/-proxy.js b/packages/ember-runtime/lib/mixins/-proxy.js index ec1e768a84d..e9c653013b2 100644 --- a/packages/ember-runtime/lib/mixins/-proxy.js +++ b/packages/ember-runtime/lib/mixins/-proxy.js @@ -6,7 +6,7 @@ import Ember from 'ember-metal/core'; // Ember.assert import { get } from 'ember-metal/property_get'; import { set } from 'ember-metal/property_set'; -import { meta } from 'ember-metal/utils'; +import { meta } from 'ember-metal/meta'; import { addObserver, removeObserver, diff --git a/packages/ember-runtime/lib/system/core_object.js b/packages/ember-runtime/lib/system/core_object.js index 09e4c0ce43f..49c1ad72b84 100644 --- a/packages/ember-runtime/lib/system/core_object.js +++ b/packages/ember-runtime/lib/system/core_object.js @@ -26,9 +26,9 @@ import { generateGuid, GUID_KEY_PROPERTY, NEXT_SUPER_PROPERTY, - meta, makeArray } from 'ember-metal/utils'; +import { meta } from 'ember-metal/meta'; import { finishChains } from 'ember-metal/chains'; import { sendEvent } from 'ember-metal/events'; import { From 71ebf9cd45e18a3cc4f49bad980c735ad78becb1 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Mon, 3 Aug 2015 05:38:25 -0400 Subject: [PATCH 31/39] fancy new listener data structures --- packages/ember-metal/lib/events.js | 116 ++-------------- packages/ember-metal/lib/meta.js | 45 ++---- packages/ember-metal/lib/meta_listeners.js | 154 +++++++++++++++++++++ packages/ember-metal/tests/events_test.js | 5 +- packages/ember-metal/tests/meta_test.js | 42 ++++-- 5 files changed, 208 insertions(+), 154 deletions(-) create mode 100644 packages/ember-metal/lib/meta_listeners.js diff --git a/packages/ember-metal/lib/events.js b/packages/ember-metal/lib/events.js index 353781f0cd5..e8f20bf56f4 100644 --- a/packages/ember-metal/lib/events.js +++ b/packages/ember-metal/lib/events.js @@ -14,9 +14,7 @@ import { } from 'ember-metal/utils'; import { meta as metaFor } from 'ember-metal/meta'; -/* listener flags */ -var ONCE = 1; -var SUSPENDED = 2; +import { ONCE, SUSPENDED } from 'ember-metal/meta_listeners'; /* @@ -51,16 +49,10 @@ function indexOf(array, target, method) { return index; } -function actionsFor(obj, eventName) { - return metaFor(obj, true).writableListeners(eventName); -} - export function accumulateListeners(obj, eventName, otherActions) { var meta = obj['__ember_meta__']; - var actions = meta && meta.readableListeners(eventName); - - if (!actions) { return; } - + if (!meta) { return; } + var actions = meta.matchingListeners(eventName); var newActions = []; for (var i = actions.length - 3; i >= 0; i -= 3) { @@ -98,19 +90,12 @@ export function addListener(obj, eventName, target, method, once) { target = null; } - var actions = actionsFor(obj, eventName); - var actionIndex = indexOf(actions, target, method); var flags = 0; - if (once) { flags |= ONCE; } - if (actionIndex !== -1) { - return; - } - - actions.push(target, method, flags); + metaFor(obj).addToListeners(eventName, target, method, flags); if ('function' === typeof obj.didAddListener) { obj.didAddListener(eventName, target, method); @@ -138,31 +123,11 @@ export function removeListener(obj, eventName, target, method) { target = null; } - function _removeListener(target, method) { - var actions = actionsFor(obj, eventName); - var actionIndex = indexOf(actions, target, method); - - // action doesn't exist, give up silently - if (actionIndex === -1) { return; } - - actions.splice(actionIndex, 3); - + metaFor(obj).removeFromListeners(eventName, target, method, (...args) => { if ('function' === typeof obj.didRemoveListener) { - obj.didRemoveListener(eventName, target, method); + obj.didRemoveListener(...args); } - } - - if (method) { - _removeListener(target, method); - } else { - var meta = obj['__ember_meta__']; - var actions = meta && meta.readableListeners(eventName); - - if (!actions) { return; } - for (var i = actions.length - 3; i >= 0; i -= 3) { - _removeListener(actions[i], actions[i + 1]); - } - } + }); } /** @@ -184,25 +149,7 @@ export function removeListener(obj, eventName, target, method) { @param {Function} callback */ export function suspendListener(obj, eventName, target, method, callback) { - if (!method && 'function' === typeof target) { - method = target; - target = null; - } - - var actions = actionsFor(obj, eventName); - var actionIndex = indexOf(actions, target, method); - - if (actionIndex !== -1) { - actions[actionIndex + 2] |= SUSPENDED; // mark the action as suspended - } - - try { - return callback.call(target); - } finally { - if (actionIndex !== -1) { - actions[actionIndex + 2] &= ~SUSPENDED; - } - } + return suspendListeners(obj, [eventName], target, method, callback); } /** @@ -223,30 +170,7 @@ export function suspendListeners(obj, eventNames, target, method, callback) { method = target; target = null; } - - var suspendedActions = []; - var actionsList = []; - - for (let i = 0, l = eventNames.length; i < l; i++) { - let eventName = eventNames[i]; - let actions = actionsFor(obj, eventName); - let actionIndex = indexOf(actions, target, method); - - if (actionIndex !== -1) { - actions[actionIndex + 2] |= SUSPENDED; - suspendedActions.push(actionIndex); - actionsList.push(actions); - } - } - - try { - return callback.call(target); - } finally { - for (let i = 0, l = suspendedActions.length; i < l; i++) { - let actionIndex = suspendedActions[i]; - actionsList[i][actionIndex + 2] &= ~SUSPENDED; - } - } + return metaFor(obj).suspendListeners(eventNames, target, method, callback); } /** @@ -258,18 +182,7 @@ export function suspendListeners(obj, eventNames, target, method, callback) { @param obj */ export function watchedEvents(obj) { - var listeners = obj['__ember_meta__'].getAllListeners(); - var ret = []; - - if (listeners) { - for (var eventName in listeners) { - if (eventName !== '__source__' && - listeners[eventName]) { - ret.push(eventName); - } - } - } - return ret; + return metaFor(obj).watchedEvents(); } /** @@ -290,7 +203,7 @@ export function watchedEvents(obj) { export function sendEvent(obj, eventName, params, actions) { if (!actions) { var meta = obj['__ember_meta__']; - actions = meta && meta.readableListeners(eventName); + actions = meta && meta.matchingListeners(eventName); } if (!actions) { return; } @@ -330,9 +243,8 @@ export function sendEvent(obj, eventName, params, actions) { */ export function hasListeners(obj, eventName) { var meta = obj['__ember_meta__']; - var actions = meta && meta.readableListeners(eventName); - - return !!(actions && actions.length); + if (!meta) { return false; } + return meta.matchingListeners(eventName).length > 0; } /** @@ -345,7 +257,7 @@ export function hasListeners(obj, eventName) { export function listenersFor(obj, eventName) { var ret = []; var meta = obj['__ember_meta__']; - var actions = meta && meta.readableListeners(eventName); + var actions = meta && meta.matchingListeners(eventName); if (!actions) { return ret; } diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index 9e6e2b9dacc..ca5154aa40a 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -4,14 +4,15 @@ 'REMOVE_USE_STRICT: true'; import isEnabled from 'ember-metal/features'; +import { protoMethods as listenerMethods } from 'ember-metal/meta_listeners'; /** @module ember-metal */ /* - This declares the members on the Meta class. All access to Meta needs - to go through them. + This declares several meta-programmed members on the Meta class. Such + meta! In general, the `readable` variants will give you an object (if it already exists) that you can read but should not modify. The @@ -25,8 +26,7 @@ import isEnabled from 'ember-metal/features'; peekWatching, clearWatching, writableMixins, readableMixins, peekMixins, clearMixins, writableBindings, readableBindings, peekBindings, clearBindings, writableValues, readableValues, - peekValues, clearValues, writableListeners, readableListeners, - getAllListeners, writableDeps, readableDeps, getAllDeps + peekValues, clearValues, writableDeps, readableDeps, getAllDeps writableChainWatchers, readableChainWatchers, writableChains, readableChains @@ -37,7 +37,6 @@ let members = { mixins: inheritedMap, bindings: inheritedMap, values: inheritedMap, - listeners: inheritedMapOfLists, deps: inheritedMapOfMaps, chainWatchers: ownCustomObject, chains: inheritedCustomObject @@ -63,6 +62,12 @@ function Meta(obj, parentMeta) { // have detailed knowledge of how each property should really be // inherited, and we can optimize it much better than JS runtimes. this.parent = parentMeta; + + this._initializeListeners(); +} + +for (let name in listenerMethods) { + Meta.prototype[name] = listenerMethods[name]; } (function setupMembers() { @@ -142,36 +147,6 @@ function getInherited(key) { } } -// Implements a member that provides a lazily created mapping from -// keys to lists, with inheritance at both levels. -function inheritedMapOfLists(name, Meta) { - let key = memberProperty(name); - let capitalized = capitalize(name); - - Meta.prototype['writable' + capitalized] = function(subkey) { - let map = getOrCreateInheritedMap.call(this, key); - let list = map[subkey]; - if (!list) { - list = map[subkey] = []; - } else if (!Object.hasOwnProperty.call(map, subkey)) { - list = map[subkey] = list.slice(); - } - return list; - }; - - Meta.prototype['readable' + capitalized] = function(subkey) { - let map = getInherited.call(this, key); - if (map) { - return map[subkey]; - } - }; - - Meta.prototype['getAll' + capitalized] = function() { - return getInherited.call(this, key); - }; -} - - // Implements a member that provides a lazily created map of maps, // with inheritance at both levels. function inheritedMapOfMaps(name, Meta) { diff --git a/packages/ember-metal/lib/meta_listeners.js b/packages/ember-metal/lib/meta_listeners.js new file mode 100644 index 00000000000..736dda52585 --- /dev/null +++ b/packages/ember-metal/lib/meta_listeners.js @@ -0,0 +1,154 @@ +/* + When we render a rich template hierarchy, the set of events that + *might* happen tends to be much larger than the set of events that + actually happen. This implies that we should make listener creation & + destruction cheap, even at the cost of making event dispatch more + expensive. + + Thus we store a new listener with a single push and no new + allocations, without even bothering to do deduplication -- we can + save that for dispatch time, if an event actually happens. + */ + +/* listener flags */ +export var ONCE = 1; +export var SUSPENDED = 2; + +export var protoMethods = { + + addToListeners(eventName, target, method, flags) { + if (!this._listeners) { + this._listeners = []; + } + this._listeners.push(eventName, target, method, flags); + }, + + _finalizeListeners() { + if (this._listenersFinalized) { return; } + if (!this._listeners) { this._listeners = []; } + let pointer = this.parent; + while (pointer) { + let listeners = pointer._listeners; + if (listeners) { + this._listeners = this._listeners.concat(listeners); + } + if (pointer._listenersFinalized) { break; } + pointer = pointer.parent; + } + this._listenersFinalized = true; + }, + + removeFromListeners(eventName, target, method, didRemove) { + let pointer = this; + while (pointer) { + let listeners = pointer._listeners; + if (listeners) { + for (let index = listeners.length - 4; index >= 0; index -= 4) { + if (listeners[index] === eventName && (!method || (listeners[index + 1] === target && listeners[index + 2] === method))) { + if (pointer === this) { + // we are modifying our own list, so we edit directly + if (typeof didRemove === 'function') { + didRemove(eventName, target, listeners[index + 2]); + } + listeners.splice(index, 4); + } else { + // we are trying to remove an inherited listener, so we do + // just-in-time copying to detach our own listeners from + // our inheritance chain. + this._finalizeListeners(); + return this.removeFromListeners(eventName, target, method); + } + } + } + } + if (pointer._listenersFinalized) { break; } + pointer = pointer.parent; + } + }, + + matchingListeners(eventName) { + let pointer = this; + let result = []; + while (pointer) { + let listeners = pointer._listeners; + if (listeners) { + for (let index = 0; index < listeners.length - 3; index += 4) { + if (listeners[index] === eventName) { + pushUniqueListener(result, listeners, index); + } + } + } + if (pointer._listenersFinalized) { break; } + pointer = pointer.parent; + } + let sus = this._suspendedListeners; + if (sus) { + for (let susIndex = 0; susIndex < sus.length - 2; susIndex += 3) { + if (eventName === sus[susIndex]) { + for (let resultIndex = 0; resultIndex < result.length - 2; resultIndex += 3) { + if (result[resultIndex] === sus[susIndex + 1] && result[resultIndex + 1] === sus[susIndex + 2]) { + result[resultIndex + 2] |= SUSPENDED; + } + } + } + } + } + return result; + }, + + suspendListeners(eventNames, target, method, callback) { + let sus = this._suspendedListeners; + if (!sus) { + sus = this._suspendedListeners = []; + } + for (let i = 0; i < eventNames.length; i++) { + sus.push(eventNames[i], target, method); + } + try { + return callback.call(target); + } finally { + if (sus.length === eventNames.length) { + this._suspendedListeners = undefined; + } else { + for (let i = sus.length - 3; i >= 0; i -= 3) { + if (sus[i + 1] === target && sus[i + 2] === method && eventNames.indexOf(sus[i]) !== -1) { + sus.splice(i, 3); + } + } + } + } + }, + + watchedEvents() { + let pointer = this; + let names = {}; + while (pointer) { + let listeners = pointer._listeners; + if (listeners) { + for (let index = 0; index < listeners.length - 3; index += 4) { + names[listeners[index]] = true; + } + } + if (pointer._listenersFinalized) { break; } + pointer = pointer.parent; + } + return Object.keys(names); + }, + + _initializeListeners() { + this._listeners = undefined; + this._listenersFinalized = undefined; + this._suspendedListeners = undefined; + } +}; + +function pushUniqueListener(destination, source, index) { + let target = source[index + 1]; + let method = source[index + 2]; + for (let destinationIndex = 0; destinationIndex < destination.length - 2; destinationIndex += 3) { + if (destination[destinationIndex] === target && destination[destinationIndex + 1] === method) { + return; + } + } + destination.push(target, method, source[index + 3]); +} diff --git a/packages/ember-metal/tests/events_test.js b/packages/ember-metal/tests/events_test.js index d09713b226a..68e507b8ffc 100644 --- a/packages/ember-metal/tests/events_test.js +++ b/packages/ember-metal/tests/events_test.js @@ -179,7 +179,6 @@ QUnit.test('calling removeListener without method should remove all listeners', addListener(obj, 'event!', F2); equal(hasListeners(obj, 'event!'), true, 'has listeners'); - removeListener(obj, 'event!'); equal(hasListeners(obj, 'event!'), false, 'has no more listeners'); @@ -205,7 +204,7 @@ QUnit.test('while suspended, it should not be possible to add a duplicate listen suspendListener(obj, 'event!', target, target.method, callback); equal(target.count, 1, 'should invoke'); - equal(meta(obj).readableListeners('event!').length, 3, 'a duplicate listener wasn\'t added'); + equal(meta(obj).matchingListeners('event!').length, 3, 'a duplicate listener wasn\'t added'); // now test suspendListeners... @@ -214,7 +213,7 @@ QUnit.test('while suspended, it should not be possible to add a duplicate listen suspendListeners(obj, ['event!'], target, target.method, callback); equal(target.count, 2, 'should have invoked again'); - equal(meta(obj).readableListeners('event!').length, 3, 'a duplicate listener wasn\'t added'); + equal(meta(obj).matchingListeners('event!').length, 3, 'a duplicate listener wasn\'t added'); }); QUnit.test('a listener can be added as part of a mixin', function() { diff --git a/packages/ember-metal/tests/meta_test.js b/packages/ember-metal/tests/meta_test.js index 721523fcee8..125b30f4ef6 100644 --- a/packages/ember-metal/tests/meta_test.js +++ b/packages/ember-metal/tests/meta_test.js @@ -54,31 +54,45 @@ QUnit.test('meta is not enumerable', function () { } }); -QUnit.skip('meta.listeners basics', function(assert) { +QUnit.test('meta.listeners basics', function(assert) { let t = {}; let m = meta({}); - m.addToListeners({ eventName: 'hello', target: t, method: 'm', flags: 0 }); - let matching = m.matchingListeners(e => e.eventName === 'hello'); - assert.equal(matching.length, 1); - assert.equal(matching[0].target, t); - m.removeFromListeners({ eventName: 'hello', target: t, method: 'm' }); - matching = m.matchingListeners(e => e.eventName === 'hello'); + m.addToListeners('hello', t, 'm', 0); + let matching = m.matchingListeners('hello'); + assert.equal(matching.length, 3); + assert.equal(matching[0], t); + m.removeFromListeners('hello', t, 'm'); + matching = m.matchingListeners('hello'); assert.equal(matching.length, 0); }); -QUnit.skip('meta.listeners inheritance', function(assert) { +QUnit.test('meta.listeners inheritance', function(assert) { let target = {}; let parent = {}; let parentMeta = meta(parent); - parentMeta.addToListeners({ eventName: 'hello', target, method: 'm', flags: 0 }); + parentMeta.addToListeners('hello', target, 'm', 0); let child = Object.create(parent); let m = meta(child); - let matching = m.matchingListeners(e => e.eventName === 'hello'); - assert.equal(matching.length, 1); - assert.equal(matching[0].target, target); - m.removeFromListeners({ eventName: 'hello', target, method: 'm' }); - matching = m.matchingListeners(e => e.eventName === 'hello'); + let matching = m.matchingListeners('hello'); + assert.equal(matching.length, 3); + assert.equal(matching[0], target); + assert.equal(matching[1], 'm'); + assert.equal(matching[2], 0); + m.removeFromListeners('hello', target, 'm'); + matching = m.matchingListeners('hello'); assert.equal(matching.length, 0); + matching = parentMeta.matchingListeners('hello'); + assert.equal(matching.length, 3); +}); + +QUnit.test('meta.listeners deduplication', function(assert) { + let t = {}; + let m = meta({}); + m.addToListeners('hello', t, 'm', 0); + m.addToListeners('hello', t, 'm', 0); + let matching = m.matchingListeners('hello'); + assert.equal(matching.length, 3); + assert.equal(matching[0], t); }); From 84a0a50b618da480588cf0c9e60e0fcd9785fdc3 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Mon, 3 Aug 2015 06:36:28 -0400 Subject: [PATCH 32/39] remove outdated comment --- packages/ember-metal/lib/meta.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index ca5154aa40a..1c1194e1c28 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -99,8 +99,7 @@ function getOrCreateOwnMap(key) { } // Implements a member that is a lazily created POJO with inheritable -// values. For member `thing` you get methods `getThing`, -// `getOrCreateThing`, and `peekThing`. +// values. function inheritedMap(name, Meta) { let key = memberProperty(name); let capitalized = capitalize(name); From 7b5fcc01b3da387eea48166e91f22da28a56fef9 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Mon, 3 Aug 2015 12:56:06 -0400 Subject: [PATCH 33/39] micro-optimize every Object.create(null) to our own EmptyObject --- .../lib/system/application.js | 5 +++-- packages/ember-htmlbars/lib/helpers.js | 3 ++- packages/ember-metal/lib/chains.js | 3 ++- packages/ember-metal/lib/empty_object.js | 19 +++++++++++++++++++ packages/ember-metal/lib/map.js | 11 ++++++----- packages/ember-metal/lib/mixin.js | 3 ++- packages/ember-metal/lib/streams/stream.js | 5 +++-- .../lib/keywords/render.js | 3 ++- packages/ember-routing/lib/system/router.js | 9 +++++---- packages/ember-views/lib/views/text_field.js | 3 ++- 10 files changed, 46 insertions(+), 18 deletions(-) create mode 100644 packages/ember-metal/lib/empty_object.js diff --git a/packages/ember-application/lib/system/application.js b/packages/ember-application/lib/system/application.js index d7b61617071..0c0bf785ec7 100644 --- a/packages/ember-application/lib/system/application.js +++ b/packages/ember-application/lib/system/application.js @@ -9,6 +9,7 @@ import Ember from 'ember-metal'; // Ember.deprecate, Ember.assert, Ember.librari import isEnabled from 'ember-metal/features'; import { get } from 'ember-metal/property_get'; import { set } from 'ember-metal/property_set'; +import EmptyObject from 'ember-metal/empty_object'; import { runLoadHooks } from 'ember-runtime/system/lazy_load'; import Namespace from 'ember-runtime/system/namespace'; import DefaultResolver from 'ember-application/system/resolver'; @@ -722,8 +723,8 @@ if (isEnabled('ember-application-visit')) { } Application.reopenClass({ - initializers: Object.create(null), - instanceInitializers: Object.create(null), + initializers: new EmptyObject(), + instanceInitializers: new EmptyObject(), /** Initializer receives an object which has the following attributes: diff --git a/packages/ember-htmlbars/lib/helpers.js b/packages/ember-htmlbars/lib/helpers.js index 8016f423ca2..b89ed29a085 100644 --- a/packages/ember-htmlbars/lib/helpers.js +++ b/packages/ember-htmlbars/lib/helpers.js @@ -8,8 +8,9 @@ @property helpers */ import Ember from 'ember-metal/core'; +import EmptyObject from 'ember-metal/empty_object'; -var helpers = Object.create(null); +var helpers = new EmptyObject(); /** @module ember diff --git a/packages/ember-metal/lib/chains.js b/packages/ember-metal/lib/chains.js index 63bc024e5a9..0111e832ead 100644 --- a/packages/ember-metal/lib/chains.js +++ b/packages/ember-metal/lib/chains.js @@ -2,6 +2,7 @@ import Ember from 'ember-metal/core'; // warn, assert, etc; import { get, normalizeTuple } from 'ember-metal/property_get'; import { meta as metaFor } from 'ember-metal/meta'; import { watchKey, unwatchKey } from 'ember-metal/watch_key'; +import EmptyObject from 'ember-metal/empty_object'; var FIRST_KEY = /^([^\.]+)/; @@ -19,7 +20,7 @@ function isVolatile(obj) { function Chains() { } -Chains.prototype = Object.create(null); +Chains.prototype = new EmptyObject(); function ChainWatchers(obj) { // this obj would be the referencing chain node's parent node's value diff --git a/packages/ember-metal/lib/empty_object.js b/packages/ember-metal/lib/empty_object.js new file mode 100644 index 00000000000..dbe6d6ddb35 --- /dev/null +++ b/packages/ember-metal/lib/empty_object.js @@ -0,0 +1,19 @@ +// This exists because `Object.create(null)` is absurdly slow compared +// to `new EmptyObject()`. In either case, you want a null prototype +// when you're treating the object instances as arbitrary dictionaries +// and don't want your keys colliding with build-in methods on the +// default object prototype. + +var proto = Object.create(null, { + // without this, we will always still end up with (new + // EmptyObject()).constructor === Object + constructor: { + value: undefined, + enumerable: false, + writable: true + } +}); + +function EmptyObject() {} +EmptyObject.prototype = proto; +export default EmptyObject; diff --git a/packages/ember-metal/lib/map.js b/packages/ember-metal/lib/map.js index df8f54c2799..740983d2a36 100644 --- a/packages/ember-metal/lib/map.js +++ b/packages/ember-metal/lib/map.js @@ -23,6 +23,7 @@ import Ember from 'ember-metal/core'; import { guidFor } from 'ember-metal/utils'; +import EmptyObject from 'ember-metal/empty_object'; function missingFunction(fn) { throw new TypeError(`${Object.prototype.toString.call(fn)} is not a function`); @@ -33,10 +34,10 @@ function missingNew(name) { } function copyNull(obj) { - var output = Object.create(null); + var output = new EmptyObject(); for (var prop in obj) { - // hasOwnPropery is not needed because obj is Object.create(null); + // hasOwnPropery is not needed because obj is new EmptyObject(); output[prop] = obj[prop]; } @@ -92,7 +93,7 @@ OrderedSet.prototype = { @private */ clear() { - this.presenceSet = Object.create(null); + this.presenceSet = new EmptyObject(); this.list = []; this.size = 0; }, @@ -246,7 +247,7 @@ function Map() { if (this instanceof this.constructor) { this._keys = OrderedSet.create(); this._keys._silenceRemoveDeprecation = true; - this._values = Object.create(null); + this._values = new EmptyObject(); this.size = 0; } else { missingNew('OrderedSet'); @@ -405,7 +406,7 @@ Map.prototype = { */ clear() { this._keys.clear(); - this._values = Object.create(null); + this._values = new EmptyObject(); this.size = 0; }, diff --git a/packages/ember-metal/lib/mixin.js b/packages/ember-metal/lib/mixin.js index 874b8d77a49..bafe40de2cd 100644 --- a/packages/ember-metal/lib/mixin.js +++ b/packages/ember-metal/lib/mixin.js @@ -10,6 +10,7 @@ import Ember from 'ember-metal/core'; // warn, assert, wrap, et; import merge from 'ember-metal/merge'; +import EmptyObject from 'ember-metal/empty_object'; import { get } from 'ember-metal/property_get'; import { set, trySet } from 'ember-metal/property_set'; import { @@ -332,7 +333,7 @@ function connectStreamBinding(obj, key, stream) { stream.subscribe(onNotify); if (obj._streamBindingSubscriptions === undefined) { - obj._streamBindingSubscriptions = Object.create(null); + obj._streamBindingSubscriptions = new EmptyObject(); } obj._streamBindingSubscriptions[key] = onNotify; diff --git a/packages/ember-metal/lib/streams/stream.js b/packages/ember-metal/lib/streams/stream.js index 372a31b0e47..57ee12656ef 100644 --- a/packages/ember-metal/lib/streams/stream.js +++ b/packages/ember-metal/lib/streams/stream.js @@ -2,6 +2,7 @@ import Ember from 'ember-metal/core'; import { getFirstKey, getTailPath } from 'ember-metal/path_cache'; import { addObserver, removeObserver } from 'ember-metal/observer'; import { isStream } from 'ember-metal/streams/utils'; +import EmptyObject from 'ember-metal/empty_object'; import Subscriber from 'ember-metal/streams/subscriber'; import Dependency from 'ember-metal/streams/dependency'; @@ -51,7 +52,7 @@ Stream.prototype = { getKey(key) { if (this.children === undefined) { - this.children = Object.create(null); + this.children = new EmptyObject(); } var keyStream = this.children[key]; @@ -69,7 +70,7 @@ Stream.prototype = { var tailPath = getTailPath(path); if (this.children === undefined) { - this.children = Object.create(null); + this.children = new EmptyObject(); } var keyStream = this.children[firstKey]; diff --git a/packages/ember-routing-htmlbars/lib/keywords/render.js b/packages/ember-routing-htmlbars/lib/keywords/render.js index c08a00bcd57..142d38ae5d6 100644 --- a/packages/ember-routing-htmlbars/lib/keywords/render.js +++ b/packages/ember-routing-htmlbars/lib/keywords/render.js @@ -1,5 +1,6 @@ import Ember from 'ember-metal/core'; // assert import { get } from 'ember-metal/property_get'; +import EmptyObject from 'ember-metal/empty_object'; import EmberError from 'ember-metal/error'; import { isStream, read } from 'ember-metal/streams/utils'; import { camelize } from 'ember-runtime/system/string'; @@ -186,7 +187,7 @@ function childOutletState(name, env) { if (!selectedOutletState) { return; } var matched = selectedOutletState.outlets[name]; if (matched) { - var childState = Object.create(null); + var childState = new EmptyObject(); childState[matched.render.outlet] = matched; matched.wasUsed = true; return childState; diff --git a/packages/ember-routing/lib/system/router.js b/packages/ember-routing/lib/system/router.js index 0d23bc58197..58f6175568c 100644 --- a/packages/ember-routing/lib/system/router.js +++ b/packages/ember-routing/lib/system/router.js @@ -4,6 +4,7 @@ import EmberError from 'ember-metal/error'; import { get } from 'ember-metal/property_get'; import { set } from 'ember-metal/property_set'; import { defineProperty } from 'ember-metal/properties'; +import EmptyObject from 'ember-metal/empty_object'; import { computed } from 'ember-metal/computed'; import merge from 'ember-metal/merge'; import run from 'ember-metal/run_loop'; @@ -102,7 +103,7 @@ var EmberRouter = EmberObject.extend(Evented, { init() { this._activeViews = {}; - this._qpCache = Object.create(null); + this._qpCache = new EmptyObject(); this._resetQueuedQueryParameterChanges(); }, @@ -464,7 +465,7 @@ var EmberRouter = EmberObject.extend(Evented, { }, _getHandlerFunction() { - var seen = Object.create(null); + var seen = new EmptyObject(); var container = this.container; var DefaultRoute = container.lookupFactory('route:basic'); @@ -1054,7 +1055,7 @@ function appendLiveRoute(liveRoutes, defaultParentState, renderOptions) { var target; var myState = { render: renderOptions, - outlets: Object.create(null) + outlets: new EmptyObject() }; if (renderOptions.into) { target = findLiveRoute(liveRoutes, renderOptions.into); @@ -1088,7 +1089,7 @@ function appendOrphan(liveRoutes, into, myState) { render: { name: '__ember_orphans__' }, - outlets: Object.create(null) + outlets: new EmptyObject() }; } liveRoutes.outlets.__ember_orphans__.outlets[into] = myState; diff --git a/packages/ember-views/lib/views/text_field.js b/packages/ember-views/lib/views/text_field.js index 1a403ac72eb..0fc539b6827 100644 --- a/packages/ember-views/lib/views/text_field.js +++ b/packages/ember-views/lib/views/text_field.js @@ -6,9 +6,10 @@ import { computed } from 'ember-metal/computed'; import environment from 'ember-metal/environment'; import Component from 'ember-views/views/component'; import TextSupport from 'ember-views/mixins/text_support'; +import EmptyObject from 'ember-metal/empty_object'; var inputTypeTestElement; -var inputTypes = Object.create(null); +var inputTypes = new EmptyObject(); function canSetTypeOfInput(type) { if (type in inputTypes) { return inputTypes[type]; From c64b3166dc1d2855266ec2b86a03ec2d786398f1 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Mon, 3 Aug 2015 17:41:16 -0400 Subject: [PATCH 34/39] fix silly unnecessary defProp --- packages/ember-metal/lib/meta.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index 1c1194e1c28..95781771a51 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -268,10 +268,8 @@ export function meta(obj, writable) { return ret || EMPTY_META; } - if (obj.__defineNonEnumerable) { - obj.__defineNonEnumerable(EMBER_META_PROPERTY); - } else { - Object.defineProperty(obj, '__ember_meta__', META_DESC); + if (ret && ret.source === obj) { + return ret; } if (!ret) { @@ -279,9 +277,16 @@ export function meta(obj, writable) { if (isEnabled('mandatory-setter')) { ret.writableValues(); } - } else if (ret.source !== obj) { + } else { ret = new Meta(obj, ret); } + + if (obj.__defineNonEnumerable) { + obj.__defineNonEnumerable(EMBER_META_PROPERTY); + } else { + Object.defineProperty(obj, '__ember_meta__', META_DESC); + } obj.__ember_meta__ = ret; + return ret; } From 09218cf889c964b36c46bfaf37f52a1032dcdced Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Mon, 3 Aug 2015 20:24:21 -0400 Subject: [PATCH 35/39] incorporating stef feedback --- packages/ember-metal/lib/meta.js | 51 +++++++++++++------------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index 95781771a51..c0b357554ed 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -43,12 +43,11 @@ let members = { }; let memberNames = Object.keys(members); +let memberProperties = memberNames.map(memberProperty); function Meta(obj, parentMeta) { // preallocate a slot for each member - for (let i = 0; i < memberNames.length; i++) { - this[memberProperty(memberNames[i])] = undefined; - } + memberProperties.forEach(prop => this[prop] = undefined); // used only internally this.source = obj; @@ -69,15 +68,7 @@ function Meta(obj, parentMeta) { for (let name in listenerMethods) { Meta.prototype[name] = listenerMethods[name]; } - -(function setupMembers() { - for (let i = 0; i < memberNames.length; i++) { - let name = memberNames[i]; - let implementation = members[name]; - implementation(name, Meta); - } -})(); - +memberNames.forEach(name => members[name](name, Meta)); // Implements a member that is a lazily created, non-inheritable // POJO. @@ -85,18 +76,18 @@ function ownMap(name, Meta) { let key = memberProperty(name); let capitalized = capitalize(name); Meta.prototype['writable' + capitalized] = function() { - return getOrCreateOwnMap.call(this, key); + return this._getOrCreateOwnMap(key); }; Meta.prototype['readable' + capitalized] = function() { return this[key]; }; } -function getOrCreateOwnMap(key) { +Meta.prototype._getOrCreateOwnMap = function(key) { let ret = this[key]; if (!ret) { ret = this[key] = {}; } return ret; -} +}; // Implements a member that is a lazily created POJO with inheritable // values. @@ -105,15 +96,15 @@ function inheritedMap(name, Meta) { let capitalized = capitalize(name); Meta.prototype['writable' + capitalized] = function() { - return getOrCreateInheritedMap.call(this, key); + return this._getOrCreateInheritedMap(key); }; Meta.prototype['readable' + capitalized] = function() { - return getInherited.call(this, key); + return this._getInherited(key); }; Meta.prototype['peek' + capitalized] = function(subkey) { - let map = getInherited.call(this, key); + let map = this._getInherited(key); if (map) { return map[subkey]; } @@ -124,27 +115,27 @@ function inheritedMap(name, Meta) { }; } -function getOrCreateInheritedMap(key) { +Meta.prototype._getOrCreateInheritedMap = function(key) { let ret = this[key]; if (!ret) { if (this.parent) { - ret = this[key] = Object.create(getOrCreateInheritedMap.call(this.parent, key)); + ret = this[key] = Object.create(this.parent._getOrCreateInheritedMap(key)); } else { ret = this[key] = {}; } } return ret; -} +}; -function getInherited(key) { +Meta.prototype._getInherited = function(key) { let pointer = this; - while (pointer) { + while (pointer !== undefined) { if (pointer[key]) { return pointer[key]; } pointer = pointer.parent; } -} +}; // Implements a member that provides a lazily created map of maps, // with inheritance at both levels. @@ -153,7 +144,7 @@ function inheritedMapOfMaps(name, Meta) { let capitalized = capitalize(name); Meta.prototype['writable' + capitalized] = function(subkey) { - let outerMap = getOrCreateInheritedMap.call(this, key); + let outerMap = this._getOrCreateInheritedMap(key); let innerMap = outerMap[subkey]; if (!innerMap) { innerMap = outerMap[subkey] = {}; @@ -164,14 +155,14 @@ function inheritedMapOfMaps(name, Meta) { }; Meta.prototype['readable' + capitalized] = function(subkey) { - let map = getInherited.call(this, key); + let map = this._getInherited(key); if (map) { return map[subkey]; } }; Meta.prototype['getAll' + capitalized] = function() { - return getInherited.call(this, key); + return this._getInherited(key); }; } @@ -198,11 +189,11 @@ function ownCustomObject(name, Meta) { function inheritedCustomObject(name, Meta) { let key = memberProperty(name); let capitalized = capitalize(name); - let writable = Meta.prototype['writable' + capitalized] = function(create) { + Meta.prototype['writable' + capitalized] = function(create) { let ret = this[key]; if (!ret) { if (this.parent) { - ret = this[key] = writable.call(this.parent, create).copy(this.source); + ret = this[key] = this.parent['writable' + capitalized](create).copy(this.source); } else { ret = this[key] = create(this.source); } @@ -210,7 +201,7 @@ function inheritedCustomObject(name, Meta) { return ret; }; Meta.prototype['readable' + capitalized] = function() { - return getInherited.call(this, key); + return this._getInherited(key); }; } From 714f0b8b7d4c95d18b561845493cc031d182eec7 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Mon, 3 Aug 2015 21:53:18 -0400 Subject: [PATCH 36/39] use empty object --- packages/ember-metal/lib/meta.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index c0b357554ed..44e00bbd5df 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -5,6 +5,7 @@ import isEnabled from 'ember-metal/features'; import { protoMethods as listenerMethods } from 'ember-metal/meta_listeners'; +import EmptyObject from 'ember-metal/empty_object'; /** @module ember-metal @@ -84,7 +85,7 @@ function ownMap(name, Meta) { Meta.prototype._getOrCreateOwnMap = function(key) { let ret = this[key]; if (!ret) { - ret = this[key] = {}; + ret = this[key] = new EmptyObject(); } return ret; }; @@ -111,7 +112,7 @@ function inheritedMap(name, Meta) { }; Meta.prototype['clear' + capitalized] = function() { - this[key] = {}; + this[key] = new EmptyObject(); }; } @@ -121,7 +122,7 @@ Meta.prototype._getOrCreateInheritedMap = function(key) { if (this.parent) { ret = this[key] = Object.create(this.parent._getOrCreateInheritedMap(key)); } else { - ret = this[key] = {}; + ret = this[key] = new EmptyObject(); } } return ret; @@ -147,7 +148,7 @@ function inheritedMapOfMaps(name, Meta) { let outerMap = this._getOrCreateInheritedMap(key); let innerMap = outerMap[subkey]; if (!innerMap) { - innerMap = outerMap[subkey] = {}; + innerMap = outerMap[subkey] = new EmptyObject(); } else if (!Object.hasOwnProperty.call(outerMap, subkey)) { innerMap = outerMap[subkey] = Object.create(innerMap); } From 580a40a75f8553bd4581f3b15ba1958c09eb3543 Mon Sep 17 00:00:00 2001 From: Stefan Penner Date: Tue, 4 Aug 2015 14:34:43 -0700 Subject: [PATCH 37/39] speed up Meta instantiation 6ms -> noise turns out there is no 0 cost abstraction in JS yet... --- packages/ember-metal/lib/meta.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index 44e00bbd5df..1c7a395ebdb 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -44,12 +44,16 @@ let members = { }; let memberNames = Object.keys(members); -let memberProperties = memberNames.map(memberProperty); function Meta(obj, parentMeta) { - // preallocate a slot for each member - memberProperties.forEach(prop => this[prop] = undefined); - + this.cache = undefined; + this.watching = undefined; + this.mixins = undefined; + this.bindings = undefined; + this.values = undefined; + this.deps = undefined; + this.chainWatchers = undefined; + this.chains = undefined; // used only internally this.source = obj; From a64e21130631d3314d303545c8fa1435f582632f Mon Sep 17 00:00:00 2001 From: Stefan Penner Date: Tue, 4 Aug 2015 16:34:03 -0700 Subject: [PATCH 38/39] =?UTF-8?q?don=E2=80=99t=20event=20try=20the=20for?= =?UTF-8?q?=20loop=20if=20there=20are=20no=20actions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/ember-metal/lib/events.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ember-metal/lib/events.js b/packages/ember-metal/lib/events.js index e8f20bf56f4..6f609b9bb39 100644 --- a/packages/ember-metal/lib/events.js +++ b/packages/ember-metal/lib/events.js @@ -206,7 +206,7 @@ export function sendEvent(obj, eventName, params, actions) { actions = meta && meta.matchingListeners(eventName); } - if (!actions) { return; } + if (!actions || actions.length === 0) { return; } for (var i = actions.length - 3; i >= 0; i -= 3) { // looping in reverse for once listeners var target = actions[i]; From b6b0739ff8fd3c7df9c7c6ca3a0565ef7b5486c2 Mon Sep 17 00:00:00 2001 From: Stefan Penner Date: Tue, 4 Aug 2015 16:46:57 -0700 Subject: [PATCH 39/39] remove get for an internal property that will never be a CP In one example this removes nearly 8% of gets --- packages/ember-views/lib/views/component.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ember-views/lib/views/component.js b/packages/ember-views/lib/views/component.js index 7cf2539b34a..5767947860f 100644 --- a/packages/ember-views/lib/views/component.js +++ b/packages/ember-views/lib/views/component.js @@ -171,7 +171,7 @@ var Component = View.extend(TargetActionSupport, ComponentTemplateDeprecation, { _template: computed('templateName', { get() { - if (get(this, '_deprecatedFlagForBlockProvided')) { + if (this._deprecatedFlagForBlockProvided) { return true; } var templateName = get(this, 'templateName');