diff --git a/packages/@ember/-internals/meta/lib/meta.ts b/packages/@ember/-internals/meta/lib/meta.ts index 59f99933311..d6ea5d286d0 100644 --- a/packages/@ember/-internals/meta/lib/meta.ts +++ b/packages/@ember/-internals/meta/lib/meta.ts @@ -92,7 +92,7 @@ type Listener = RemoveAllListener | StringListener | FunctionListener; let currentListenerVersion = 1; export class Meta { - _descriptors: any | undefined; + _descriptors: Map | undefined; _watching: any | undefined; _mixins: any | undefined; _deps: any | undefined; @@ -256,6 +256,20 @@ export class Meta { } } + _findInheritedMap(key: string, subkey: string): any | undefined { + let pointer: Meta | null = this; + while (pointer !== null) { + let map: Map = pointer[key]; + if (map !== undefined) { + let value = map.get(subkey); + if (value !== undefined) { + return value; + } + } + pointer = pointer.parent; + } + } + _hasInInheritedSet(key: string, value: any) { let pointer: Meta | null = this; while (pointer !== null) { @@ -461,12 +475,12 @@ export class Meta { : '', !this.isMetaDestroyed() ); - let map = this._getOrCreateOwnMap('_descriptors'); - map[subkey] = value; + let map = this._descriptors || (this._descriptors = new Map()); + map.set(subkey, value); } peekDescriptors(subkey: string) { - let possibleDesc = this._findInherited2('_descriptors', subkey); + let possibleDesc = this._findInheritedMap('_descriptors', subkey); return possibleDesc === UNDEFINED ? undefined : possibleDesc; } @@ -480,16 +494,15 @@ export class Meta { while (pointer !== null) { let map = pointer._descriptors; if (map !== undefined) { - for (let key in map) { + map.forEach((value, key) => { seen = seen === undefined ? new Set() : seen; if (!seen.has(key)) { seen.add(key); - let value = map[key]; if (value !== UNDEFINED) { fn(key, value); } } - } + }); } pointer = pointer.parent; } @@ -728,7 +741,7 @@ export class Meta { return this._listeners; } - matchingListeners(eventName: string): (string | boolean | object | null)[] | undefined | void { + matchingListeners(eventName: string): (string | boolean | object | null)[] | undefined { let listeners = this.flattenedListeners(); let result; @@ -845,7 +858,7 @@ export function setMeta(obj: object, meta: Meta) { metaStore.set(obj, meta); } -export function peekMeta(obj: object) { +export function peekMeta(obj: object): Meta | null { assert('Cannot call `peekMeta` on null', obj !== null); assert('Cannot call `peekMeta` on undefined', obj !== undefined); assert( @@ -884,6 +897,8 @@ export function peekMeta(obj: object) { pointer = getPrototypeOf(pointer); } + + return null; } /** @@ -909,7 +924,7 @@ export function deleteMeta(obj: object) { } let meta = peekMeta(obj); - if (meta !== undefined) { + if (meta !== null) { meta.destroy(); } } @@ -950,7 +965,7 @@ export const meta: { let maybeMeta = peekMeta(obj); // remove this code, in-favor of explicit parent - if (maybeMeta !== undefined && maybeMeta.source === obj) { + if (maybeMeta !== null && maybeMeta.source === obj) { return maybeMeta; } @@ -972,7 +987,7 @@ if (DEBUG) { @return {Descriptor} @private */ -export function descriptorFor(obj: object, keyName: string, _meta?: Meta) { +export function descriptorFor(obj: object, keyName: string, _meta?: Meta | null) { assert('Cannot call `descriptorFor` on null', obj !== null); assert('Cannot call `descriptorFor` on undefined', obj !== undefined); assert( @@ -982,7 +997,7 @@ export function descriptorFor(obj: object, keyName: string, _meta?: Meta) { let meta = _meta === undefined ? peekMeta(obj) : _meta; - if (meta !== undefined) { + if (meta !== null) { return meta.peekDescriptors(keyName); } } diff --git a/packages/@ember/-internals/metal/lib/chains.ts b/packages/@ember/-internals/metal/lib/chains.ts index ffcdf24acf8..9a2e65383f9 100644 --- a/packages/@ember/-internals/metal/lib/chains.ts +++ b/packages/@ember/-internals/metal/lib/chains.ts @@ -8,7 +8,7 @@ function isObject(obj: any): obj is object { return typeof obj === 'object' && obj !== null; } -function isVolatile(obj: any, keyName: string, meta?: Meta): boolean { +function isVolatile(obj: any, keyName: string, meta?: Meta | null): boolean { let desc = descriptorFor(obj, keyName, meta); return !(desc !== undefined && desc._volatile === false); } @@ -117,7 +117,7 @@ function removeChainWatcher(obj: object, keyName: string, node: ChainNode, _meta let meta = _meta === undefined ? peekMeta(obj) : _meta; if ( - meta === undefined || + meta === null || meta.isSourceDestroying() || meta.isMetaDestroyed() || meta.readableChainWatchers() === undefined @@ -336,7 +336,7 @@ function lazyGet(obj: object, key: string): any { let meta = peekMeta(obj); // check if object meant only to be a prototype - if (meta !== undefined && meta.proto === obj) { + if (meta !== null && meta.proto === obj) { return; } diff --git a/packages/@ember/-internals/metal/lib/computed.ts b/packages/@ember/-internals/metal/lib/computed.ts index bd1ad4ea0b4..6d4379365f5 100644 --- a/packages/@ember/-internals/metal/lib/computed.ts +++ b/packages/@ember/-internals/metal/lib/computed.ts @@ -359,7 +359,7 @@ class ComputedProperty extends Descriptor implements DescriptorWithDependentKeys // don't create objects just to invalidate let meta = peekMeta(obj); - if (meta === undefined || meta.source !== obj) { + if (meta === null || meta.source !== obj) { return; } diff --git a/packages/@ember/-internals/metal/lib/events.ts b/packages/@ember/-internals/metal/lib/events.ts index 35eef4169e2..7f7c52b5a00 100644 --- a/packages/@ember/-internals/metal/lib/events.ts +++ b/packages/@ember/-internals/metal/lib/events.ts @@ -115,11 +115,12 @@ export function sendEvent( eventName: string, params: any[], actions?: any[], - _meta?: Meta -): boolean { + _meta?: Meta | null +) { if (actions === undefined) { let meta = _meta === undefined ? peekMeta(obj) : _meta; - actions = typeof meta === 'object' && meta !== null && meta.matchingListeners(eventName); + actions = + typeof meta === 'object' && meta !== null ? meta.matchingListeners(eventName) : undefined; } if (actions === undefined || actions.length === 0) { @@ -161,7 +162,7 @@ export function sendEvent( */ export function hasListeners(obj: object, eventName: string): boolean { let meta = peekMeta(obj); - if (meta === undefined) { + if (meta === null) { return false; } let matched = meta.matchingListeners(eventName); diff --git a/packages/@ember/-internals/metal/lib/mixin.ts b/packages/@ember/-internals/metal/lib/mixin.ts index 3a63abc6a8c..4b085530dca 100644 --- a/packages/@ember/-internals/metal/lib/mixin.ts +++ b/packages/@ember/-internals/metal/lib/mixin.ts @@ -548,7 +548,7 @@ export default class Mixin { static mixins(obj: object): Mixin[] { let meta = peekMeta(obj); let ret: Mixin[] = []; - if (meta === undefined) { + if (meta === null) { return ret; } @@ -612,7 +612,7 @@ export default class Mixin { return _detect(obj, this); } let meta = peekMeta(obj); - if (meta === undefined) { + if (meta === null) { return false; } return meta.hasMixin(this); diff --git a/packages/@ember/-internals/metal/lib/properties.ts b/packages/@ember/-internals/metal/lib/properties.ts index 7eb70cf21fc..5d0096e3fcb 100644 --- a/packages/@ember/-internals/metal/lib/properties.ts +++ b/packages/@ember/-internals/metal/lib/properties.ts @@ -63,7 +63,7 @@ interface ExtendedObject { export function MANDATORY_SETTER_FUNCTION(name: string): MandatorySetterFunction { function SETTER_FUNCTION(this: object, value: any | undefined | null): void { - let m = peekMeta(this); + let m = peekMeta(this)!; if (m.isInitializing() || m.isPrototypeMeta(this)) { m.writeValues(name, value); } else { @@ -79,7 +79,7 @@ export function MANDATORY_SETTER_FUNCTION(name: string): MandatorySetterFunction export function DEFAULT_GETTER_FUNCTION(name: string): DefaultGetterFunction { return function GETTER_FUNCTION(this: any): void { let meta = peekMeta(this); - if (meta !== undefined) { + if (meta !== null) { return meta.peekValues(name); } }; @@ -89,7 +89,7 @@ export function INHERITING_GETTER_FUNCTION(name: string): InheritingGetterFuncti function IGETTER_FUNCTION(this: any): void { let meta = peekMeta(this); let val; - if (meta !== undefined) { + if (meta !== null) { val = meta.readInheritedValue('values', name); if (val === UNDEFINED) { let proto = Object.getPrototypeOf(this); diff --git a/packages/@ember/-internals/metal/lib/property_events.ts b/packages/@ember/-internals/metal/lib/property_events.ts index 147f70afcc9..7ab6f8173e7 100644 --- a/packages/@ember/-internals/metal/lib/property_events.ts +++ b/packages/@ember/-internals/metal/lib/property_events.ts @@ -34,11 +34,10 @@ let deferred = 0; @since 3.1.0 @public */ -function notifyPropertyChange(obj: object, keyName: string, _meta?: Meta): void { +function notifyPropertyChange(obj: object, keyName: string, _meta?: Meta | null): void { let meta = _meta === undefined ? peekMeta(obj) : _meta; - let hasMeta = meta !== undefined; - if (hasMeta && (meta.isInitializing() || meta.isPrototypeMeta(obj))) { + if (meta !== null && (meta.isInitializing() || meta.isPrototypeMeta(obj))) { return; } @@ -48,7 +47,7 @@ function notifyPropertyChange(obj: object, keyName: string, _meta?: Meta): void possibleDesc.didChange(obj, keyName); } - if (hasMeta && meta.peekWatching(keyName) > 0) { + if (meta !== null && meta.peekWatching(keyName) > 0) { dependentKeysDidChange(obj, keyName, meta); chainsDidChange(obj, keyName, meta); notifyObservers(obj, keyName, meta); @@ -58,7 +57,7 @@ function notifyPropertyChange(obj: object, keyName: string, _meta?: Meta): void obj[PROPERTY_DID_CHANGE](keyName); } - if (hasMeta) { + if (meta !== null) { if (meta.isSourceDestroying()) { return; } diff --git a/packages/@ember/-internals/metal/lib/property_get.ts b/packages/@ember/-internals/metal/lib/property_get.ts index 5d715df9a2a..b22dcbd69c0 100644 --- a/packages/@ember/-internals/metal/lib/property_get.ts +++ b/packages/@ember/-internals/metal/lib/property_get.ts @@ -96,6 +96,10 @@ export function get(obj: object, keyName: string): any { let isFunction = type === 'function'; let isObjectLike = isObject || isFunction; + if (isPath(keyName)) { + return isObjectLike ? _getPath(obj, keyName) : undefined; + } + let value: any; if (isObjectLike) { @@ -119,9 +123,6 @@ export function get(obj: object, keyName: string): any { } if (value === undefined) { - if (isPath(keyName)) { - return _getPath(obj, keyName); - } if ( isObject && !(keyName in obj) && @@ -133,9 +134,9 @@ export function get(obj: object, keyName: string): any { return value; } -export function _getPath(root: T, path: string): any { +export function _getPath(root: T, path: string | string[]): any { let obj: any = root; - let parts = path.split('.'); + let parts = typeof path === 'string' ? path.split('.') : path; for (let i = 0; i < parts.length; i++) { if (obj === undefined || obj === null || (obj as MaybeHasIsDestroyed).isDestroyed) { diff --git a/packages/@ember/-internals/metal/lib/property_set.ts b/packages/@ember/-internals/metal/lib/property_set.ts index ec3053461c3..76d07d44847 100644 --- a/packages/@ember/-internals/metal/lib/property_set.ts +++ b/packages/@ember/-internals/metal/lib/property_set.ts @@ -14,7 +14,7 @@ interface ExtendedObject { } let setWithMandatorySetter: >( - meta: Meta, + meta: Meta | null, obj: T, keyName: K, value: T[K] @@ -116,7 +116,7 @@ export function set(obj: object, keyName: string, value: any, tolerant?: boolean if (DEBUG) { setWithMandatorySetter = (meta, obj, keyName, value) => { - if (meta !== undefined && meta.peekWatching(keyName) > 0) { + if (meta !== null && meta.peekWatching(keyName) > 0) { makeEnumerable(obj, keyName); meta.writeValue(obj, keyName, value); } else { @@ -136,17 +136,18 @@ if (DEBUG) { function setPath(root: object, path: string, value: any, tolerant?: boolean): any { let parts = path.split('.'); - let keyName = parts.pop() as string; + let keyName = parts.pop()!; - assert('Property set failed: You passed an empty path', keyName!.trim().length > 0); + assert('Property set failed: You passed an empty path', keyName.trim().length > 0); - let newPath = parts.join('.'); - let newRoot = getPath(root, newPath); + let newRoot = getPath(root, parts); if (newRoot !== null && newRoot !== undefined) { return set(newRoot, keyName, value); } else if (!tolerant) { - throw new EmberError(`Property set failed: object in path "${newPath}" could not be found.`); + throw new EmberError( + `Property set failed: object in path "${parts.join('.')}" could not be found.` + ); } } diff --git a/packages/@ember/-internals/metal/lib/watch_key.ts b/packages/@ember/-internals/metal/lib/watch_key.ts index 2ef6999f38c..b2499a6fdbf 100644 --- a/packages/@ember/-internals/metal/lib/watch_key.ts +++ b/packages/@ember/-internals/metal/lib/watch_key.ts @@ -94,7 +94,7 @@ export function unwatchKey(obj: object, keyName: string, _meta?: Meta): void { let meta = _meta === undefined ? peekMeta(obj) : _meta; // do nothing of this object has already been destroyed - if (meta === undefined || meta.isSourceDestroyed()) { + if (meta === null || meta.isSourceDestroyed()) { return; } diff --git a/packages/@ember/-internals/metal/lib/watch_path.ts b/packages/@ember/-internals/metal/lib/watch_path.ts index 9d6d828325d..84499106f8c 100644 --- a/packages/@ember/-internals/metal/lib/watch_path.ts +++ b/packages/@ember/-internals/metal/lib/watch_path.ts @@ -14,7 +14,7 @@ export function watchPath(obj: any, keyPath: string, meta?: Meta): void { export function unwatchPath(obj: any, keyPath: string, meta?: Meta): void { let m = meta === undefined ? peekMeta(obj) : meta; - if (m === undefined) { + if (m === null) { return; } let counter = m.peekWatching(keyPath); diff --git a/packages/@ember/-internals/metal/lib/watching.ts b/packages/@ember/-internals/metal/lib/watching.ts index a095e6db8e4..3183cf439d4 100644 --- a/packages/@ember/-internals/metal/lib/watching.ts +++ b/packages/@ember/-internals/metal/lib/watching.ts @@ -33,7 +33,7 @@ export function isWatching(obj: any, key: string): boolean { export function watcherCount(obj: any, key: string): number { let meta = peekMeta(obj); - return (meta !== undefined && meta.peekWatching(key)) || 0; + return (meta !== null && meta.peekWatching(key)) || 0; } /** diff --git a/packages/ember/tests/routing/query_params_test.js b/packages/ember/tests/routing/query_params_test.js index 26210303740..cf6efa4afff 100644 --- a/packages/ember/tests/routing/query_params_test.js +++ b/packages/ember/tests/routing/query_params_test.js @@ -1334,7 +1334,7 @@ moduleFor( Route.extend({ model(p, trans) { let m = peekMeta(trans[PARAMS_SYMBOL].application); - assert.ok(m === undefined, "A meta object isn't constructed for this params POJO"); + assert.ok(m === null, "A meta object isn't constructed for this params POJO"); }, }) );