Skip to content

Commit 02babeb

Browse files
rwjblueRobert Jackson
authored andcommitted
Store object meta in native WeakMap if present.
1 parent 473a4f5 commit 02babeb

File tree

1 file changed

+100
-22
lines changed

1 file changed

+100
-22
lines changed

packages/ember-metal/lib/meta.js

Lines changed: 100 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@
55
import { EmptyObject, lookupDescriptor, symbol } from 'ember-utils';
66
import isEnabled from './features';
77
import { protoMethods as listenerMethods } from './meta_listeners';
8+
import { runInDebug } from './debug';
9+
10+
let counters = {
11+
peekCalls: 0,
12+
peekParentCalls: 0,
13+
peekPrototypeWalks: 0,
14+
setCalls: 0,
15+
deleteCalls: 0,
16+
metaCalls: 0,
17+
metaInstantiated: 0
18+
};
819

920
/**
1021
@module ember-metal
@@ -53,7 +64,9 @@ if (isEnabled('ember-glimmer-detect-backtracking-rerender') ||
5364
let memberNames = Object.keys(members);
5465
const META_FIELD = '__ember_meta__';
5566

56-
function Meta(obj, parentMeta) {
67+
export function Meta(obj, parentMeta) {
68+
runInDebug(() => counters.metaInstantiated++);
69+
5770
this._cache = undefined;
5871
this._weak = undefined;
5972
this._watching = undefined;
@@ -352,20 +365,87 @@ if (isEnabled('mandatory-setter')) {
352365
};
353366
}
354367

368+
const HAS_NATIVE_WEAKMAP = (function() {
369+
// detect if `WeakMap` is even present
370+
let hasWeakMap = typeof WeakMap === 'function';
371+
if (!hasWeakMap) { return false; }
372+
373+
let instance = new WeakMap();
374+
// use `Object`'s `.toString` directly to prevent us from detecting
375+
// polyfills as native weakmaps
376+
return Object.prototype.toString.call(instance) === '[object WeakMap]';
377+
})();
378+
379+
let setMeta, peekMeta, deleteMeta;
380+
355381
// choose the one appropriate for given platform
356-
let setMeta = function(obj, meta) {
357-
// if `null` already, just set it to the new value
358-
// otherwise define property first
359-
if (obj[META_FIELD] !== null) {
360-
if (obj.__defineNonEnumerable) {
361-
obj.__defineNonEnumerable(EMBER_META_PROPERTY);
362-
} else {
363-
Object.defineProperty(obj, META_FIELD, META_DESC);
382+
if (HAS_NATIVE_WEAKMAP) {
383+
let getPrototypeOf = Object.getPrototypeOf;
384+
let metaStore = new WeakMap();
385+
386+
setMeta = function WeakMap_setMeta(obj, meta) {
387+
runInDebug(() => counters.setCalls++);
388+
metaStore.set(obj, meta);
389+
};
390+
391+
peekMeta = function WeakMap_peekMeta(obj) {
392+
runInDebug(() => counters.peekCalls++);
393+
394+
return metaStore.get(obj);
395+
};
396+
397+
peekMeta = function WeakMap_peekParentMeta(obj) {
398+
let pointer = obj;
399+
let meta;
400+
while (pointer) {
401+
meta = metaStore.get(pointer);
402+
// jshint loopfunc:true
403+
runInDebug(() => counters.peekCalls++);
404+
// stop if we find a `null` value, since
405+
// that means the meta was deleted
406+
// any other truthy value is a "real" meta
407+
if (meta === null || meta) {
408+
return meta;
409+
}
410+
411+
pointer = getPrototypeOf(pointer);
412+
runInDebug(() => counters.peakPrototypeWalks++);
364413
}
365-
}
414+
};
366415

367-
obj[META_FIELD] = meta;
368-
};
416+
deleteMeta = function WeakMap_deleteMeta(obj) {
417+
runInDebug(() => counters.deleteCalls++);
418+
419+
// set value to `null` so that we can detect
420+
// a deleted meta in peekMeta later
421+
metaStore.set(obj, null);
422+
};
423+
} else {
424+
setMeta = function Fallback_setMeta(obj, meta) {
425+
// if `null` already, just set it to the new value
426+
// otherwise define property first
427+
if (obj[META_FIELD] !== null) {
428+
if (obj.__defineNonEnumerable) {
429+
obj.__defineNonEnumerable(EMBER_META_PROPERTY);
430+
} else {
431+
Object.defineProperty(obj, META_FIELD, META_DESC);
432+
}
433+
}
434+
435+
obj[META_FIELD] = meta;
436+
};
437+
438+
peekMeta = function Fallback_peekMeta(obj) {
439+
return obj[META_FIELD];
440+
};
441+
442+
deleteMeta = function Fallback_deleteMeta(obj) {
443+
if (typeof obj[META_FIELD] !== 'object') {
444+
return;
445+
}
446+
obj[META_FIELD] = null;
447+
};
448+
}
369449

370450
/**
371451
Retrieves the meta hash for an object. If `writable` is true ensures the
@@ -386,6 +466,8 @@ let setMeta = function(obj, meta) {
386466
@return {Object} the meta hash for an object
387467
*/
388468
export function meta(obj) {
469+
runInDebug(() => counters.metaCalls++);
470+
389471
let maybeMeta = peekMeta(obj);
390472
let parent;
391473

@@ -402,13 +484,9 @@ export function meta(obj) {
402484
return newMeta;
403485
}
404486

405-
export function peekMeta(obj) {
406-
return obj[META_FIELD];
407-
}
408-
409-
export function deleteMeta(obj) {
410-
if (typeof obj[META_FIELD] !== 'object') {
411-
return;
412-
}
413-
obj[META_FIELD] = null;
414-
}
487+
export {
488+
peekMeta,
489+
setMeta,
490+
deleteMeta,
491+
counters
492+
};

0 commit comments

Comments
 (0)