diff --git a/README.md b/README.md index 5a4b459..f5dc583 100644 --- a/README.md +++ b/README.md @@ -130,9 +130,17 @@ there is cached result. Builtin IO functions are mocked to cache IO. Current list of builtin cached functions is: -- `Date` constructor +- `Date` - `Math.random()` - `fetch` +- `Response` methods: + - `arrayBuffer` + - `blob` + - `formData` + - `json` + - `text` +- `setTimeout` +- `clearTimeout` If you want to make your own own function IO-caching, or import third party function and make it IO-caching, then you should use `IO` pragma. diff --git a/src/cmd.js b/src/cmd.js index 8be9c2a..73f9aad 100644 --- a/src/cmd.js +++ b/src/cmd.js @@ -768,6 +768,11 @@ const on_deferred_call = (state, call, calltree_changed_token, logs) => { } } +// TODO test +const clear_io_cache = state => { + return run_code({...state, io_cache: null}) +} + const do_load_dir = (state, dir) => { const collect_files = dir => dir.kind == 'file' ? [dir] @@ -848,5 +853,6 @@ export const COMMANDS = { external_imports_loaded, eval_modules_finished, on_deferred_call, + clear_io_cache, calltree: calltree_commands, } diff --git a/src/record_io.js b/src/record_io.js index fbadd9f..2784867 100644 --- a/src/record_io.js +++ b/src/record_io.js @@ -20,7 +20,11 @@ const io_patch = (cxt, path, use_context = false) => { const original = obj[method] obj[method] = function(...args) { - // TODO guard calls from prev run + // TODO if called from previous version of code (calltree_changed_token is + // different), then do not call IO function and throw error to finish + // previous run ASAP + + // TODO remove console.error('patched method', name, { io_cache_is_recording: cxt.io_cache_is_recording, io_cache_is_replay_aborted: cxt.io_cache_is_replay_aborted, @@ -28,14 +32,18 @@ const io_patch = (cxt, path, use_context = false) => { ? cxt.io_cache.length : cxt.io_cache_index }) - // TODO guard that in find_call io methods are not called? - // if(searched_location != null) { - // throw new Error('illegal state') - // } + + // sanity check + if(cxt.searched_location != null) { + throw new Error('illegal state') + } + if(cxt.io_cache_is_replay_aborted) { // Try to finish fast throw new Error('io replay aborted') - } else if(cxt.io_cache_is_recording) { + } + + if(cxt.io_cache_is_recording) { let ok, value, error const has_new_target = new.target != null try { @@ -141,6 +149,11 @@ const io_patch = (cxt, path, use_context = false) => { cxt.io_cache_resolver_is_set = true original_setTimeout(() => { + if(cxt.io_cache_is_replay_aborted) { + console.error('RESOLVER ABORTED') + return + } + // TODO guard from previous run console.error('resolver', { io_cache_is_replay_aborted: cxt.io_cache_is_replay_aborted, @@ -151,38 +164,32 @@ const io_patch = (cxt, path, use_context = false) => { // TODO check if call from prev run - if(cxt.io_cache_is_replay_aborted) { - console.error('RESOLVER ABORTED') - return - } - + // Sanity check if(cxt.io_cache_index >= cxt.io_cache.length) { - // TODO Do nothing or what? - // Should not gonna happen throw new Error('illegal state') + } + + const next_event = cxt.io_cache[cxt.io_cache_index] + if(next_event.type == 'call') { + // TODO Call not happened, replay? + cxt.io_cache_is_replay_aborted = true } else { - const next_event = cxt.io_cache[cxt.io_cache_index] - if(next_event.type == 'call') { - // TODO Call not happened, replay? - cxt.io_cache_is_replay_aborted = true - } else { - while( - cxt.io_cache_index < cxt.io_cache.length - && - cxt.io_cache[cxt.io_cache_index].type == 'resolution' - ) { - const resolution = cxt.io_cache[cxt.io_cache_index] - const resolver = cxt.io_cache_resolvers.get(resolution.index) + while( + cxt.io_cache_index < cxt.io_cache.length + && + cxt.io_cache[cxt.io_cache_index].type == 'resolution' + ) { + const resolution = cxt.io_cache[cxt.io_cache_index] + const resolver = cxt.io_cache_resolvers.get(resolution.index) - cxt.io_cache_index++ + cxt.io_cache_index++ - if(cxt.io_cache[resolution.index].name == 'setTimeout') { - resolver() - } else { - resolver(cxt.io_cache[resolution.index].value) - } - console.log('RESOLVE', cxt.io_cache_index, resolution.index) + if(cxt.io_cache[resolution.index].name == 'setTimeout') { + resolver() + } else { + resolver(cxt.io_cache[resolution.index].value) } + console.log('RESOLVE', cxt.io_cache_index, resolution.index) } } @@ -192,9 +199,6 @@ const io_patch = (cxt, path, use_context = false) => { cxt.io_cache_index++ if(call.ok) { - // TODO resolve promises in the same order they were resolved on - // initial execution - if(call.value instanceof cxt.window.Promise) { return new Promise(resolve => { cxt.io_cache_resolvers.set(cxt.io_cache_index - 1, resolve) diff --git a/test/test.js b/test/test.js index 75fe6cc..4a5735a 100644 --- a/test/test.js +++ b/test/test.js @@ -3127,8 +3127,6 @@ const y = x()` console.log(await delay(0)) ` - console.log('CODE2', code2.slice(75)) - const next = await command_input_async(i, code2, 0) // Assert cache was used