55import { EmptyObject , lookupDescriptor , symbol } from 'ember-utils' ;
66import isEnabled from './features' ;
77import { 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') ||
5364let memberNames = Object . keys ( members ) ;
5465const 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*/
388468export 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