Skip to content

Commit f29d590

Browse files
rwjbluechancancode
authored andcommitted
Store object meta in native WeakMap if present.
1 parent 14a18e7 commit f29d590

File tree

1 file changed

+71
-21
lines changed

1 file changed

+71
-21
lines changed

packages/ember-metal/lib/meta.js

Lines changed: 71 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -354,19 +354,80 @@ if (isEnabled('mandatory-setter')) {
354354
};
355355
}
356356

357+
const HAS_NATIVE_WEAKMAP = (function() {
358+
// detect if `WeakMap` is even present
359+
let hasWeakMap = typeof WeakMap === 'function';
360+
if (!hasWeakMap) { return false; }
361+
362+
let instance = new WeakMap();
363+
// use `Object`'s `.toString` directly to prevent us from detecting
364+
// polyfills as native weakmaps
365+
return Object.prototype.toString.call(instance) === '[object WeakMap]';
366+
})();
367+
368+
let setMeta, peekMeta, deleteMeta;
369+
357370
// 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);
371+
if (HAS_NATIVE_WEAKMAP) {
372+
let getPrototypeOf = Object.getPrototypeOf;
373+
let metaStore = new WeakMap();
374+
375+
setMeta = function WeakMap_setMeta(obj, meta) {
376+
metaStore.set(obj, meta);
377+
};
378+
379+
peekMeta = function WeakMap_peekMeta(obj) {
380+
let pointer = obj;
381+
let meta;
382+
while (pointer) {
383+
meta = metaStore.get(pointer);
384+
// stop if we find a `null` value, since
385+
// that means the meta was deleted
386+
// any other truthy value is a "real" meta
387+
if (meta === null || meta) {
388+
return meta;
389+
}
390+
391+
pointer = getPrototypeOf(pointer);
366392
}
367-
}
393+
};
368394

369-
obj[META_FIELD] = meta;
395+
deleteMeta = function WeakMap_deleteMeta(obj) {
396+
// set value to `null` so that we can detect
397+
// a deleted meta in peekMeta later
398+
metaStore.set(obj, null);
399+
};
400+
} else {
401+
setMeta = function Fallback_setMeta(obj, meta) {
402+
// if `null` already, just set it to the new value
403+
// otherwise define property first
404+
if (obj[META_FIELD] !== null) {
405+
if (obj.__defineNonEnumerable) {
406+
obj.__defineNonEnumerable(EMBER_META_PROPERTY);
407+
} else {
408+
Object.defineProperty(obj, META_FIELD, META_DESC);
409+
}
410+
}
411+
412+
obj[META_FIELD] = meta;
413+
};
414+
415+
peekMeta = function Fallback_peekMeta(obj) {
416+
return obj[META_FIELD];
417+
};
418+
419+
deleteMeta = function Fallback_deleteMeta(obj) {
420+
if (typeof obj[META_FIELD] !== 'object') {
421+
return;
422+
}
423+
obj[META_FIELD] = null;
424+
};
425+
}
426+
427+
export {
428+
peekMeta,
429+
setMeta,
430+
deleteMeta
370431
};
371432

372433
/**
@@ -403,14 +464,3 @@ export function meta(obj) {
403464
setMeta(obj, newMeta);
404465
return newMeta;
405466
}
406-
407-
export function peekMeta(obj) {
408-
return obj[META_FIELD];
409-
}
410-
411-
export function deleteMeta(obj) {
412-
if (typeof obj[META_FIELD] !== 'object') {
413-
return;
414-
}
415-
obj[META_FIELD] = null;
416-
}

0 commit comments

Comments
 (0)