Skip to content

Commit 02de4fe

Browse files
rwjblueRobert Jackson
authored andcommitted
Store object meta in native WeakMap if present.
1 parent 7ce7f4e commit 02de4fe

File tree

1 file changed

+119
-51
lines changed

1 file changed

+119
-51
lines changed

packages/ember-metal/lib/meta.js

Lines changed: 119 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@ import EmptyObject from './empty_object';
88
import { lookupDescriptor } from './utils';
99
import symbol from './symbol';
1010

11+
let metaCounters = window._metaCounters = {
12+
peekCalls: 0,
13+
peekParentCalls: 0,
14+
peekPrototypeWalks: 0,
15+
setCalls: 0,
16+
deleteCalls: 0,
17+
metaCalls: 0,
18+
metaInstantiated: 0
19+
};
20+
1121
/**
1222
@module ember-metal
1323
*/
@@ -55,7 +65,8 @@ if (isEnabled('ember-glimmer-detect-backtracking-rerender') ||
5565
let memberNames = Object.keys(members);
5666
const META_FIELD = '__ember_meta__';
5767

58-
function Meta(obj, parentMeta) {
68+
export function Meta(obj, parentMeta) {
69+
metaCounters.metaInstantiated++;
5970
this._cache = undefined;
6071
this._weak = undefined;
6172
this._watching = undefined;
@@ -354,63 +365,120 @@ if (isEnabled('mandatory-setter')) {
354365
};
355366
}
356367

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, meta, deleteMeta;
380+
357381
// choose the one appropriate for given platform
358-
let setMeta = function(obj, meta) {
359-
// if `null` already, just set it to the new value
360-
// otherwise define property first
361-
if (obj[META_FIELD] !== null) {
362-
if (obj.__defineNonEnumerable) {
363-
obj.__defineNonEnumerable(EMBER_META_PROPERTY);
364-
} else {
365-
Object.defineProperty(obj, META_FIELD, META_DESC);
366-
}
367-
}
382+
if (HAS_NATIVE_WEAKMAP) {
383+
let getPrototypeOf = Object.getPrototypeOf;
384+
let metaStore = new WeakMap();
368385

369-
obj[META_FIELD] = meta;
370-
};
386+
setMeta = function WeakMap_setMeta(obj, meta) {
387+
metaCounters.setCalls++;
388+
metaStore.set(obj, meta);
389+
};
371390

372-
/**
373-
Retrieves the meta hash for an object. If `writable` is true ensures the
374-
hash is writable for this object as well.
375-
376-
The meta object contains information about computed property descriptors as
377-
well as any watched properties and other information. You generally will
378-
not access this information directly but instead work with higher level
379-
methods that manipulate this hash indirectly.
380-
381-
@method meta
382-
@for Ember
383-
@private
384-
385-
@param {Object} obj The object to retrieve meta for
386-
@param {Boolean} [writable=true] Pass `false` if you do not intend to modify
387-
the meta hash, allowing the method to avoid making an unnecessary copy.
388-
@return {Object} the meta hash for an object
389-
*/
390-
export function meta(obj) {
391-
let maybeMeta = peekMeta(obj);
392-
let parent;
391+
peekMeta = function WeakMap_peekMeta(obj) {
392+
metaCounters.peekCalls++;
393+
return metaStore.get(obj);
394+
};
393395

394-
// remove this code, in-favor of explicit parent
395-
if (maybeMeta) {
396-
if (maybeMeta.source === obj) {
396+
deleteMeta = function WeakMap_deleteMeta(obj) {
397+
metaCounters.deleteCalls++;
398+
399+
// set value to `null` so that we can detect
400+
// a deleted meta in peekMeta later
401+
metaStore.set(obj, null);
402+
};
403+
404+
/**
405+
Retrieves the meta hash for an object. If `writable` is true ensures the
406+
hash is writable for this object as well.
407+
408+
The meta object contains information about computed property descriptors as
409+
well as any watched properties and other information. You generally will
410+
not access this information directly but instead work with higher level
411+
methods that manipulate this hash indirectly.
412+
413+
@method meta
414+
@for Ember
415+
@private
416+
417+
@param {Object} obj The object to retrieve meta for
418+
@param {Boolean} [writable=true] Pass `false` if you do not intend to modify
419+
the meta hash, allowing the method to avoid making an unnecessary copy.
420+
@return {Object} the meta hash for an object
421+
*/
422+
meta = function WeakMap_meta(obj) {
423+
metaCounters.metaCalls++;
424+
let maybeMeta = peekMeta(obj);
425+
426+
if (maybeMeta) {
397427
return maybeMeta;
398428
}
399-
parent = maybeMeta;
400-
}
401429

402-
let newMeta = new Meta(obj, parent);
403-
setMeta(obj, newMeta);
404-
return newMeta;
405-
}
430+
let newMeta = new Meta(obj, peekMeta(getPrototypeOf(obj)));
431+
setMeta(obj, newMeta);
432+
return newMeta;
433+
};
434+
} else {
435+
setMeta = function Fallback_setMeta(obj, meta) {
436+
// if `null` already, just set it to the new value
437+
// otherwise define property first
438+
if (obj[META_FIELD] !== null) {
439+
if (obj.__defineNonEnumerable) {
440+
obj.__defineNonEnumerable(EMBER_META_PROPERTY);
441+
} else {
442+
Object.defineProperty(obj, META_FIELD, META_DESC);
443+
}
444+
}
406445

407-
export function peekMeta(obj) {
408-
return obj[META_FIELD];
409-
}
446+
obj[META_FIELD] = meta;
447+
};
410448

411-
export function deleteMeta(obj) {
412-
if (typeof obj[META_FIELD] !== 'object') {
413-
return;
414-
}
415-
obj[META_FIELD] = null;
449+
peekMeta = function Fallback_peekMeta(obj) {
450+
return obj[META_FIELD];
451+
};
452+
453+
deleteMeta = function Fallback_deleteMeta(obj) {
454+
if (typeof obj[META_FIELD] !== 'object') {
455+
return;
456+
}
457+
obj[META_FIELD] = null;
458+
};
459+
460+
meta = function Fallback_meta(obj) {
461+
metaCounters.metaCalls++;
462+
let maybeMeta = peekMeta(obj);
463+
let parent;
464+
465+
// remove this code, in-favor of explicit parent
466+
if (maybeMeta) {
467+
if (maybeMeta.source === obj) {
468+
return maybeMeta;
469+
}
470+
parent = maybeMeta;
471+
}
472+
473+
let newMeta = new Meta(obj, parent);
474+
setMeta(obj, newMeta);
475+
return newMeta;
476+
};
416477
}
478+
479+
export {
480+
peekMeta,
481+
setMeta,
482+
deleteMeta,
483+
meta
484+
};

0 commit comments

Comments
 (0)