finish record io

This commit is contained in:
Dmitry Vasilev
2023-02-13 17:39:34 +08:00
parent 56ea47a9ab
commit 6c82e78a0f
19 changed files with 249 additions and 24826 deletions

View File

@@ -41,6 +41,17 @@ const is_stackoverflow = node =>
// Firefox
node.error?.message == "too much recursion"
export const has_error = n =>
!n.ok
||
(
n.value instanceof globalThis.run_window.Promise
&&
n.value.status != null
&&
!n.value.status.ok
)
export const calltree_node_loc = node => node.toplevel
? {module: node.module}
: node.fn.__location

View File

@@ -3,7 +3,8 @@ import {el, stringify, fn_link, scrollIntoViewIfNeeded} from './domutils.js'
import {FLAGS} from '../feature_flags.js'
import {stringify_for_header} from './value_explorer.js'
import {find_node} from '../ast_utils.js'
import {is_expandable, root_calltree_node, get_deferred_calls} from '../calltree.js'
import {is_expandable, root_calltree_node, get_deferred_calls, has_error}
from '../calltree.js'
// TODO perf - quadratic difficulty
const join = arr => arr.reduce(
@@ -13,17 +14,6 @@ const join = arr => arr.reduce(
[],
)
const is_error = n =>
!n.ok
||
(
n.value instanceof globalThis.run_window.Promise
&&
n.value.status != null
&&
!n.value.status.ok
)
export class CallTree {
constructor(ui, container) {
this.ui = ui
@@ -46,9 +36,11 @@ export class CallTree {
this.ui.editor.focus()
}
/* TODO test
if(e.key == 'F3') {
this.ui.set_active_tab('logs')
}
*/
if(e.key == 'a') {
if(FLAGS.embed_value_explorer) {
@@ -119,7 +111,7 @@ export class CallTree {
)
: el('span',
'call_header '
+ (is_error(n) ? 'error' : '')
+ (has_error(n) ? 'error' : '')
+ (n.fn.__location == null ? ' native' : '')
,
// TODO show `this` argument

View File

@@ -297,7 +297,7 @@ export class Editor {
this.ace_editor.commands.removeCommand('goToNextError')
this.ace_editor.commands.bindKey("F4", "goto_definition");
this.ace_editor.commands.bindKey("F5", "goto_definition");
VimApi._mapCommand({
keys: 'gd',
type: 'action',

45
src/editor/io_cache.js Normal file
View File

@@ -0,0 +1,45 @@
import {header, stringify_for_header} from './value_explorer.js'
import {el} from './domutils.js'
import {has_error} from '../calltree.js'
// TODO render grey items there were not used in run
export class IO_Cache {
constructor(ui, el) {
this.el = el
this.ui = ui
this.el.addEventListener('keydown', (e) => {
if(e.key == 'Escape') {
this.ui.editor.focus()
}
if(e.key == 'F4') {
this.ui.editor.focus()
}
})
}
render_io_cache(items) {
this.el.innerHTML = ''
for(let item of items) {
if(item.type == 'resolution') {
continue
}
this.el.appendChild(
el('div',
'call_header ' + (has_error(item) ? 'error' : ''),
item.name,
'(' ,
// TODO fn_link, like in ./calltree.js
item.args.map(a => header(a)).join(', '),
'): ' ,
(item.ok ? stringify_for_header(item.value) : item.error.toString())
)
)
}
}
}

View File

@@ -22,9 +22,11 @@ export class Logs {
this.ui.editor.focus_value_explorer(this.el)
}
/* TODO test
if(e.key == 'F2') {
this.ui.set_active_tab('calltree')
}
*/
if(e.key == 'F3') {
this.ui.editor.focus()
@@ -75,6 +77,7 @@ export class Logs {
+ ':'
),
' ',
// TODO fn_link, for function args, like in ./calltree.js
log.args.map(a => header(a)).join(', ')
)
)

View File

@@ -3,6 +3,7 @@ import {Editor} from './editor.js'
import {Files} from './files.js'
import {CallTree} from './calltree.js'
import {Logs} from './logs.js'
import {IO_Cache} from './io_cache.js'
import {Eval} from './eval.js'
import {el} from './domutils.js'
import {FLAGS} from '../feature_flags.js'
@@ -41,6 +42,12 @@ export class UI {
href: 'javascript: void(0)',
}, 'Logs (F3)')
),
this.tabs.io_cache = el('div', 'tab',
el('a', {
click: () => this.set_active_tab('io_cache'),
href: 'javascript: void(0)',
}, 'IO cache (F4)')
),
this.entrypoint_select = el('div', 'entrypoint_select')
),
this.debugger.calltree = el('div', {
@@ -51,6 +58,10 @@ export class UI {
'class': 'tab_content logs',
tabindex: 0,
}),
this.debugger.io_cache = el('div', {
'class': 'tab_content io_cache',
tabindex: 0,
}),
),
this.debugger_loading = el('div', 'debugger_wrapper')
),
@@ -74,11 +85,19 @@ export class UI {
*/
el('a', {
'class': 'open_run_window',
'class': 'statusbar_action first',
href: 'javascript: void(0)',
click: () => exec('clear_io_cache')
},
'Clear IO cache (F6)'
),
el('a', {
'class': 'statusbar_action',
href: 'javascript: void(0)',
click: this.open_run_window,
},
'(Re)open run window (F6)'
'(Re)open run window (F7)'
),
this.options = el('div', 'options',
@@ -137,13 +156,21 @@ export class UI {
this.set_active_tab('logs')
}
if(e.key == 'F5'){
this.fullscreen_editor()
if(e.key == 'F4'){
this.set_active_tab('io_cache')
}
if(e.key == 'F6'){
exec('clear_io_cache')
}
if(e.key == 'F7'){
this.open_run_window()
}
if(e.key == 'F8'){
this.fullscreen_editor()
}
})
if(!FLAGS.embed_value_explorer) {
@@ -161,6 +188,7 @@ export class UI {
this.calltree = new CallTree(this, this.debugger.calltree)
this.logs = new Logs(this, this.debugger.logs)
this.io_cache = new IO_Cache(this, this.debugger.io_cache)
// TODO jump to another module
// TODO use exec
@@ -184,6 +212,7 @@ export class UI {
}
set_active_tab(tab_id, skip_focus = false) {
this.active_tab = tab_id
Object.values(this.tabs).forEach(el => el.classList.remove('active'))
this.tabs[tab_id].classList.add('active')
Object.values(this.debugger).forEach(el => el.style.display = 'none')
@@ -272,8 +301,15 @@ export class UI {
this.debugger_loading.style = 'display: none'
this.debugger_loaded.style = ''
this.calltree.render_calltree(state)
this.logs.render_logs(null, state.logs)
// render lazily
// TODO
//if(this.active_tab == 'io_cache') {
this.io_cache.render_io_cache(state.io_cache)
//}
}
render_problems(problems) {
@@ -327,14 +363,18 @@ export class UI {
['Focus console logs', 'F3'],
['Navigate console logs', '↑ ↓ or jk'],
['Leave console logs', 'F3 or Esc'],
['Jump to definition', 'F4', 'gd'],
['Focus IO cache', 'F4'],
['Leave IO cache', 'F4 or Esc'],
['Jump to definition', 'F5', 'gd'],
['Expand selection to eval expression', 'Ctrl-↓ or Ctrl-j'],
['Collapse selection', 'Ctrl-↑ or Ctrl-k'],
['Step into call', 'Ctrl-i', '\\i'],
['Step out of call', 'Ctrl-o', '\\o'],
['When in call tree view, jump to return statement', 'Enter'],
['When in call tree view, jump to function arguments', 'a'],
['Expand/collapse editor to fullscreen', 'F5'],
['Clear IO cache', 'F6'],
['(Re)open run window (F7)', 'F7'],
['Expand/collapse editor to fullscreen', 'F8'],
]
return el('dialog', 'help_dialog',
el('table', 'help',

View File

@@ -3,6 +3,7 @@
// TODO show Errors in red
// TODO fns as clickable links (jump to definition), both for header and for
// content
// TODO show constructor.name in header?
import {el, stringify, scrollIntoViewIfNeeded} from './domutils.js'

View File

@@ -1,5 +1,7 @@
import {set_record_call} from './runtime.js'
// TODO remove all console.log
const get_object_to_patch = (cxt, path) => {
let obj = cxt.window
for(let i = 0; i < path.length - 1; i++) {
@@ -25,6 +27,7 @@ const io_patch = (cxt, path, use_context = false) => {
// 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,
@@ -32,11 +35,7 @@ const io_patch = (cxt, path, use_context = false) => {
? cxt.io_cache.length
: cxt.io_cache_index
})
// sanity check
if(cxt.searched_location != null) {
throw new Error('illegal state')
}
*/
if(cxt.io_cache_is_replay_aborted) {
// Try to finish fast
@@ -70,7 +69,8 @@ const io_patch = (cxt, path, use_context = false) => {
? new original(...args)
: original.apply(this, args)
console.log('value', value)
// TODO remove
//console.log('value', value)
if(value instanceof cxt.window.Promise) {
// TODO use cxt.promise_then, not finally which calls
@@ -134,12 +134,12 @@ const io_patch = (cxt, path, use_context = false) => {
JSON.stringify(call.args) != JSON.stringify(args)
)
){
console.error('DISCARD cache', call)
//TODO remove console.error('DISCARD cache', call)
cxt.io_cache_is_replay_aborted = true
// Try to finish fast
throw new Error('io replay aborted')
} else {
console.log('cached call found', call)
// TODO remove console.log('cached call found', call)
const next_resolution = cxt.io_cache.find((e, i) =>
e.type == 'resolution' && i > cxt.io_cache_index
)
@@ -190,7 +190,7 @@ const io_patch = (cxt, path, use_context = false) => {
} else {
resolver(cxt.io_cache[resolution.index].value)
}
console.log('RESOLVE', cxt.io_cache_index, resolution.index)
// TODO remove console.log('RESOLVE', cxt.io_cache_index, resolution.index)
}
}