From 4dbc7c26e29d754b23eb1820a21aece4689081cb Mon Sep 17 00:00:00 2001 From: Dmitry Vasilev Date: Thu, 22 Dec 2022 22:14:36 +0800 Subject: [PATCH] WIP --- src/eval.js | 5 +++ src/patch_promise.js | 27 +++++++++++++++- src/reserved.js | 8 +++-- test/self_hosted_test.js | 8 ++--- test/test.js | 34 ++++++++++++++++----- test/utils.js | 66 +++++++++++++++++++++------------------- 6 files changed, 102 insertions(+), 46 deletions(-) diff --git a/src/eval.js b/src/eval.js index 058774c..2f7a4f7 100644 --- a/src/eval.js +++ b/src/eval.js @@ -276,6 +276,11 @@ export const eval_modules = ( const codestring = ` let children, prev_children + + // TODO refactor, move patch_promise here + globalThis.get_children = () => children + globalThis.set_children = (_children) => children = _children + // TODO use native array for stack for perf? const stack = new Array() diff --git a/src/patch_promise.js b/src/patch_promise.js index bb35b91..9b2e616 100644 --- a/src/patch_promise.js +++ b/src/patch_promise.js @@ -5,11 +5,36 @@ export const patch_promise = window => { return } + class PromiseRecordChildren extends Promise { + then(on_resolve, on_reject) { + let children = window.get_children() + if(children == null) { + children = [] + window.set_children(children) + } + return super.then( + on_resolve == null + ? null + : value => { + window.set_children(children) + return on_resolve(value) + }, + + on_reject == null + ? null + : error => { + window.set_children(children) + return on_reject(error) + } + ) + } + } + class PromiseWithStatus extends window.Promise { constructor(fn) { let status let is_constructor_finished = false - const p = new window.Promise.Original( + const p = new PromiseRecordChildren( (resolve, reject) => { fn( (value) => { diff --git a/src/reserved.js b/src/reserved.js index 2c76d9f..7e324be 100644 --- a/src/reserved.js +++ b/src/reserved.js @@ -3,7 +3,9 @@ export const reserved = [ 'break', 'case', -'catch', +// TODO: fix parser to allow catch be an Object key, as other reserved words. +// Currently we make exception for promise.catch +// 'catch', 'class', 'const', 'continue', @@ -14,7 +16,9 @@ export const reserved = [ 'else', 'export', 'extends', -'finally', +// TODO: fix parser to allow finally be an Object key, as other reserved words. +// Currently we make exception for promise.finally +// 'finally', 'for', 'function', 'if', diff --git a/test/self_hosted_test.js b/test/self_hosted_test.js index acac2db..b7be520 100644 --- a/test/self_hosted_test.js +++ b/test/self_hosted_test.js @@ -67,10 +67,8 @@ const dir = load_dir('.') console.time('run') const i = test_initial_state( - ` - import './test/run.js' - `, - {project_dir: dir} + {}, // files + {project_dir: dir, entrypoint: 'test/run.js'} ) assert_equal(i.loading_external_imports_state != null, true) @@ -81,6 +79,8 @@ assert_equal(loaded.eval_modules_state != null, true) const s = loaded.eval_modules_state const result = await s.promise const state = COMMANDS.eval_modules_finished(loaded , result, s.node, s.toplevel) +const root = root_calltree_node(state) +const run = root.children[0] assert_equal(root_calltree_node(state).ok, true) diff --git a/test/test.js b/test/test.js index c845ad1..4fbdf8b 100644 --- a/test/test.js +++ b/test/test.js @@ -2779,18 +2779,38 @@ const y = x()` ) }), - // TODO - /* test('async/await Promise.then creates subcall', async () => { const i = await test_initial_state_async(` - await Promise.resolve(1).then(x => { - }) + const x = () => 1 + await Promise.resolve(1).then(x) `) - console.log('i', root_calltree_node(i)) - + const root = root_calltree_node(i) + assert_equal(root.children.at(-1).children[0].fn.name, 'x') }), - test('async/await bug', async () => { + test('async/await Promise.catch creates subcall', async () => { + const i = await test_initial_state_async(` + const x = () => 1 + await Promise.reject(1).catch(x) + `) + const root = root_calltree_node(i) + assert_equal(root.children.at(-1).children[0].fn.name, 'x') + }), + + + test_only('async/await native Promise.then creates subcall', async () => { + const i = await test_initial_state_async(` + const x = () => 1 + const async_fn = async () => 1 + await async_fn().then(x) + `) + const root = root_calltree_node(i) + assert_equal(root.children.at(-1).children[0].fn.name, 'x') + }), + + /* + // TODO + test('async/await move_cursor', async () => { const code = ` const f = async () => { console.log('f') diff --git a/test/utils.js b/test/utils.js index b3e05f0..69fcd2c 100644 --- a/test/utils.js +++ b/test/utils.js @@ -151,21 +151,20 @@ export const test_only = (message, t) => test(message, t, true) // TODO in calltree view, hide fn which has special flag set (see // filter_calltree) -const AsyncFunction = new Function(`return (async () => {}).constructor`)() - -export const run = Object.defineProperty(new AsyncFunction('tests', ` - const run_test = async t => { - try { - await t.test() - } catch(e) { - if(globalThis.process != null) { - // In node.js runner, fail fast - console.error('Failed: ' + t.message) - throw e - } else { - return e - } - } +export const run = Object.defineProperty(new Function('tests', ` + // Runs test, return failure or null if not failed + const run_test = t => { + return Promise.resolve(t.test()) + .then(() => null) + .catch(e => { + if(globalThis.process != null) { + // In node.js runner, fail fast + console.error('Failed: ' + t.message) + throw e + } else { + return e + } + }) } // If not run in node, then dont apply filter @@ -178,32 +177,35 @@ export const run = Object.defineProperty(new AsyncFunction('tests', ` // Exec each test. After all tests are done, we rethrow first error if // any. So we will mark root calltree node if one of tests failed - const failure = await tests_to_run.reduce( - async (failureP, t) => { - const failure = await failureP - const next_failure = await run_test(t) - return failure ?? next_failure - }, + return tests_to_run.reduce( + (failureP, t) => + Promise.resolve(failureP).then(failure => + run_test(t).then(next_failure => failure ?? next_failure) + ) + , null - ) + ).then(failure => { - if(failure != null) { - throw failure - } else { - if(globalThis.process != null) { - console.log('Ok') + if(failure != null) { + throw failure + } else { + if(globalThis.process != null) { + console.log('Ok') + } } - } + + }) } else { const test = tests.find(t => t.message.includes(filter)) if(test == null) { throw new Error('test not found') } else { - await run_test(test) - if(globalThis.process != null) { - console.log('Ok') - } + return run_test(test).then(() => { + if(globalThis.process != null) { + console.log('Ok') + } + }) } } `), 'name', {value: 'run'})