Skip to content

Commit fb40e32

Browse files
committed
Store object meta in native WeakMap if present.
1 parent e81233a commit fb40e32

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
@@ -342,19 +342,80 @@ if (isEnabled('mandatory-setter')) {
342342
};
343343
}
344344

345+
const HAS_NATIVE_WEAKMAP = (function() {
346+
// detect if `WeakMap` is even present
347+
let hasWeakMap = typeof WeakMap === 'function';
348+
if (!hasWeakMap) { return false; }
349+
350+
let instance = new WeakMap();
351+
// use `Object`'s `.toString` directly to prevent us from detecting
352+
// polyfills as native weakmaps
353+
return Object.prototype.toString.call(instance) === '[object WeakMap]';
354+
})();
355+
356+
let setMeta, peekMeta, deleteMeta;
357+
345358
// choose the one appropriate for given platform
346-
let setMeta = function(obj, meta) {
347-
// if `null` already, just set it to the new value
348-
// otherwise define property first
349-
if (obj[META_FIELD] !== null) {
350-
if (obj.__defineNonEnumerable) {
351-
obj.__defineNonEnumerable(EMBER_META_PROPERTY);
352-
} else {
353-
Object.defineProperty(obj, META_FIELD, META_DESC);
359+
if (HAS_NATIVE_WEAKMAP) {
360+
let getPrototypeOf = Object.getPrototypeOf;
361+
let metaStore = new WeakMap();
362+
363+
setMeta = function WeakMap_setMeta(obj, meta) {
364+
metaStore.set(obj, meta);
365+
};
366+
367+
peekMeta = function WeakMap_peekMeta(obj) {
368+
let pointer = obj;
369+
let meta;
370+
while (pointer) {
371+
meta = metaStore.get(pointer);
372+
// stop if we find a `null` value, since
373+
// that means the meta was deleted
374+
// any other truthy value is a "real" meta
375+
if (meta === null || meta) {
376+
return meta;
377+
}
378+
379+
pointer = getPrototypeOf(pointer);
354380
}
355-
}
381+
};
356382

357-
obj[META_FIELD] = meta;
383+
deleteMeta = function WeakMap_deleteMeta(obj) {
384+
// set value to `null` so that we can detect
385+
// a deleted meta in peekMeta later
386+
metaStore.set(obj, null);
387+
};
388+
} else {
389+
setMeta = function Fallback_setMeta(obj, meta) {
390+
// if `null` already, just set it to the new value
391+
// otherwise define property first
392+
if (obj[META_FIELD] !== null) {
393+
if (obj.__defineNonEnumerable) {
394+
obj.__defineNonEnumerable(EMBER_META_PROPERTY);
395+
} else {
396+
Object.defineProperty(obj, META_FIELD, META_DESC);
397+
}
398+
}
399+
400+
obj[META_FIELD] = meta;
401+
};
402+
403+
peekMeta = function Fallback_peekMeta(obj) {
404+
return obj[META_FIELD];
405+
};
406+
407+
deleteMeta = function Fallback_deleteMeta(obj) {
408+
if (typeof obj[META_FIELD] !== 'object') {
409+
return;
410+
}
411+
obj[META_FIELD] = null;
412+
};
413+
}
414+
415+
export {
416+
peekMeta,
417+
setMeta,
418+
deleteMeta
358419
};
359420

360421
/**
@@ -391,14 +452,3 @@ export function meta(obj) {
391452
setMeta(obj, newMeta);
392453
return newMeta;
393454
}
394-
395-
export function peekMeta(obj) {
396-
return obj[META_FIELD];
397-
}
398-
399-
export function deleteMeta(obj) {
400-
if (typeof obj[META_FIELD] !== 'object') {
401-
return;
402-
}
403-
obj[META_FIELD] = null;
404-
}

0 commit comments

Comments
 (0)