WeakTupleMap.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. /**
  7. * Strong-key child map used for tuple elements that cannot be stored in a
  8. * `WeakMap`.
  9. * @template {EXPECTED_ANY[]} T
  10. * @template V
  11. * @typedef {Map<EXPECTED_ANY, WeakTupleMap<T, V>>} M
  12. */
  13. /**
  14. * Weak-key child map used for tuple elements that are objects and can be held
  15. * without preventing garbage collection.
  16. * @template {EXPECTED_ANY[]} T
  17. * @template V
  18. * @typedef {WeakMap<EXPECTED_OBJECT, WeakTupleMap<T, V>>} W
  19. */
  20. /**
  21. * Reports whether a tuple element can be stored in a `WeakMap`.
  22. * @param {EXPECTED_ANY} thing thing
  23. * @returns {boolean} true if is weak
  24. */
  25. const isWeakKey = (thing) => typeof thing === "object" && thing !== null;
  26. /**
  27. * Extracts the element type from a tuple-like array.
  28. * @template {unknown[]} T
  29. * @typedef {T extends ReadonlyArray<infer ElementType> ? ElementType : never} ArrayElement
  30. */
  31. /**
  32. * Stores values by tuple keys while using `WeakMap` for object elements so the
  33. * cache can release entries when those objects are collected.
  34. * @template {EXPECTED_ANY[]} K
  35. * @template V
  36. */
  37. class WeakTupleMap {
  38. /**
  39. * Initializes an empty tuple trie node with optional value and child maps.
  40. */
  41. constructor() {
  42. /**
  43. * @private
  44. * @type {number}
  45. */
  46. this.f = 0;
  47. /**
  48. * @private
  49. * @type {V | undefined}
  50. */
  51. this.v = undefined;
  52. /**
  53. * @private
  54. * @type {M<K, V> | undefined}
  55. */
  56. this.m = undefined;
  57. /**
  58. * @private
  59. * @type {W<K, V> | undefined}
  60. */
  61. this.w = undefined;
  62. }
  63. /**
  64. * Stores a value at the node identified by the provided tuple key.
  65. * @param {[...K, V]} args tuple
  66. * @returns {void}
  67. */
  68. set(...args) {
  69. /** @type {WeakTupleMap<K, V>} */
  70. let node = this;
  71. for (let i = 0; i < args.length - 1; i++) {
  72. node = node._get(/** @type {ArrayElement<K>} */ (args[i]));
  73. }
  74. node._setValue(/** @type {V} */ (args[args.length - 1]));
  75. }
  76. /**
  77. * Checks whether the exact tuple key has a stored value.
  78. * @param {K} args tuple
  79. * @returns {boolean} true, if the tuple is in the Set
  80. */
  81. has(...args) {
  82. /** @type {WeakTupleMap<K, V> | undefined} */
  83. let node = this;
  84. for (let i = 0; i < args.length; i++) {
  85. node = node._peek(/** @type {ArrayElement<K>} */ (args[i]));
  86. if (node === undefined) return false;
  87. }
  88. return node._hasValue();
  89. }
  90. /**
  91. * Returns the value stored for the exact tuple key, if any.
  92. * @param {K} args tuple
  93. * @returns {V | undefined} the value
  94. */
  95. get(...args) {
  96. /** @type {WeakTupleMap<K, V> | undefined} */
  97. let node = this;
  98. for (let i = 0; i < args.length; i++) {
  99. node = node._peek(/** @type {ArrayElement<K>} */ (args[i]));
  100. if (node === undefined) return;
  101. }
  102. return node._getValue();
  103. }
  104. /**
  105. * Returns an existing value for the tuple or computes, stores, and returns a
  106. * new one when the tuple is missing.
  107. * @param {[...K, (...args: K) => V]} args tuple
  108. * @returns {V} the value
  109. */
  110. provide(...args) {
  111. /** @type {WeakTupleMap<K, V>} */
  112. let node = this;
  113. for (let i = 0; i < args.length - 1; i++) {
  114. node = node._get(/** @type {ArrayElement<K>} */ (args[i]));
  115. }
  116. if (node._hasValue()) return /** @type {V} */ (node._getValue());
  117. const fn = /** @type {(...args: K) => V} */ (args[args.length - 1]);
  118. const newValue = fn(.../** @type {K} */ (args.slice(0, -1)));
  119. node._setValue(newValue);
  120. return newValue;
  121. }
  122. /**
  123. * Memoizes `compute(thisArg, ...args)` under the tuple key `[compute,
  124. * ...args]`, computing it on a miss. Equivalent to `provide(compute, ...args,
  125. * () => compute(thisArg, ...args))` but without allocating that closure (or an
  126. * extra rest array) on every call — `ModuleGraph#cached` runs this on hot
  127. * paths where most calls hit the cache and the closure would be pure waste.
  128. * @param {(thisArg: EXPECTED_ANY, ...args: K) => V} compute computer, also the first key element
  129. * @param {EXPECTED_ANY} thisArg first argument passed to `compute`
  130. * @param {K} args remaining key elements, also passed to `compute`
  131. * @returns {V} the value
  132. */
  133. cachedProvide(compute, thisArg, args) {
  134. /** @type {WeakTupleMap<K, V>} */
  135. let node = this._get(/** @type {ArrayElement<K>} */ (compute));
  136. for (let i = 0; i < args.length; i++) {
  137. node = node._get(/** @type {ArrayElement<K>} */ (args[i]));
  138. }
  139. if (node._hasValue()) return /** @type {V} */ (node._getValue());
  140. const newValue = compute(thisArg, ...args);
  141. node._setValue(newValue);
  142. return newValue;
  143. }
  144. /**
  145. * Removes the value stored for the tuple key without pruning the trie.
  146. * @param {K} args tuple
  147. * @returns {void}
  148. */
  149. delete(...args) {
  150. /** @type {WeakTupleMap<K, V> | undefined} */
  151. let node = this;
  152. for (let i = 0; i < args.length; i++) {
  153. node = node._peek(/** @type {ArrayElement<K>} */ (args[i]));
  154. if (node === undefined) return;
  155. }
  156. node._deleteValue();
  157. }
  158. /**
  159. * Clears the stored value and all strong and weak child maps from this node.
  160. * @returns {void}
  161. */
  162. clear() {
  163. this.f = 0;
  164. this.v = undefined;
  165. this.w = undefined;
  166. this.m = undefined;
  167. }
  168. /**
  169. * Returns the value stored directly on this trie node.
  170. * @returns {V | undefined} stored value
  171. */
  172. _getValue() {
  173. return this.v;
  174. }
  175. /**
  176. * Reports whether this trie node currently stores a value.
  177. * @returns {boolean} true when a value is present
  178. */
  179. _hasValue() {
  180. return (this.f & 1) === 1;
  181. }
  182. /**
  183. * Stores a value directly on this trie node.
  184. * @param {V} v value
  185. * @private
  186. */
  187. _setValue(v) {
  188. this.f |= 1;
  189. this.v = v;
  190. }
  191. /**
  192. * Removes the value stored directly on this trie node.
  193. */
  194. _deleteValue() {
  195. this.f &= 6;
  196. this.v = undefined;
  197. }
  198. /**
  199. * Returns the child node for a tuple element without creating one.
  200. * @param {ArrayElement<K>} thing thing
  201. * @returns {WeakTupleMap<K, V> | undefined} thing
  202. * @private
  203. */
  204. _peek(thing) {
  205. if (isWeakKey(thing)) {
  206. if ((this.f & 4) !== 4) return;
  207. return /** @type {WeakMap<ArrayElement<K>, WeakTupleMap<K, V>>} */ (
  208. this.w
  209. ).get(thing);
  210. }
  211. if ((this.f & 2) !== 2) return;
  212. return /** @type {Map<ArrayElement<K>, WeakTupleMap<K, V>>} */ (this.m).get(
  213. thing
  214. );
  215. }
  216. /**
  217. * Returns the child node for a tuple element, creating and storing it when
  218. * necessary.
  219. * @private
  220. * @param {ArrayElement<K>} thing thing
  221. * @returns {WeakTupleMap<K, V>} value
  222. */
  223. _get(thing) {
  224. if (isWeakKey(thing)) {
  225. if ((this.f & 4) !== 4) {
  226. /** @type {W<K, V>} */
  227. const newMap = new WeakMap();
  228. this.f |= 4;
  229. /** @type {WeakTupleMap<K, V>} */
  230. const newNode = new WeakTupleMap();
  231. (this.w = newMap).set(thing, newNode);
  232. return newNode;
  233. }
  234. const entry = /** @type {W<K, V>} */ (this.w).get(thing);
  235. if (entry !== undefined) {
  236. return entry;
  237. }
  238. /** @type {WeakTupleMap<K, V>} */
  239. const newNode = new WeakTupleMap();
  240. /** @type {W<K, V>} */
  241. (this.w).set(thing, newNode);
  242. return newNode;
  243. }
  244. if ((this.f & 2) !== 2) {
  245. /** @type {M<K, V>} */
  246. const newMap = new Map();
  247. this.f |= 2;
  248. /** @type {WeakTupleMap<K, V>} */
  249. const newNode = new WeakTupleMap();
  250. (this.m = newMap).set(thing, newNode);
  251. return newNode;
  252. }
  253. const entry =
  254. /** @type {M<K, V>} */
  255. (this.m).get(thing);
  256. if (entry !== undefined) {
  257. return entry;
  258. }
  259. /** @type {WeakTupleMap<K, V>} */
  260. const newNode = new WeakTupleMap();
  261. /** @type {M<K, V>} */
  262. (this.m).set(thing, newNode);
  263. return newNode;
  264. }
  265. }
  266. module.exports = WeakTupleMap;