UNPKG

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