diff --git a/packages/ember-metal/lib/meta.js b/packages/ember-metal/lib/meta.js index 6561fb00dfa..30249e76ec5 100644 --- a/packages/ember-metal/lib/meta.js +++ b/packages/ember-metal/lib/meta.js @@ -131,6 +131,14 @@ export class Meta { return this.proto !== obj; } + setTag(tag) { + this._tag = tag; + } + + getTag(tag) { + return this._tag; + } + destroy() { if (this.isMetaDestroyed()) { return; } diff --git a/packages/ember-metal/lib/property_events.js b/packages/ember-metal/lib/property_events.js index 9e111856854..423ec068576 100644 --- a/packages/ember-metal/lib/property_events.js +++ b/packages/ember-metal/lib/property_events.js @@ -83,12 +83,12 @@ function propertyWillChange(obj, keyName, _meta) { */ function propertyDidChange(obj, keyName, _meta) { let meta = _meta || peekMeta(obj); + let hasMeta = !!meta; - if (meta && !meta.isInitialized(obj)) { + if (hasMeta && !meta.isInitialized(obj)) { return; } - let watching = meta && meta.peekWatching(keyName) > 0; let possibleDesc = obj[keyName]; let desc = (possibleDesc !== null && typeof possibleDesc === 'object' && possibleDesc.isDescriptor) ? possibleDesc : undefined; @@ -97,8 +97,8 @@ function propertyDidChange(obj, keyName, _meta) { desc.didChange(obj, keyName); } - if (watching) { - if (meta.hasDeps(keyName)) { + if (hasMeta && meta.peekWatching(keyName) > 0) { + if (meta.hasDeps(keyName) && !meta.isSourceDestroying()) { dependentKeysDidChange(obj, keyName, meta); } @@ -106,14 +106,14 @@ function propertyDidChange(obj, keyName, _meta) { notifyObservers(obj, keyName, meta); } - if (obj[PROPERTY_DID_CHANGE]) { obj[PROPERTY_DID_CHANGE](keyName); } - if (meta && meta.isSourceDestroying()) { return; } - - markObjectAsDirty(meta, keyName); + if (hasMeta) { + if (meta.isSourceDestroying()) { return; } + markObjectAsDirty(meta, keyName); + } if (EMBER_GLIMMER_DETECT_BACKTRACKING_RERENDER || EMBER_GLIMMER_ALLOW_BACKTRACKING_RERENDER) { assertNotRendered(obj, keyName, meta); @@ -123,9 +123,8 @@ function propertyDidChange(obj, keyName, _meta) { let WILL_SEEN, DID_SEEN; // called whenever a property is about to change to clear the cache of any dependent keys (and notify those properties of changes, etc...) function dependentKeysWillChange(obj, depKey, meta) { - if (meta && meta.isSourceDestroying()) { return; } - - if (meta && meta.hasDeps(depKey)) { + if (meta.isSourceDestroying()) { return; } + if (meta.hasDeps(depKey)) { let seen = WILL_SEEN; let top = !seen; @@ -143,21 +142,17 @@ function dependentKeysWillChange(obj, depKey, meta) { // called whenever a property has just changed to update dependent keys function dependentKeysDidChange(obj, depKey, meta) { - if (meta && meta.isSourceDestroying()) { return; } + let seen = DID_SEEN; + let top = !seen; - if (meta && meta.hasDeps(depKey)) { - let seen = DID_SEEN; - let top = !seen; - - if (top) { - seen = DID_SEEN = {}; - } + if (top) { + seen = DID_SEEN = {}; + } - iterDeps(propertyDidChange, obj, depKey, seen, meta); + iterDeps(propertyDidChange, obj, depKey, seen, meta); - if (top) { - DID_SEEN = null; - } + if (top) { + DID_SEEN = null; } } @@ -258,7 +253,7 @@ function changeProperties(callback, binding) { } function notifyBeforeObservers(obj, keyName, meta) { - if (meta && meta.isSourceDestroying()) { return; } + if (meta.isSourceDestroying()) { return; } let eventName = `${keyName}:before`; let listeners, added; @@ -272,7 +267,7 @@ function notifyBeforeObservers(obj, keyName, meta) { } function notifyObservers(obj, keyName, meta) { - if (meta && meta.isSourceDestroying()) { return; } + if (meta.isSourceDestroying()) { return; } let eventName = `${keyName}:change`; let listeners; diff --git a/packages/ember-metal/lib/tags.js b/packages/ember-metal/lib/tags.js index 9ba4b334d5e..977fdd3c8c6 100644 --- a/packages/ember-metal/lib/tags.js +++ b/packages/ember-metal/lib/tags.js @@ -14,20 +14,18 @@ function makeTag() { } export function tagForProperty(object, propertyKey, _meta) { - if (isProxy(object)) { - return tagFor(object, _meta); + if (typeof object !== 'object' || object === null) { return CONSTANT_TAG; } + + let meta = _meta || metaFor(object); + if (meta.isProxy()) { + return tagFor(object, meta); } - if (typeof object === 'object' && object) { - let meta = _meta || metaFor(object); - let tags = meta.writableTags(); - let tag = tags[propertyKey]; - if (tag) { return tag; } + let tags = meta.writableTags(); + let tag = tags[propertyKey]; + if (tag) { return tag; } - return tags[propertyKey] = makeTag(); - } else { - return CONSTANT_TAG; - } + return tags[propertyKey] = makeTag(); } export function tagFor(object, _meta) { @@ -40,19 +38,23 @@ export function tagFor(object, _meta) { } export function markObjectAsDirty(meta, propertyKey) { - let objectTag = meta && meta.readableTag(); + let objectTag = meta.readableTag(); if (objectTag) { objectTag.dirty(); } - let tags = meta && meta.readableTags(); + let tags = meta.readableTags(); let propertyTag = tags && tags[propertyKey]; if (propertyTag) { propertyTag.dirty(); } + if (propertyKey === 'content' && meta.isProxy()) { + meta.getTag().contentDidChange(); + } + if (objectTag || propertyTag) { ensureRunloop(); } diff --git a/packages/ember-runtime/lib/mixins/-proxy.js b/packages/ember-runtime/lib/mixins/-proxy.js index 53a0a833a07..a9cae5863ee 100644 --- a/packages/ember-runtime/lib/mixins/-proxy.js +++ b/packages/ember-runtime/lib/mixins/-proxy.js @@ -84,22 +84,13 @@ export default Mixin.create({ init() { this._super(...arguments); - meta(this).setProxy(); + let m = meta(this); + m.setProxy(); + m.setTag(new ProxyTag(this)); }, - _initializeTag: on('init', function() { - meta(this)._tag = new ProxyTag(this); - }), - - _contentDidChange: observer('content', function() { - assert('Can\'t set Proxy\'s content to itself', get(this, 'content') !== this); - tagFor(this).contentDidChange(); - }), - isTruthy: bool('content'), - _debugContainerKey: null, - willWatchProperty(key) { let contentKey = `content.${key}`; _addBeforeObserver(this, contentKey, null, contentPropertyWillChange); @@ -126,6 +117,7 @@ export default Mixin.create({ setUnknownProperty(key, value) { let m = meta(this); + if (m.proto === this) { // if marked as prototype then just defineProperty // rather than delegate @@ -141,6 +133,7 @@ export default Mixin.create({ !this.isController, { id: 'ember-runtime.controller-proxy', until: '3.0.0' } ); + return set(content, key, value); } });