async calls find_call and test async call nav

This commit is contained in:
Dmitry Vasilev
2022-11-24 04:41:34 +08:00
parent 7fa3e78db3
commit 9a8e86a390
3 changed files with 186 additions and 56 deletions

View File

@@ -606,7 +606,7 @@ export const find_call = (state, index) => {
if(ct_node_id != null) { if(ct_node_id != null) {
const ct_node = find_node( const ct_node = find_node(
root_calltree_node(state), state.calltree,
n => n.id == ct_node_id n => n.id == ct_node_id
) )
if(ct_node == null) { if(ct_node == null) {
@@ -630,7 +630,12 @@ export const find_call = (state, index) => {
} }
const loc = {index: node.index, module: state.current_module} const loc = {index: node.index, module: state.current_module}
const {calltree, call} = state.calltree_actions.find_call( const {
calltree,
call,
is_found_async_call,
async_call_index
} = state.calltree_actions.find_call(
loc, loc,
get_async_calls(state) get_async_calls(state)
) )
@@ -645,20 +650,41 @@ export const find_call = (state, index) => {
) )
} }
const merged = make_calltree( let next_calltree, active_calltree_node
merge_calltrees(root_calltree_node(state), calltree),
get_async_calls(state),
)
const active_calltree_node = find_same_node( if(is_found_async_call) {
root_calltree_node({calltree: merged}), const async_calls = get_async_calls(state)
calltree, const prev_call = async_calls[async_call_index]
call.id const merged = merge_calltrees(prev_call, calltree)
) const next_async_calls = async_calls.map((c, i) =>
i == async_call_index
? merged
: c
)
next_calltree = make_calltree(
root_calltree_node(state),
next_async_calls,
)
active_calltree_node = find_same_node(
merged,
calltree,
call.id
)
} else {
next_calltree = make_calltree(
merge_calltrees(root_calltree_node(state), calltree),
get_async_calls(state),
)
active_calltree_node = find_same_node(
root_calltree_node({calltree: next_calltree}),
calltree,
call.id
)
}
return add_frame( return add_frame(
expand_path( expand_path(
{...state, calltree: merged}, {...state, calltree: next_calltree},
active_calltree_node active_calltree_node
), ),
active_calltree_node, active_calltree_node,

View File

@@ -312,19 +312,40 @@ export const eval_modules = (
const find_call = (location, async_calls) => { const find_call = (location, async_calls) => {
searched_location = location searched_location = location
const {modules, calltree} = run() let is_found_async_call = false
let i
let {calltree} = run()
is_recording_async_calls = false
if(found_call == null && async_calls != null) { if(found_call == null && async_calls != null) {
for(let c of async_calls) { for(i = 0; i < async_calls.length; i++) {
c.fn.apply(c.context, c.args) const c = async_calls[i]
try {
c.fn.apply(c.context, c.args)
} catch(e) {
// do nothing. Exception was caught and recorded inside 'trace'
}
if(found_call != null) { if(found_call != null) {
is_found_async_call = true
calltree = children[0]
children = null
break break
} }
} }
} }
is_recording_async_calls = true
searched_location = null searched_location = null
const call = found_call const call = found_call
found_call = null found_call = null
return {modules, calltree, call} return {
is_found_async_call,
async_call_index: i,
calltree,
call
}
} }
const trace = (fn, name, argscount, __location, get_closure) => { const trace = (fn, name, argscount, __location, get_closure) => {
@@ -563,8 +584,15 @@ export const eval_modules = (
return assign_code(parse_result.modules, expanded) return assign_code(parse_result.modules, expanded)
}, },
find_call: (loc, async_calls) => { find_call: (loc, async_calls) => {
const {modules, calltree, call} = actions.find_call(loc, async_calls) const {
is_found_async_call,
async_call_index,
calltree,
call
} = actions.find_call(loc, async_calls)
return { return {
is_found_async_call,
async_call_index,
calltree: assign_code(parse_result.modules, calltree), calltree: assign_code(parse_result.modules, calltree),
// TODO: `call` does not have `code` property here. Currently it is // TODO: `call` does not have `code` property here. Currently it is
// worked around by callers. Refactor // worked around by callers. Refactor

View File

@@ -2313,16 +2313,13 @@ const y = x()`
test('async calls', () => { test('async calls', () => {
const code = ` const code = `
const fn = () => { export const fn = () => {
fn2() fn2()
} }
const fn2 = () => { const fn2 = () => {
console.log(1) console.log(1)
} }
// Use Function constructor to exec impure code for testing
new Function('fn', 'globalThis.__run_async_call = fn')(fn)
` `
const {get_async_call, on_async_call} = (new Function(` const {get_async_call, on_async_call} = (new Function(`
@@ -2338,7 +2335,10 @@ const y = x()`
`))() `))()
const i = test_initial_state(code, { on_async_call }) const i = test_initial_state(code, { on_async_call })
globalThis.__run_async_call(10)
// Make async call
i.modules[''].fn(10)
const call = get_async_call() const call = get_async_call()
assert_equal(call.fn.name, 'fn') assert_equal(call.fn.name, 'fn')
assert_equal(call.code.index, code.indexOf('() => {')) assert_equal(call.code.index, code.indexOf('() => {'))
@@ -2360,20 +2360,15 @@ const y = x()`
assert_equal(nav2.state.current_calltree_node.fn.name, 'fn2') assert_equal(nav2.state.current_calltree_node.fn.name, 'fn2')
}), }),
// TODO
/*
test('async calls calltree nav', () => { test('async calls calltree nav', () => {
const code = ` const code = `
const fn = () => { const normal_call = () => {
fn2()
} }
const fn2 = () => { normal_call(0)
console.log(1)
}
// Use Function constructor to exec impure code for testing export const async_call = () => {
new Function('fn', 'globalThis.__run_async_call = fn')(fn) }
` `
const {get_async_call, on_async_call} = (new Function(` const {get_async_call, on_async_call} = (new Function(`
@@ -2389,41 +2384,76 @@ const y = x()`
`))() `))()
const i = test_initial_state(code, { on_async_call }) const i = test_initial_state(code, { on_async_call })
globalThis.__run_async_call(10)
const call = get_async_call()
assert_equal(call.fn.name, 'fn')
assert_equal(call.code.index, code.indexOf('() => {'))
assert_equal(call.args, [10])
const state = COMMANDS.on_async_call(i, call)
assert_equal(get_async_calls(state), [call])
assert_equal(state.logs.logs.length, 1) const after_async_calls = [1, 2, 3].reduce(
(s, a) => {
// Make async calls
i.modules[''].async_call(a)
const call = get_async_call()
return COMMANDS.on_async_call(s, call)
},
i
)
// Expand call assert_equal(
const {state: expanded} = COMMANDS.calltree.click(state, call.id) get_async_calls(after_async_calls).map(c => c.args[0]),
assert_equal(get_async_calls(expanded)[0].children[0].fn.name, 'fn2') [1,2,3]
)
assert_equal(after_async_calls.current_calltree_node.toplevel, true)
const down = COMMANDS.calltree.arrow_down(after_async_calls).state
const first_async_call_selected = COMMANDS.calltree.arrow_down(
COMMANDS.calltree.arrow_down(after_async_calls).state
).state
// After we press arrow down, first async call gets selected
assert_equal(
first_async_call_selected.current_calltree_node.args[0],
1,
)
// One more arrow down, second async call gets selected
assert_equal(
COMMANDS.calltree.arrow_down(first_async_call_selected)
.state
.current_calltree_node
.args[0],
2
)
// After we press arrow up when first async call selected, we select last
// visible non async call
assert_equal(
COMMANDS.calltree.arrow_up(first_async_call_selected)
.state
.current_calltree_node
.args[0],
0
)
// After we press arrow left when first async call selected, we stay on
// this call
assert_equal(
COMMANDS.calltree.arrow_left(first_async_call_selected)
.current_calltree_node
.args[0],
1
)
// Navigate logs
const nav = COMMANDS.calltree.navigate_logs_position(expanded, 0)
assert_equal(nav.state.current_calltree_node.is_log, true)
const nav2 = COMMANDS.calltree.arrow_left(nav.state)
assert_equal(nav2.state.current_calltree_node.fn.name, 'fn2')
}), }),
*/
test_only('async_calls find_call', () => { test('async_calls find_call', () => {
const code = ` const code = `
const fn = () => { export const fn = () => {
fn2() fn2()
} }
const fn2 = () => { const fn2 = () => {
console.log(1) console.log(1)
} }
// Use Function constructor to exec impure code for testing
new Function('fn', 'globalThis.__run_async_call = fn')(fn)
` `
const {get_async_call, on_async_call} = (new Function(` const {get_async_call, on_async_call} = (new Function(`
@@ -2439,12 +2469,58 @@ const y = x()`
`))() `))()
const i = test_initial_state(code, { on_async_call }) const i = test_initial_state(code, { on_async_call })
globalThis.__run_async_call(10)
// Make async call
i.modules[''].fn()
const call = get_async_call() const call = get_async_call()
const state = COMMANDS.on_async_call(i, call) const state = COMMANDS.on_async_call(i, call)
const {state: moved} = COMMANDS.move_cursor(state, code.indexOf('fn2')) const {state: moved} = COMMANDS.move_cursor(state, code.indexOf('fn2'))
console.log('active_calltree_node', moved.active_calltree_node) assert_equal(moved.active_calltree_node.fn.name, 'fn')
// Move cursor to toplevel and back, find cached (calltree_node_by_loc) call
const move_back = COMMANDS.move_cursor(
COMMANDS.move_cursor(moved, 0).state,
code.indexOf('fn2')
).state
assert_equal(move_back.active_calltree_node.fn.name, 'fn')
}), }),
test('async_calls find_call then async_call bug', () => {
const code = `
export const fn = () => { /* label */ }
`
const {get_async_call, on_async_call} = (new Function(`
let call
return {
get_async_call() {
return call
},
on_async_call(_call) {
call = _call
}
}
`))()
const i = test_initial_state(code, { on_async_call })
// Make async call
i.modules[''].fn(1)
const state = COMMANDS.on_async_call(i, get_async_call())
// find call
const {state: moved} = COMMANDS.move_cursor(state, code.indexOf('label'))
// Make async call
i.modules[''].fn(2)
const result = COMMANDS.on_async_call(moved, get_async_call())
// there was a bug throwing error when added second async call
assert_equal(get_async_calls(result).map(c => c.args), [[1], [2]])
}),
] ]