All files / lib/internal/util iterable_weak_map.js

100% Statements 84/84
100% Branches 18/18
100% Functions 7/7
100% Lines 84/84

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85139x 139x 139x 139x 139x 139x 139x 139x 139x 139x 139x 139x 139x 139x 139x 139x 139x 145x 139x 139x 139x 120x 120x 1x 1x 1x 120x 119x 119x 119x 119x 119x 119x 119x 119x 120x 139x 139x 1x 1x 139x 139x 1x 1x 139x 139x 2x 2x 1x 1x 1x 1x 1x 1x 2x 139x 139x 148x 148x 148x 230x 230x 82x 230x 81x 81x 148x 148x 148x 148x 148x 148x 148x 139x 139x 1x 1x 1x 139x 139x 139x 139x 139x 139x  
'use strict';
 
const {
  ObjectFreeze,
  SafeFinalizationRegistry,
  SafeSet,
  SafeWeakMap,
  SafeWeakRef,
  SymbolIterator,
} = primordials;
 
// This class is modified from the example code in the WeakRefs specification:
// https://github.com/tc39/proposal-weakrefs
// Licensed under ECMA's MIT-style license, see:
// https://github.com/tc39/ecma262/blob/HEAD/LICENSE.md
class IterableWeakMap {
  #weakMap = new SafeWeakMap();
  #refSet = new SafeSet();
  #finalizationGroup = new SafeFinalizationRegistry(cleanup);
 
  set(key, value) {
    const entry = this.#weakMap.get(key);
    if (entry) {
      // If there's already an entry for the object represented by "key",
      // the value can be updated without creating a new WeakRef:
      this.#weakMap.set(key, { value, ref: entry.ref });
    } else {
      const ref = new SafeWeakRef(key);
      this.#weakMap.set(key, { value, ref });
      this.#refSet.add(ref);
      this.#finalizationGroup.register(key, {
        set: this.#refSet,
        ref
      }, ref);
    }
  }
 
  get(key) {
    return this.#weakMap.get(key)?.value;
  }
 
  has(key) {
    return this.#weakMap.has(key);
  }
 
  delete(key) {
    const entry = this.#weakMap.get(key);
    if (!entry) {
      return false;
    }
    this.#weakMap.delete(key);
    this.#refSet.delete(entry.ref);
    this.#finalizationGroup.unregister(entry.ref);
    return true;
  }
 
  [SymbolIterator]() {
    const iterator = this.#refSet[SymbolIterator]();
 
    const next = () => {
      const result = iterator.next();
      if (result.done) return result;
      const key = result.value.deref();
      if (key == null) return next();
      const { value } = this.#weakMap.get(key);
      return { done: false, value };
    };
 
    return {
      [SymbolIterator]() { return this; },
      next,
    };
  }
}
 
function cleanup({ set, ref }) {
  set.delete(ref);
}
 
ObjectFreeze(IterableWeakMap.prototype);
 
module.exports = {
  IterableWeakMap,
};