caching.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.makeWeakCache = makeWeakCache;
  6. exports.makeWeakCacheSync = makeWeakCacheSync;
  7. exports.makeStrongCache = makeStrongCache;
  8. exports.makeStrongCacheSync = makeStrongCacheSync;
  9. exports.assertSimpleType = assertSimpleType;
  10. function _gensync() {
  11. const data = _interopRequireDefault(require("gensync"));
  12. _gensync = function () {
  13. return data;
  14. };
  15. return data;
  16. }
  17. var _async = require("../gensync-utils/async");
  18. var _util = require("./util");
  19. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  20. const synchronize = gen => {
  21. return (0, _gensync().default)(gen).sync;
  22. };
  23. function* genTrue(data) {
  24. return true;
  25. }
  26. function makeWeakCache(handler) {
  27. return makeCachedFunction(WeakMap, handler);
  28. }
  29. function makeWeakCacheSync(handler) {
  30. return synchronize(makeWeakCache(handler));
  31. }
  32. function makeStrongCache(handler) {
  33. return makeCachedFunction(Map, handler);
  34. }
  35. function makeStrongCacheSync(handler) {
  36. return synchronize(makeStrongCache(handler));
  37. }
  38. function makeCachedFunction(CallCache, handler) {
  39. const callCacheSync = new CallCache();
  40. const callCacheAsync = new CallCache();
  41. const futureCache = new CallCache();
  42. return function* cachedFunction(arg, data) {
  43. const asyncContext = yield* (0, _async.isAsync)();
  44. const callCache = asyncContext ? callCacheAsync : callCacheSync;
  45. const cached = yield* getCachedValueOrWait(asyncContext, callCache, futureCache, arg, data);
  46. if (cached.valid) return cached.value;
  47. const cache = new CacheConfigurator(data);
  48. const handlerResult = handler(arg, cache);
  49. let finishLock;
  50. let value;
  51. if ((0, _util.isIterableIterator)(handlerResult)) {
  52. const gen = handlerResult;
  53. value = yield* (0, _async.onFirstPause)(gen, () => {
  54. finishLock = setupAsyncLocks(cache, futureCache, arg);
  55. });
  56. } else {
  57. value = handlerResult;
  58. }
  59. updateFunctionCache(callCache, cache, arg, value);
  60. if (finishLock) {
  61. futureCache.delete(arg);
  62. finishLock.release(value);
  63. }
  64. return value;
  65. };
  66. }
  67. function* getCachedValue(cache, arg, data) {
  68. const cachedValue = cache.get(arg);
  69. if (cachedValue) {
  70. for (const {
  71. value,
  72. valid
  73. } of cachedValue) {
  74. if (yield* valid(data)) return {
  75. valid: true,
  76. value
  77. };
  78. }
  79. }
  80. return {
  81. valid: false,
  82. value: null
  83. };
  84. }
  85. function* getCachedValueOrWait(asyncContext, callCache, futureCache, arg, data) {
  86. const cached = yield* getCachedValue(callCache, arg, data);
  87. if (cached.valid) {
  88. return cached;
  89. }
  90. if (asyncContext) {
  91. const cached = yield* getCachedValue(futureCache, arg, data);
  92. if (cached.valid) {
  93. const value = yield* (0, _async.waitFor)(cached.value.promise);
  94. return {
  95. valid: true,
  96. value
  97. };
  98. }
  99. }
  100. return {
  101. valid: false,
  102. value: null
  103. };
  104. }
  105. function setupAsyncLocks(config, futureCache, arg) {
  106. const finishLock = new Lock();
  107. updateFunctionCache(futureCache, config, arg, finishLock);
  108. return finishLock;
  109. }
  110. function updateFunctionCache(cache, config, arg, value) {
  111. if (!config.configured()) config.forever();
  112. let cachedValue = cache.get(arg);
  113. config.deactivate();
  114. switch (config.mode()) {
  115. case "forever":
  116. cachedValue = [{
  117. value,
  118. valid: genTrue
  119. }];
  120. cache.set(arg, cachedValue);
  121. break;
  122. case "invalidate":
  123. cachedValue = [{
  124. value,
  125. valid: config.validator()
  126. }];
  127. cache.set(arg, cachedValue);
  128. break;
  129. case "valid":
  130. if (cachedValue) {
  131. cachedValue.push({
  132. value,
  133. valid: config.validator()
  134. });
  135. } else {
  136. cachedValue = [{
  137. value,
  138. valid: config.validator()
  139. }];
  140. cache.set(arg, cachedValue);
  141. }
  142. }
  143. }
  144. class CacheConfigurator {
  145. constructor(data) {
  146. this._active = true;
  147. this._never = false;
  148. this._forever = false;
  149. this._invalidate = false;
  150. this._configured = false;
  151. this._pairs = [];
  152. this._data = void 0;
  153. this._data = data;
  154. }
  155. simple() {
  156. return makeSimpleConfigurator(this);
  157. }
  158. mode() {
  159. if (this._never) return "never";
  160. if (this._forever) return "forever";
  161. if (this._invalidate) return "invalidate";
  162. return "valid";
  163. }
  164. forever() {
  165. if (!this._active) {
  166. throw new Error("Cannot change caching after evaluation has completed.");
  167. }
  168. if (this._never) {
  169. throw new Error("Caching has already been configured with .never()");
  170. }
  171. this._forever = true;
  172. this._configured = true;
  173. }
  174. never() {
  175. if (!this._active) {
  176. throw new Error("Cannot change caching after evaluation has completed.");
  177. }
  178. if (this._forever) {
  179. throw new Error("Caching has already been configured with .forever()");
  180. }
  181. this._never = true;
  182. this._configured = true;
  183. }
  184. using(handler) {
  185. if (!this._active) {
  186. throw new Error("Cannot change caching after evaluation has completed.");
  187. }
  188. if (this._never || this._forever) {
  189. throw new Error("Caching has already been configured with .never or .forever()");
  190. }
  191. this._configured = true;
  192. const key = handler(this._data);
  193. const fn = (0, _async.maybeAsync)(handler, `You appear to be using an async cache handler, but Babel has been called synchronously`);
  194. if ((0, _async.isThenable)(key)) {
  195. return key.then(key => {
  196. this._pairs.push([key, fn]);
  197. return key;
  198. });
  199. }
  200. this._pairs.push([key, fn]);
  201. return key;
  202. }
  203. invalidate(handler) {
  204. this._invalidate = true;
  205. return this.using(handler);
  206. }
  207. validator() {
  208. const pairs = this._pairs;
  209. return function* (data) {
  210. for (const [key, fn] of pairs) {
  211. if (key !== (yield* fn(data))) return false;
  212. }
  213. return true;
  214. };
  215. }
  216. deactivate() {
  217. this._active = false;
  218. }
  219. configured() {
  220. return this._configured;
  221. }
  222. }
  223. function makeSimpleConfigurator(cache) {
  224. function cacheFn(val) {
  225. if (typeof val === "boolean") {
  226. if (val) cache.forever();else cache.never();
  227. return;
  228. }
  229. return cache.using(() => assertSimpleType(val()));
  230. }
  231. cacheFn.forever = () => cache.forever();
  232. cacheFn.never = () => cache.never();
  233. cacheFn.using = cb => cache.using(() => assertSimpleType(cb()));
  234. cacheFn.invalidate = cb => cache.invalidate(() => assertSimpleType(cb()));
  235. return cacheFn;
  236. }
  237. function assertSimpleType(value) {
  238. if ((0, _async.isThenable)(value)) {
  239. throw new Error(`You appear to be using an async cache handler, ` + `which your current version of Babel does not support. ` + `We may add support for this in the future, ` + `but if you're on the most recent version of @babel/core and still ` + `seeing this error, then you'll need to synchronously handle your caching logic.`);
  240. }
  241. if (value != null && typeof value !== "string" && typeof value !== "boolean" && typeof value !== "number") {
  242. throw new Error("Cache keys must be either string, boolean, number, null, or undefined.");
  243. }
  244. return value;
  245. }
  246. class Lock {
  247. constructor() {
  248. this.released = false;
  249. this.promise = void 0;
  250. this._resolve = void 0;
  251. this.promise = new Promise(resolve => {
  252. this._resolve = resolve;
  253. });
  254. }
  255. release(value) {
  256. this.released = true;
  257. this._resolve(value);
  258. }
  259. }