This commit is contained in:
Dmitry Vasilev
2022-12-15 21:15:52 +08:00
parent de2d030aa9
commit 2cbb546b8c
5 changed files with 131 additions and 66 deletions

View File

@@ -7,6 +7,7 @@ import {eval_frame} from './eval.js'
export const pp_calltree = tree => ({ export const pp_calltree = tree => ({
id: tree.id, id: tree.id,
ok: tree.ok, ok: tree.ok,
args: tree.args,
value: tree.value, value: tree.value,
is_log: tree.is_log, is_log: tree.is_log,
has_more_children: tree.has_more_children, has_more_children: tree.has_more_children,

View File

@@ -18,31 +18,39 @@ import {
set_cursor_position, current_cursor_position, set_location set_cursor_position, current_cursor_position, set_location
} from './calltree.js' } from './calltree.js'
const collect_logs = call => const collect_logs = (logs, call) => {
collect_nodes_with_parents(call, n => n.is_log) const id_to_log = new Map(
.map(({parent, node}) => ( collect_nodes_with_parents(call, n => n.is_log)
{ .map(({parent, node}) => (
id: node.id, [
toplevel: parent.toplevel, node.id,
module: parent.toplevel {
? parent.module id: node.id,
: parent.fn.__location.module, toplevel: parent.toplevel,
parent_name: parent.fn?.name, module: parent.toplevel
args: node.args, ? parent.module
log_fn_name: node.fn.name, : parent.fn.__location.module,
} parent_name: parent.fn?.name,
)) args: node.args,
log_fn_name: node.fn.name,
}
]
))
)
return logs.map(l => id_to_log.get(l.id))
}
const apply_eval_result = (state, eval_result) => { const apply_eval_result = (state, eval_result) => {
// TODO what if console.log called from native fn (like Array::map)? // TODO what if console.log called from native fn (like Array::map)?
// Currently it is not recorded. Maybe we should monkey patch `console`? // Currently it is not recorded. Maybe we should monkey patch `console`?
const logs = collect_logs(eval_result.calltree)
return { return {
...state, ...state,
calltree: make_calltree(eval_result.calltree, null), calltree: make_calltree(eval_result.calltree, null),
calltree_actions: eval_result.calltree_actions, calltree_actions: eval_result.calltree_actions,
logs: {logs, log_position: null}, logs: {
logs: collect_logs(eval_result.logs, eval_result.calltree),
log_position: null
},
modules: eval_result.modules, modules: eval_result.modules,
} }
} }
@@ -751,7 +759,7 @@ const move_cursor = (s, index) => {
return do_move_cursor(state, index) return do_move_cursor(state, index)
} }
const on_deferred_call = (state, call, calltree_changed_token) => { const on_deferred_call = (state, call, calltree_changed_token, logs) => {
if(state.calltree_changed_token != calltree_changed_token) { if(state.calltree_changed_token != calltree_changed_token) {
return state return state
} }
@@ -760,7 +768,10 @@ const on_deferred_call = (state, call, calltree_changed_token) => {
root_calltree_node(state), root_calltree_node(state),
[...(get_deferred_calls(state) ?? []), call], [...(get_deferred_calls(state) ?? []), call],
), ),
logs: {...state.logs, logs: state.logs.logs.concat(collect_logs(call))}, logs: {
...state.logs,
logs: state.logs.logs.concat(collect_logs(logs, call))
},
} }
} }

View File

@@ -290,6 +290,8 @@ export const eval_modules = (
// TODO use native array for stack for perf? // TODO use native array for stack for perf?
const stack = new Array() const stack = new Array()
let logs = []
let call_counter = 0 let call_counter = 0
let current_module let current_module
@@ -336,7 +338,7 @@ export const eval_modules = (
let is_found_deferred_call = false let is_found_deferred_call = false
let i let i
let {calltree} = run() let {calltree, logs} = run()
is_recording_deferred_calls = false is_recording_deferred_calls = false
if(found_call == null && deferred_calls != null) { if(found_call == null && deferred_calls != null) {
@@ -365,7 +367,8 @@ export const eval_modules = (
is_found_deferred_call, is_found_deferred_call,
deferred_call_index: i, deferred_call_index: i,
calltree, calltree,
call call,
logs,
} }
} }
@@ -418,17 +421,20 @@ export const eval_modules = (
try { try {
value = fn(...args) value = fn(...args)
ok = true ok = true
return value instanceof Promise.Original if(value instanceof Promise.Original) {
? value set_record_call()
.then(v => { return value
value.status = {ok: true, value: v} .then(v => {
return v value.status = {ok: true, value: v}
}) return v
.catch(e => { })
value.status = {ok: false, error: e} .catch(e => {
throw e value.status = {ok: false, error: e}
}) throw e
: value })
} else {
return value
}
} catch(_error) { } catch(_error) {
ok = false ok = false
error = _error error = _error
@@ -476,7 +482,9 @@ export const eval_modules = (
} }
const call = children[0] const call = children[0]
children = null children = null
on_deferred_call(call, calltree_changed_token) const _logs = logs
logs = []
on_deferred_call(call, calltree_changed_token, _logs)
} }
} }
} }
@@ -548,6 +556,11 @@ export const eval_modules = (
is_new, is_new,
} }
if(is_log) {
// TODO do not collect logs on find_call?
logs.push(call)
}
const should_record_call = stack.pop() const should_record_call = stack.pop()
if(should_record_call) { if(should_record_call) {
@@ -602,8 +615,10 @@ export const eval_modules = (
current_call.children = children current_call.children = children
if(!current_call.ok) { if(!current_call.ok) {
is_recording_deferred_calls = true is_recording_deferred_calls = true
const _logs = logs
logs = []
children = null children = null
return { modules: __modules, calltree: current_call } return { modules: __modules, calltree: current_call, logs: _logs }
} }
` `
) )
@@ -611,8 +626,10 @@ export const eval_modules = (
+ +
` `
is_recording_deferred_calls = true is_recording_deferred_calls = true
const _logs = logs
logs = []
children = null children = null
return { modules: __modules, calltree: current_call } return { modules: __modules, calltree: current_call, logs: _logs }
} }
return { return {
@@ -634,10 +651,11 @@ export const eval_modules = (
: map_object(external_imports, (name, {module}) => module), : map_object(external_imports, (name, {module}) => module),
/* on_deferred_call */ /* on_deferred_call */
(call, calltree_changed_token) => { (call, calltree_changed_token, logs) => {
return on_deferred_call( return on_deferred_call(
assign_code(parse_result.modules, call), assign_code(parse_result.modules, call),
calltree_changed_token, calltree_changed_token,
logs,
) )
}, },
@@ -655,7 +673,8 @@ export const eval_modules = (
is_found_deferred_call, is_found_deferred_call,
deferred_call_index, deferred_call_index,
calltree, calltree,
call call,
logs,
} = actions.find_call(loc, deferred_calls) } = actions.find_call(loc, deferred_calls)
return { return {
is_found_deferred_call, is_found_deferred_call,
@@ -664,6 +683,7 @@ export const eval_modules = (
// 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
call, call,
logs,
} }
} }
} }
@@ -676,6 +696,7 @@ export const eval_modules = (
modules: result.modules, modules: result.modules,
calltree: assign_code(parse_result.modules, result.calltree), calltree: assign_code(parse_result.modules, result.calltree),
call: result.call, call: result.call,
logs: result.logs,
calltree_actions, calltree_actions,
}) })

View File

@@ -25,6 +25,20 @@ import {
export const tests = [ export const tests = [
// TODO
/*
test('trace', () => {
assert_code_evals_to(`
const trace = () => 1;
trace()
`,
1
)
}),
*/
test('invalid token in the beginning', () => { test('invalid token in the beginning', () => {
const result = parse('# import') const result = parse('# import')
assert_equal(result, { assert_equal(result, {
@@ -2697,38 +2711,57 @@ const y = x()`
) )
}), }),
//test('async/await calltree', async () => { test('async/await calltree', async () => {
// const i = await test_initial_state_async(` const i = await test_initial_state_async(`
// const x = () => 1 const x = () => 1
// const delay = async time => { const delay = async time => {
// await 1 //Promise.resolve() await 1 //TODO Promise.resolve()
// x() x()
// } }
// await delay(3) await delay(3)
// /* TODO /* TODO
// await Promise.all([ await Promise.all([
// delay(3), delay(3),
// ]) ])
// */ */
// `) `)
// log(pp_calltree(root_calltree_node(i))) const root = root_calltree_node(i)
// assert_equal(root_calltree_node(i).children.length, 1) assert_equal(root.children.length, 1)
//}), const call_delay = root.children[0]
assert_equal(call_delay.fn.name, 'delay')
assert_equal(call_delay.fn.name, 'delay')
}),
// TODO
test('async/await logs out of order', async () => { test('async/await logs out of order', async () => {
const i = await test_initial_state_async(` const i = await test_initial_state_async(`
const delay = async time => { const delay = async time => {
await new Promise(res => globalThis.setTimeout(res, time*10)) await new Promise(res => globalThis.setTimeout(res, time*10))
console.log(time) console.log(time)
} }
await Promise.all([
delay(3), await Promise.all([delay(2), delay(1)])
delay(2),
delay(1),
])
`) `)
const logs = i.logs.logs.map(l => l.args[0]) const logs = i.logs.logs.map(l => l.args[0])
assert_equal(logs, [1,2,3]) assert_equal(logs, [1, 2])
}) }),
test('async/await logs out of order', async () => {
const i = await test_initial_state_async(`
// Init promises p1 and p2 that are resolved in different order (p2 then
// p1)
const p2 = Promise.resolve(2)
const p1 = p2.then(() => 1)
const log = async p => {
const v = await p
console.log(v)
}
await Promise.all([log(p1), log(p2)])
`)
const logs = i.logs.logs.map(l => l.args[0])
assert_equal(logs, [2, 1])
}),
] ]

View File

@@ -71,14 +71,13 @@ export const test_initial_state_async = async code => {
export const test_deferred_calls_state = code => { export const test_deferred_calls_state = code => {
const {get_deferred_call, on_deferred_call} = (new Function(` const {get_deferred_call, on_deferred_call} = (new Function(`
let call, calltree_changed_token let args
return { return {
get_deferred_call() { get_deferred_call() {
return [call, calltree_changed_token] return args
}, },
on_deferred_call(_call, _calltree_changed_token) { on_deferred_call(..._args) {
call = _call args = _args
calltree_changed_token = _calltree_changed_token
} }
} }
`))() `))()