UNPKG

7.38 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.makeWeakCache = makeWeakCache;
7exports.makeWeakCacheSync = makeWeakCacheSync;
8exports.makeStrongCache = makeStrongCache;
9exports.makeStrongCacheSync = makeStrongCacheSync;
10exports.assertSimpleType = assertSimpleType;
11
12function _gensync() {
13 const data = _interopRequireDefault(require("gensync"));
14
15 _gensync = function () {
16 return data;
17 };
18
19 return data;
20}
21
22var _async = require("../gensync-utils/async");
23
24var _util = require("./util");
25
26function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
27
28const synchronize = gen => {
29 return (0, _gensync().default)(gen).sync;
30};
31
32function* genTrue(data) {
33 return true;
34}
35
36function makeWeakCache(handler) {
37 return makeCachedFunction(WeakMap, handler);
38}
39
40function makeWeakCacheSync(handler) {
41 return synchronize(makeWeakCache(handler));
42}
43
44function makeStrongCache(handler) {
45 return makeCachedFunction(Map, handler);
46}
47
48function makeStrongCacheSync(handler) {
49 return synchronize(makeStrongCache(handler));
50}
51
52function makeCachedFunction(CallCache, handler) {
53 const callCacheSync = new CallCache();
54 const callCacheAsync = new CallCache();
55 const futureCache = new CallCache();
56 return function* cachedFunction(arg, data) {
57 const asyncContext = yield* (0, _async.isAsync)();
58 const callCache = asyncContext ? callCacheAsync : callCacheSync;
59 const cached = yield* getCachedValueOrWait(asyncContext, callCache, futureCache, arg, data);
60 if (cached.valid) return cached.value;
61 const cache = new CacheConfigurator(data);
62 const handlerResult = handler(arg, cache);
63 let finishLock;
64 let value;
65
66 if ((0, _util.isIterableIterator)(handlerResult)) {
67 const gen = handlerResult;
68 value = yield* (0, _async.onFirstPause)(gen, () => {
69 finishLock = setupAsyncLocks(cache, futureCache, arg);
70 });
71 } else {
72 value = handlerResult;
73 }
74
75 updateFunctionCache(callCache, cache, arg, value);
76
77 if (finishLock) {
78 futureCache.delete(arg);
79 finishLock.release(value);
80 }
81
82 return value;
83 };
84}
85
86function* getCachedValue(cache, arg, data) {
87 const cachedValue = cache.get(arg);
88
89 if (cachedValue) {
90 for (const {
91 value,
92 valid
93 } of cachedValue) {
94 if (yield* valid(data)) return {
95 valid: true,
96 value
97 };
98 }
99 }
100
101 return {
102 valid: false,
103 value: null
104 };
105}
106
107function* getCachedValueOrWait(asyncContext, callCache, futureCache, arg, data) {
108 const cached = yield* getCachedValue(callCache, arg, data);
109
110 if (cached.valid) {
111 return cached;
112 }
113
114 if (asyncContext) {
115 const cached = yield* getCachedValue(futureCache, arg, data);
116
117 if (cached.valid) {
118 const value = yield* (0, _async.waitFor)(cached.value.promise);
119 return {
120 valid: true,
121 value
122 };
123 }
124 }
125
126 return {
127 valid: false,
128 value: null
129 };
130}
131
132function setupAsyncLocks(config, futureCache, arg) {
133 const finishLock = new Lock();
134 updateFunctionCache(futureCache, config, arg, finishLock);
135 return finishLock;
136}
137
138function updateFunctionCache(cache, config, arg, value) {
139 if (!config.configured()) config.forever();
140 let cachedValue = cache.get(arg);
141 config.deactivate();
142
143 switch (config.mode()) {
144 case "forever":
145 cachedValue = [{
146 value,
147 valid: genTrue
148 }];
149 cache.set(arg, cachedValue);
150 break;
151
152 case "invalidate":
153 cachedValue = [{
154 value,
155 valid: config.validator()
156 }];
157 cache.set(arg, cachedValue);
158 break;
159
160 case "valid":
161 if (cachedValue) {
162 cachedValue.push({
163 value,
164 valid: config.validator()
165 });
166 } else {
167 cachedValue = [{
168 value,
169 valid: config.validator()
170 }];
171 cache.set(arg, cachedValue);
172 }
173
174 }
175}
176
177class CacheConfigurator {
178 constructor(data) {
179 this._active = true;
180 this._never = false;
181 this._forever = false;
182 this._invalidate = false;
183 this._configured = false;
184 this._pairs = [];
185 this._data = data;
186 }
187
188 simple() {
189 return makeSimpleConfigurator(this);
190 }
191
192 mode() {
193 if (this._never) return "never";
194 if (this._forever) return "forever";
195 if (this._invalidate) return "invalidate";
196 return "valid";
197 }
198
199 forever() {
200 if (!this._active) {
201 throw new Error("Cannot change caching after evaluation has completed.");
202 }
203
204 if (this._never) {
205 throw new Error("Caching has already been configured with .never()");
206 }
207
208 this._forever = true;
209 this._configured = true;
210 }
211
212 never() {
213 if (!this._active) {
214 throw new Error("Cannot change caching after evaluation has completed.");
215 }
216
217 if (this._forever) {
218 throw new Error("Caching has already been configured with .forever()");
219 }
220
221 this._never = true;
222 this._configured = true;
223 }
224
225 using(handler) {
226 if (!this._active) {
227 throw new Error("Cannot change caching after evaluation has completed.");
228 }
229
230 if (this._never || this._forever) {
231 throw new Error("Caching has already been configured with .never or .forever()");
232 }
233
234 this._configured = true;
235 const key = handler(this._data);
236 const fn = (0, _async.maybeAsync)(handler, `You appear to be using an async cache handler, but Babel has been called synchronously`);
237
238 if ((0, _async.isThenable)(key)) {
239 return key.then(key => {
240 this._pairs.push([key, fn]);
241
242 return key;
243 });
244 }
245
246 this._pairs.push([key, fn]);
247
248 return key;
249 }
250
251 invalidate(handler) {
252 this._invalidate = true;
253 return this.using(handler);
254 }
255
256 validator() {
257 const pairs = this._pairs;
258 return function* (data) {
259 for (const [key, fn] of pairs) {
260 if (key !== (yield* fn(data))) return false;
261 }
262
263 return true;
264 };
265 }
266
267 deactivate() {
268 this._active = false;
269 }
270
271 configured() {
272 return this._configured;
273 }
274
275}
276
277function makeSimpleConfigurator(cache) {
278 function cacheFn(val) {
279 if (typeof val === "boolean") {
280 if (val) cache.forever();else cache.never();
281 return;
282 }
283
284 return cache.using(() => assertSimpleType(val()));
285 }
286
287 cacheFn.forever = () => cache.forever();
288
289 cacheFn.never = () => cache.never();
290
291 cacheFn.using = cb => cache.using(() => assertSimpleType(cb()));
292
293 cacheFn.invalidate = cb => cache.invalidate(() => assertSimpleType(cb()));
294
295 return cacheFn;
296}
297
298function assertSimpleType(value) {
299 if ((0, _async.isThenable)(value)) {
300 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.`);
301 }
302
303 if (value != null && typeof value !== "string" && typeof value !== "boolean" && typeof value !== "number") {
304 throw new Error("Cache keys must be either string, boolean, number, null, or undefined.");
305 }
306
307 return value;
308}
309
310class Lock {
311 constructor() {
312 this.released = false;
313 this.promise = new Promise(resolve => {
314 this._resolve = resolve;
315 });
316 }
317
318 release(value) {
319 this.released = true;
320
321 this._resolve(value);
322 }
323
324}
\No newline at end of file