find call with respect of cursor position inside branch

This commit is contained in:
Dmitry Vasilev
2023-08-02 06:00:08 +03:00
parent adbca074c2
commit 54d502396d
9 changed files with 528 additions and 86 deletions

View File

@@ -2,7 +2,7 @@ import {map_accum, map_find, map_object, stringify, findLast} from './utils.js'
import {is_eq, find_error_origin_node} from './ast_utils.js'
import {find_node, find_leaf, ancestry_inc} from './ast_utils.js'
import {color} from './color.js'
import {eval_frame, eval_expand_calltree_node} from './eval.js'
import {eval_frame, eval_expand_calltree_node, get_after_if_path} from './eval.js'
export const pp_calltree = tree => ({
id: tree.id,
@@ -91,27 +91,38 @@ export const get_calltree_node_by_loc = (state, index) => {
}
}
const get_selected_calltree_node_by_loc = (state, node) =>
const get_selected_calltree_node_by_loc = (
state,
node,
module = state.current_module
) =>
state.selected_calltree_node_by_loc
?.[state.current_module]
?.[module]
?.[
state.parse_result.modules[state.current_module] == node
state.parse_result.modules[module] == node
// identify toplevel by index `-1`, because function and toplevel can
// have the same index (in case when module starts with function_expr)
? -1
: node.index
]
const add_selected_calltree_node_by_loc = (state, loc, node_id) => {
const set_node_by_loc = (node_by_loc, loc, node_id) => {
return {...node_by_loc,
[loc.module]: {
...node_by_loc?.[loc.module],
[loc.index ?? -1]: node_id
}
}
}
const set_selected_calltree_node_by_loc = (state, loc, node_id) => {
return {
...state,
selected_calltree_node_by_loc:
{...state.selected_calltree_node_by_loc,
[loc.module]: {
...state.selected_calltree_node_by_loc?.[loc.module],
[loc.index ?? -1]: node_id
}
}
selected_calltree_node_by_loc: set_node_by_loc(
state.selected_calltree_node_by_loc,
loc,
node_id,
)
}
}
@@ -133,24 +144,38 @@ export const add_frame = (
current_calltree_node = active_calltree_node,
) => {
let with_frame
if(state.frames?.[active_calltree_node.id] == null) {
const frame = eval_frame(active_calltree_node, state.modules)
let frame
frame = state.frames?.[active_calltree_node.id]
if(frame == null) {
frame = eval_frame(active_calltree_node, state.modules)
const execution_paths = active_calltree_node.toplevel
? null
: get_execution_paths(frame)
const coloring = color(frame)
with_frame = {...state,
frames: {...state.frames,
[active_calltree_node.id]: {...frame, coloring}
[active_calltree_node.id]: {...frame, coloring, execution_paths}
}
}
} else {
with_frame = state
}
// TODO only add if it is not the same
const result = add_selected_calltree_node_by_loc(
with_frame,
calltree_node_loc(active_calltree_node),
active_calltree_node.id,
const loc = calltree_node_loc(active_calltree_node)
const with_colored_frames = {...with_frame,
colored_frames: set_node_by_loc(
with_frame.colored_frames,
loc,
active_calltree_node.id,
)
}
return set_active_calltree_node(
with_colored_frames,
active_calltree_node,
current_calltree_node
)
return set_active_calltree_node(result, active_calltree_node, current_calltree_node)
}
const replace_calltree_node = (root, node, replacement) => {
@@ -269,7 +294,13 @@ const jump_calltree_node = (_state, _current_calltree_node) => {
// of body?
: set_location(next, loc)
return {...with_location,
const with_selected_calltree_node = set_selected_calltree_node_by_loc(
with_location,
calltree_node_loc(active_calltree_node),
with_location.active_calltree_node.id,
)
return {...with_selected_calltree_node,
value_explorer: next.current_calltree_node.toplevel
? null
: {
@@ -537,20 +568,88 @@ export const initial_calltree_node = state => {
export const default_expand_path = state => initial_calltree_node(state).state
export const get_execution_paths = frame => {
/*
- depth-first search tree. if 'result == null', then stop. Do not descend
into function_expr
- look for 'if' and ternary
- Get executed branch (branch.result.ok != null). There may be no executed
branch if cond executed with error
- for 'if' statement we also add 'get_after_if_path(if_node.index)'
*/
const do_get = (node, next_node) => {
if(node.type == 'function_expr' || node.result == null) {
return []
}
let after_if, branch
if(node.type == 'if' || node.type == 'ternary') {
const [cond, ...branches] = node.children
branch = branches.find(b => b.result != null)
}
if(node.type == 'if' && next_node != null && next_node.result != null) {
after_if = get_after_if_path(node)
}
const paths = [branch?.index, after_if].filter(i => i != null)
const children = node.children ?? []
const child_paths = children
.map((c, i) => do_get(c, children[i + 1]))
.flat()
return [...paths, ...child_paths]
}
const [args, body] = frame.children
return new Set(do_get(body))
}
const find_execution_path = (node, index) => {
if(node.children == null) {
return []
}
const child = node.children.find(c =>
c.index <= index && c.index + c.length > index
)
if(child == null) {
return []
}
const prev_ifs = node
.children
.filter(c =>
c.index < child.index && c.type == 'if'
)
.map(c => get_after_if_path(c))
const child_path = find_execution_path(child, index)
// TODO other conditionals, like &&, ||, ??, ?., ?.[]
if(node.type == 'if' || node.type == 'ternary') {
const [cond, left, right] = node.children
if(child == left || child == right) {
return [...prev_ifs, child.index, ...child_path]
}
}
return [...prev_ifs, ...child_path]
}
export const find_call_node = (state, index) => {
const module = state.parse_result.modules[state.current_module]
if(module == null) {
// Module is not executed
return null
return {node: null, path: null}
}
let node
let node, path
if(index < module.index || index >= module.index + module.length) {
// index is outside of module, it can happen because of whitespace and
// comments in the beginning and the end
node = module
path = null
} else {
const leaf = find_leaf(module, index)
const anc = ancestry_inc(leaf, module)
@@ -558,44 +657,19 @@ export const find_call_node = (state, index) => {
node = fn == null
? module
: fn
path = find_execution_path(node, index)
}
return node
return {node, path}
}
export const find_call = (state, index) => {
const node = find_call_node(state, index)
const {node, path} = find_call_node(state, index)
if(node == null) {
return state
}
if(state.active_calltree_node != null && is_eq(node, state.active_calltree_node.code)) {
return state
}
const selected_ct_node_id = get_selected_calltree_node_by_loc(state, node)
/*
TODO remove because it interferes with find_call deferred calls
if(selected_ct_node_id === null) {
// strict compare (===) with null, to check if we put null earlier to
// designate that fn is not reachable
return set_active_calltree_node(state, null)
}
*/
if(selected_ct_node_id != null) {
const ct_node = find_node(
state.calltree,
n => n.id == selected_ct_node_id
)
if(ct_node == null) {
throw new Error('illegal state')
}
return set_active_calltree_node(state, ct_node, ct_node)
}
if(node == state.parse_result.modules[root_calltree_module(state)]) {
const toplevel = root_calltree_node(state)
return add_frame(
@@ -610,19 +684,42 @@ export const find_call = (state, index) => {
return state
}
const selected_ct_node_id = get_selected_calltree_node_by_loc(state, node)
const execution_paths = selected_ct_node_id == null
? null
: state.frames[selected_ct_node_id].execution_paths
const ct_node_id = get_calltree_node_by_loc(state, node.index)
if(ct_node_id == null) {
return set_active_calltree_node(state, null)
}
const path_ct_node_id = [node.index, ...path]
.map(path_elem => {
const is_selected_node_hits_path =
selected_ct_node_id != null
&&
(execution_paths.has(path_elem) || node.index == path_elem)
if(is_selected_node_hits_path) {
return selected_ct_node_id
}
// If there is no node selected for this fn, or it did not hit the path
// when executed, try to find node calltree_node_by_loc
return get_calltree_node_by_loc(state, path_elem)
})
.findLast(node_id => node_id != null)
const ct_node = find_node(
state.calltree,
n => n.id == ct_node_id
n => n.id == path_ct_node_id
)
if(ct_node == null) {
throw new Error('illegal state')
}
return add_frame(
expand_path(state, ct_node),
ct_node,

View File

@@ -11,9 +11,9 @@ import {
root_calltree_node, root_calltree_module, make_calltree,
get_deferred_calls,
calltree_commands,
add_frame, calltree_node_loc, get_calltree_node_by_loc, expand_path,
add_frame, calltree_node_loc, expand_path,
initial_calltree_node, default_expand_path, toggle_expanded, active_frame,
find_call, find_call_node, set_active_calltree_node,
find_call, set_active_calltree_node,
set_cursor_position, current_cursor_position, set_location,
} from './calltree.js'
@@ -96,6 +96,7 @@ const run_code = (s, dirty_files) => {
active_calltree_node: null,
calltree_node_is_expanded: null,
frames: null,
colored_frames: null,
calltree_node_by_loc: null,
selected_calltree_node_by_loc: null,
selection_state: null,
@@ -223,14 +224,11 @@ const eval_modules_finished = (state, prev_state, result) => {
const {node, state: next2} = initial_calltree_node(next)
result_state = set_active_calltree_node(next2, null, node)
} else {
result_state = add_frame(
default_expand_path(
expand_path(
next,
next.active_calltree_node
)
),
next.active_calltree_node,
result_state = default_expand_path(
expand_path(
next,
next.active_calltree_node
)
)
}

View File

@@ -209,8 +209,7 @@ export const color = frame => {
export const color_file = (state, file) =>
Object
.values(state.selected_calltree_node_by_loc?.[file] ?? {})
// node_id == null means it is unreachable, so do not color
.values(state.colored_frames?.[file] ?? {})
.filter(node_id => node_id != null)
.map(node_id => state.frames[node_id].coloring)
.flat()

View File

@@ -52,6 +52,7 @@ const normalize_events = (ace_editor, {
})
ace_editor.on('changeSelection', (...args) => {
// TODO debounce changeSelection?
if(is_change_selection_supressed()) {
return
}

2
src/effects.js vendored
View File

@@ -249,7 +249,7 @@ export const apply_side_effects = (prev, next, command, ui) => {
ui.calltree.render_select_node(prev, next)
}
if(prev.selected_calltree_node_by_loc != next.selected_calltree_node_by_loc) {
if(prev.colored_frames != next.colored_frames) {
render_coloring(ui, next)
}

View File

@@ -72,11 +72,13 @@ const codegen_function_expr = (node, node_cxt) => {
? `(${args}) => `
: `function(${args})`
// TODO gensym __obj, __fn, __call_id
const prolog = '{const __call_id = __cxt.call_counter;'
const call = (node.is_async ? 'async ' : '') + decl + (
// TODO gensym __obj, __fn
(node.body.type == 'do')
? '{ let __obj, __fn; ' + do_codegen(node.body) + '}'
: '{ let __obj, __fn; return ' + do_codegen(node.body) + '}'
? prolog + do_codegen(node.body) + '}'
: prolog + 'return ' + do_codegen(node.body) + '}'
)
const argscount = node
@@ -135,6 +137,11 @@ ${JSON.stringify(errormessage)})`
}
// Note that we use 'node.index + 1' as index, which
// does not correspond to any ast node, we just use it as a convenient
// marker
export const get_after_if_path = node => node.index + 1
const codegen = (node, node_cxt, parent) => {
const do_codegen = (n, parent) => codegen(n, node_cxt, parent)
@@ -161,11 +168,20 @@ const codegen = (node, node_cxt, parent) => {
} else if(node.type == 'throw') {
return 'throw ' + do_codegen(node.expr) + ';'
} else if(node.type == 'if') {
const left = 'if(' + do_codegen(node.cond) + '){' +
do_codegen(node.branches[0]) + ' } '
return node.branches[1] == null
const codegen_branch = branch =>
`{ __save_ct_node_for_path(__cxt, __calltree_node_by_loc, ${branch.index}, __call_id);`
+ do_codegen(branch)
+ '}'
const left = 'if(' + do_codegen(node.cond) + ')'
+ codegen_branch(node.branches[0])
const result = node.branches[1] == null
? left
: left + ' else { ' + do_codegen(node.branches[1]) + ' }'
: left + ' else ' + codegen_branch(node.branches[1])
// add path also for point after if statement, in case there was a return
// inside if statement.
return result +
`__save_ct_node_for_path(__cxt, __calltree_node_by_loc, `
+ `${get_after_if_path(node)}, __call_id);`
} else if(node.type == 'array_literal'){
return '[' + node.elements.map(c => do_codegen(c)).join(', ') + ']'
} else if(node.type == 'object_literal'){
@@ -189,13 +205,18 @@ const codegen = (node, node_cxt, parent) => {
} else if(node.type == 'function_expr'){
return codegen_function_expr(node, node_cxt)
} else if(node.type == 'ternary'){
const branches = node.branches.map(branch =>
`(__save_ct_node_for_path(__cxt, __calltree_node_by_loc, ${branch.index}, __call_id), `
+ do_codegen(branch)
+ ')'
)
return ''
+ '('
+ do_codegen(node.cond)
+ ')\n? '
+ do_codegen(node.branches[0])
+ branches[0]
+'\n: '
+ do_codegen(node.branches[1])
+ branches[1]
} else if(node.type == 'const'){
const res = 'const ' + do_codegen(node.name_node) + ' = ' + do_codegen(node.expr, node) + ';'
if(node.name_node.type == 'identifier' && node.expr.type == 'function_call') {
@@ -297,7 +318,7 @@ export const eval_modules = (
io_trace,
location
) => {
// TODO gensym __cxt, __trace, __trace_call
// TODO gensym __cxt, __trace, __trace_call, __calltree_node_by_loc, __do_await
// TODO bug if module imported twice, once as external and as regular
@@ -310,11 +331,21 @@ export const eval_modules = (
const module_fns = parse_result.sorted.map(module => (
{
module,
// TODO refactor, instead of multiple args prefixed with '__', pass
// single arg called `runtime`
fn: new Function(
'__cxt',
'__calltree_node_by_loc',
'__trace',
'__trace_call',
'__do_await',
'__save_ct_node_for_path',
/* Add dummy __call_id for toplevel. It does not make any sence
* (toplevel is executed only once unlike function), we only add it
* because we dont want to codegen differently for if statements in
* toplevel and if statements within functions*/
'const __call_id = "SOMETHING_WRONG_HAPPENED";' +
codegen(parse_result.modules[module], {module})
)
}
@@ -354,7 +385,7 @@ export const eval_modules = (
logs: result.logs,
eval_cxt: result.eval_cxt,
calltree,
calltree_node_by_loc: result.eval_cxt.calltree_node_by_loc,
calltree_node_by_loc: result.calltree_node_by_loc,
io_trace: result.eval_cxt.io_trace,
}
}

View File

@@ -246,6 +246,7 @@ export const with_code_execution = (action, state = get_state()) => {
*/
if(state.eval_cxt != null) {
state.eval_cxt.is_recording_deferred_calls = false
state.eval_cxt.skip_save_ct_node_for_path = true
}
try {
@@ -253,6 +254,7 @@ export const with_code_execution = (action, state = get_state()) => {
} finally {
if(state.eval_cxt != null) {
state.eval_cxt.is_recording_deferred_calls = true
state.eval_cxt.skip_save_ct_node_for_path = false
}
}
}

View File

@@ -38,20 +38,24 @@ const make_promise_with_rejector = cxt => {
const do_run = function*(module_fns, cxt, io_trace){
let calltree
const calltree_node_by_loc = new Map(
module_fns.map(({module}) => [module, new Map()])
)
const [replay_aborted_promise, io_trace_abort_replay] =
make_promise_with_rejector(cxt)
cxt = (io_trace == null || io_trace.length == 0)
// TODO group all io_trace_ properties to single object?
? {...cxt,
calltree_node_by_loc,
logs: [],
calltree_node_by_loc: new Map(),
io_trace_is_recording: true,
io_trace: [],
}
: {...cxt,
calltree_node_by_loc,
logs: [],
calltree_node_by_loc: new Map(),
io_trace_is_recording: false,
io_trace,
io_trace_is_replay_aborted: false,
@@ -79,7 +83,14 @@ const do_run = function*(module_fns, cxt, io_trace){
try {
cxt.modules[module] = {}
const result = fn(cxt, __trace, __trace_call, __do_await)
const result = fn(
cxt,
calltree_node_by_loc.get(module),
__trace,
__trace_call,
__do_await,
__save_ct_node_for_path,
)
if(result instanceof cxt.window.Promise) {
yield cxt.window.Promise.race([replay_aborted_promise, result])
} else {
@@ -108,6 +119,7 @@ const do_run = function*(module_fns, cxt, io_trace){
calltree,
logs: _logs,
eval_cxt: cxt,
calltree_node_by_loc,
}
}
@@ -244,7 +256,7 @@ const __trace = (cxt, fn, name, argscount, __location, get_closure) => {
const call_id = ++cxt.call_counter
// populate calltree_node_by_loc only for entrypoint module
if(cxt.is_entrypoint) {
if(cxt.is_entrypoint && !cxt.skip_save_ct_node_for_path) {
let nodes_of_module = cxt.calltree_node_by_loc.get(__location.module)
if(nodes_of_module == null) {
nodes_of_module = new Map()
@@ -392,7 +404,6 @@ const __trace_call = (cxt, fn, context, args, errormessage, is_new = false) => {
}
if(is_log) {
// TODO do not collect logs on find_call?
cxt.logs.push(call)
}
@@ -412,3 +423,17 @@ const __trace_call = (cxt, fn, context, args, errormessage, is_new = false) => {
}
}
const __save_ct_node_for_path = (cxt, __calltree_node_by_loc, index, __call_id) => {
if(!cxt.is_entrypoint) {
return
}
if(cxt.skip_save_ct_node_for_path) {
return
}
if(__calltree_node_by_loc.get(index) == null) {
__calltree_node_by_loc.set(index, __call_id)
set_record_call(cxt)
}
}

View File

@@ -9,6 +9,7 @@ import {
pp_calltree,
get_deferred_calls,
current_cursor_position,
get_execution_paths,
} from '../src/calltree.js'
import {color_file} from '../src/color.js'
import {
@@ -1802,14 +1803,10 @@ const y = x()`
countdown(10)
`)
const first = root_calltree_node(s).children[0]
assert_equal(first.children[0].children, undefined)
assert_equal(first.children[0].has_more_children, true)
assert_equal(first.value, 10)
const s2 = COMMANDS.calltree.click(s, first.id)
const first2 = root_calltree_node(s2).children[0]
assert_equal(first2.children[0].value, 9)
assert_equal(first2.children[0].children, undefined)
assert_equal(first2.children[0].has_more_children, true)
assert_equal(first2.code, first2.children[0].code)
}),
@@ -2544,6 +2541,279 @@ const y = x()`
assert_equal(root_calltree_node(s2).module, '')
}),
test('find branch initial', () => {
const code = `
function x(cond) {
if(cond) {
return true
} else {
return false
}
}
x(true)
x(false)
`
const i = test_initial_state(code, code.indexOf('return false'))
assert_equal(i.value_explorer.result.value, false)
}),
test('find branch empty branch', () => {
const code = `
function x(cond) {
if(cond) {
/* label */
}
}
x(false)
x(true)
`
const i = test_initial_state(code, code.indexOf('label'))
assert_equal(i.active_calltree_node.args[0], true)
}),
test('find branch move_cursor', () => {
const code = `
function x(cond) {
if(cond) {
return true
} else {
return false
}
}
x(true)
x(false)
`
const i = test_initial_state(code)
const moved = COMMANDS.move_cursor(i, code.indexOf('return false'))
assert_equal(moved.value_explorer.result.value, false)
assert_equal(
i.colored_frames != moved.colored_frames,
true
)
}),
test('find branch ternary', () => {
const code = `
function x(cond) {
return cond ? true : false
}
x(true)
x(false)
`
const i = test_initial_state(code, code.indexOf('false'))
assert_equal(i.value_explorer.result.value, false)
}),
test('find branch move cursor within fn', () => {
const code = `
function x(cond) {
if(cond) {
return true
} else {
return false
}
}
x(true)
x(false)
`
const i = test_initial_state(code)
const s1 = COMMANDS.move_cursor(i, code.indexOf('return false'))
const s2 = COMMANDS.move_cursor(s1, code.indexOf('return true'))
assert_equal(s2.value_explorer.result.value, true)
assert_equal(
s1.colored_frames != s2.colored_frames,
true
)
}),
test('find branch fibonacci', () => {
const code = `
function fib(n) {
if(n == 0 || n == 1) {
return n
} else {
return fib(n - 1) + fib(n - 2)
}
}
fib(6)
`
const i = test_initial_state(code)
const moved = COMMANDS.move_cursor(i, code.indexOf('return n'))
assert_equal(moved.value_explorer.result.value, 1)
}),
test('find branch after if with return', () => {
const code = `
function x(cond) {
if(cond) {
return true
}
1
}
x(true)
x(false)
`
const i = test_initial_state(code, code.indexOf('1'))
assert_equal(i.value_explorer.result.value, 1)
}),
test('find branch after if with return complex', () => {
const code = `
function x(a, b) {
if(a) {
return true
}
if(a) {
return true
}
if(b) {
return true
} else {
if(false) {
return null
}
1
}
}
x(true)
x(false, true)
x(false, false)
`
const i = test_initial_state(code, code.indexOf('1'))
assert_equal(i.value_explorer.result.value, 1)
assert_equal(i.active_calltree_node.args, [false, false])
}),
test('find branch get_execution_paths', () => {
const code = `
function x() {
if(true) {/*1*/
}
if(false) {
} else {/*2*/
if(true) {/*3*/
true ? 4 : 5
}
return null
}
// not executed
if(true) {
}
// not executed
true ? 6 : 7
}
x()
`
const i = test_initial_state(code, code.indexOf('if'))
assert_equal(
[...get_execution_paths(active_frame(i))].toSorted((a,b) => a - b),
[
code.indexOf('if(true)') + 1,
code.indexOf('/*1*/') - 1,
code.indexOf('/*2*/') - 1,
code.indexOf('if(true) {/*3*/') + 1,
code.indexOf('/*3*/') - 1,
code.indexOf('4'),
]
)
}),
test('find branch get_execution_paths consice body', () => {
const code = `
const x = () => true ? 1 : 2
x()
`
const i = test_initial_state(code, code.indexOf('true'))
assert_equal(
get_execution_paths(active_frame(i)),
[code.indexOf('1')],
)
}),
test('find branch get_execution_paths nested fn', () => {
const code = `
function x() {
function y() {
true ? 1 : 2
}
}
x()
`
const i = test_initial_state(code, code.indexOf('{'))
assert_equal(
get_execution_paths(active_frame(i)),
[],
)
}),
test('find branch jump_calltree_node', () => {
const code = `
function test(x) {
if(x > 0) {
'label'
}
}
test(1)
test(2)
`
const i = test_initial_state(code, code.indexOf('label'))
assert_equal(i.active_calltree_node.args[0], 1)
// select second call
const second = COMMANDS.calltree.click(i, root_calltree_node(i).children[1].id)
assert_equal(second.active_calltree_node.args[0], 2)
}),
test('find branch preserve selected calltree node when moving inside fn', () => {
const code = `
function x(cond) {
if(cond) {
true
} else {
false
}
'finish'
}
x(true)
x(false)
`
const i = test_initial_state(code)
const first_call_id = root_calltree_node(i).children[0].id
// explicitly select first call
const selected = COMMANDS.calltree.click(i, first_call_id)
// implicitly select second call by moving cursor
const moved = COMMANDS.move_cursor(selected, code.indexOf('false'))
const finish = COMMANDS.move_cursor(moved, code.indexOf('finish'))
assert_equal(finish.active_calltree_node.id, first_call_id)
}),
test('find branch select calltree node from logs', () => {
const code = `
function f(x) {
if(x > 1) {
console.log(x)
} else {
console.log(x)
}
}
f(5)
f(10)
`
const i = test_initial_state(code)
const log_selected = COMMANDS.calltree.navigate_logs_position(i, 1)
const moved = COMMANDS.move_cursor(
log_selected,
code.indexOf('console.log')
)
assert_equal(moved.active_calltree_node.args, [10])
}),
test('stale id in frame function_call.result.calls bug', () => {
const code = `
const x = () => {/*x*/
@@ -2846,6 +3116,25 @@ const y = x()`
expanded.modules[''].fn(10)
}),
test('deferred_calls find call bug', () => {
const code = `
export const fn = () => 1
`
const {state: i, on_deferred_call} = test_deferred_calls_state(code)
const moved = COMMANDS.move_cursor(i, code.indexOf('1'))
assert_equal(moved.active_calltree_node, null)
// Make deferred call
moved.modules[''].fn(10)
const after_call = on_deferred_call(moved)
const moved2 = COMMANDS.move_cursor(after_call, code.indexOf('1'))
assert_equal(moved2.active_calltree_node.value, 1)
}),
test('async/await await non promise', async () => {
await assert_code_evals_to_async(
`