From 5310a0c2d97811a06f4f9dfe7433c6e9534c3837 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilev Date: Sat, 13 May 2023 11:13:29 +0300 Subject: [PATCH] WIP --- docs/examples/ethers/index.js | 5 +-- src/record_io.js | 78 +++++++++++++---------------------- src/runtime.js | 9 ++-- test/test.js | 11 +++++ 4 files changed, 47 insertions(+), 56 deletions(-) diff --git a/docs/examples/ethers/index.js b/docs/examples/ethers/index.js index 2745ae5..f4f6f3c 100644 --- a/docs/examples/ethers/index.js +++ b/docs/examples/ethers/index.js @@ -1,7 +1,6 @@ //import {ethers} from 'https://unpkg.com/ethers/dist/ethers.js' import {ethers} from 'https://unpkg.com/ethers@5.7.2/dist/ethers.esm.js' -/* const URL = 'https://ethereum-goerli-rpc.allthatnode.com' const p = ethers.getDefaultProvider(URL) @@ -12,7 +11,7 @@ const latest = await p.getBlock() latest -const txs = await Promise.all(latest.transactions.slice(0,2).map(t => +const txs = await Promise.all(latest.transactions.map(t => p.getTransactionReceipt(t) )) @@ -23,7 +22,7 @@ const totalGas = txs.reduce((gas,tx) => - totalGas.add(20) + totalGas.add(21) /* diff --git a/src/record_io.js b/src/record_io.js index f88dbc9..5427531 100644 --- a/src/record_io.js +++ b/src/record_io.js @@ -1,15 +1,22 @@ import {set_record_call} from './runtime.js' -const get_object_to_patch = (cxt, path) => { +// Modify window object on module load +// TODO check - if modules reloaded on window reopen +// TODO - cxt.window +apply_io_patches() + +// Current context for current execution of code +let cxt + +export const set_current_context = _cxt => { + cxt = _cxt +} + +const io_patch = (path, use_context = false) => { let obj = cxt.window for(let i = 0; i < path.length - 1; i++) { obj = obj[path[i]] } - return obj -} - -const io_patch = (cxt, path, use_context = false) => { - const obj = get_object_to_patch(cxt, path) const method = path.at(-1) if(obj == null || obj[method] == null) { // Method is absent in current env, skip patching @@ -202,69 +209,40 @@ const io_patch = (cxt, path, use_context = false) => { obj[method].__original = original } -const io_patch_remove = (cxt, path) => { - const obj = get_object_to_patch(cxt, path) - const method = path.at(-1) - if(obj == null || obj[method] == null) { - // Method is absent in current env, skip patching - return - } - - obj[method] = obj[method].__original -} - -const Response_methods = [ - 'arrayBuffer', - 'blob', - 'formData', - 'json', - 'text', -] - // TODO bare IO functions should not be exposed at all, to allow calling it // only from patched versions. Especially setInterval which can cause leaks -export const apply_io_patches = cxt => { - io_patch(cxt, ['Math', 'random']) +export const apply_io_patches = () => { + io_patch(['Math', 'random']) - io_patch(cxt, ['setTimeout']) + io_patch(['setTimeout']) // TODO if call setTimeout and then clearTimeout, cache it and remove call of // clearTimeout, and make only setTimeout, then it would never be called when // replaying from cache - io_patch(cxt, ['clearTimeout']) + io_patch(['clearTimeout']) // TODO patch setInterval to only cleanup all intervals on finish const Date = cxt.window.Date - io_patch(cxt, ['Date']) + io_patch(['Date']) cxt.window.Date.parse = Date.parse cxt.window.Date.now = Date.now cxt.window.Date.UTC = Date.UTC - io_patch(cxt, ['Date', 'now']) + io_patch(['Date', 'now']) - io_patch(cxt, ['fetch']) + io_patch(['fetch']) // Check if Response is defined, for node.js if(cxt.window.Response != null) { + const Response_methods = [ + 'arrayBuffer', + 'blob', + 'formData', + 'json', + 'text', + ] for(let key of Response_methods) { - io_patch(cxt, ['Response', 'prototype', key], true) - } - } -} - -export const remove_io_patches = cxt => { - io_patch_remove(cxt, ['Math', 'random']) - - io_patch_remove(cxt, ['setTimeout']) - io_patch_remove(cxt, ['clearTimeout']) - - io_patch_remove(cxt, ['Date']) - io_patch_remove(cxt, ['fetch']) - - // Check if Response is defined, for node.js - if(cxt.window.Response != null) { - for(let key of Response_methods) { - io_patch_remove(cxt, ['Response', 'prototype', key]) + io_patch(['Response', 'prototype', key], true) } } } diff --git a/src/runtime.js b/src/runtime.js index a656fd1..be79af1 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -1,4 +1,4 @@ -import {apply_io_patches, remove_io_patches} from './record_io.js' +import {set_current_context} from './record_io.js' /* Converts generator-returning function to promise-returning function. Allows to @@ -50,7 +50,7 @@ const do_run = function*(module_fns, cxt, io_cache){ } apply_promise_patch(cxt) - apply_io_patches(cxt) + set_current_context(cxt) for(let {module, fn} of module_fns) { cxt.found_call = null @@ -80,7 +80,6 @@ const do_run = function*(module_fns, cxt, io_cache){ cxt.logs = [] cxt.children = null - remove_io_patches(cxt) remove_promise_patch(cxt) cxt.searched_location = null @@ -97,6 +96,10 @@ const do_run = function*(module_fns, cxt, io_cache){ } export const run = gen_to_promise(function*(module_fns, cxt, io_cache) { + if(cxt.run_window != globalThis) { + // TODO refactor, remove cxt.run_window + throw new Error('illegal state') + } const result = yield* do_run(module_fns, cxt, io_cache) if(result.eval_cxt.io_cache_is_replay_aborted) { diff --git a/test/test.js b/test/test.js index 85abdf8..ce70439 100644 --- a/test/test.js +++ b/test/test.js @@ -3231,4 +3231,15 @@ const y = x()` // Deferred calls should not be record in cache assert_equal(state.eval_cxt.io_cache.length, 0) }), + + test_only('record io discard prev execution', () => { + // Populate cache + const i = test_initial_state(`Math.random(0)`) + + // Run code that does not remove IO patches immediately + const next = COMMANDS.input(i, `await Promise.resolve()`, 0) + + const next2 = COMMANDS.input(i, `Math.random(1)`, 0).state + console.log('n', next2.io_cache) + }), ]