fix error origin

This commit is contained in:
Dmitry Vasilev
2023-10-26 12:25:35 +08:00
parent 9a95c054a3
commit e8d9326d5f
4 changed files with 44 additions and 48 deletions

View File

@@ -145,33 +145,27 @@ export const find_node = (node, pred) => {
) )
} }
// TODO refactor, at eval.js we have explicit information if node is error
// origin, without guessing. See also color.js
// TODO result is ok, but value is rejected promise // TODO result is ok, but value is rejected promise
// TODO check if return result is null and throw early export const find_error_origin_node = (node, is_root = true) => {
export const find_error_origin_node = node => if(node.result == null) {
find_node( return null
// TODO do not go inside function_expr }
node, if(node.type == 'function_expr' && !is_root) {
n => n.result != null && !n.result.ok && ( return null
n.result.error != null }
|| if(node.result.is_error_origin) {
// node has no error, but its children also have no error, so this node return node
// is error origin }
n.children.find(c => find_error_origin_node(c) != null) == null if(node.children == null) {
&& return null
( }
// In case if throw null or throw undefined return node
n.type == 'throw' .children
|| .reduce(
// await can also throw null (result, c) => result ?? find_error_origin_node(c, false),
n.type == 'unary' && n.operator == 'await' null
// or function call throwing null or undefined
||
n.type == 'function_call'
)
) )
) }
/* Maps tree nodes, discarding mapped children, so maps only node contents, not /* Maps tree nodes, discarding mapped children, so maps only node contents, not
* allowing to modify structure */ * allowing to modify structure */

View File

@@ -20,7 +20,7 @@ const is_result_eq = (a,b) => a.result == null
? b.result == null ? b.result == null
: b.result != null : b.result != null
&& a.result.ok == b.result.ok && a.result.ok == b.result.ok
&& a.result.error_origin == b.result.error_origin && !!a.result.is_error_origin == !!b.result.is_error_origin
const node_to_color = node => ({ const node_to_color = node => ({
index: node.index, index: node.index,
@@ -31,11 +31,7 @@ const node_to_color = node => ({
? null ? null
: node.result.ok : node.result.ok
? {ok: true} ? {ok: true}
// node.result.error may be null, for example if throw null : {ok: false, is_error_origin: !!node.result.is_error_origin}
// See find_error_origin_node
: node.result.error == null
? {ok: false, error_origin: false}
: {ok: false, error_origin: true}
}) })
const is_short_circuit = node => const is_short_circuit = node =>
@@ -175,7 +171,7 @@ const do_color = (node, is_root = false) => {
return [node_to_color(node)] return [node_to_color(node)]
} }
if(node.result?.error != null) { if(node.result?.is_error_origin) {
const color = node_to_color(node) const color = node_to_color(node)
const exprs = collect_function_exprs(node) const exprs = collect_function_exprs(node)
if(exprs.length == 0) { if(exprs.length == 0) {
@@ -208,7 +204,7 @@ const do_color = (node, is_root = false) => {
const result = color_children(node, is_root) const result = color_children(node, is_root)
return node.result != null && !node.result.ok return node.result != null && !node.result.ok
? result.map(c => c.result == null ? result.map(c => c.result == null
? {...c, result: {ok: false, error_origin: false}} ? {...c, result: {ok: false, is_error_origin: false}}
: c : c
) )
: result : result
@@ -222,7 +218,7 @@ export const color = frame => {
c.result != null c.result != null
&& &&
// Parts that were not error origins // Parts that were not error origins
(c.result.ok || c.result.error_origin) (c.result.ok || c.result.is_error_origin)
) )
// Sanity-check result // Sanity-check result

View File

@@ -452,7 +452,7 @@ export const eval_codestring = (codestring, scope) =>
try { try {
return {ok: true, value: eval('with({...scope}){' + codestring + '}')} return {ok: true, value: eval('with({...scope}){' + codestring + '}')}
} catch(error) { } catch(error) {
return {ok: false, error} return {ok: false, error, is_error_origin: true}
} }
` `
))(codestring, scope) ))(codestring, scope)
@@ -481,7 +481,7 @@ const get_args_scope = (fn_node, args, closure) => {
if(!ok) { if(!ok) {
// TODO show exact destructuring error // TODO show exact destructuring error
return {ok, error} return {ok, error, is_error_origin: true}
} else { } else {
return { return {
ok, ok,
@@ -534,6 +534,7 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
children: result.children, children: result.children,
calls: result.calls, calls: result.calls,
error: new TypeError(child.string + ' is not iterable'), error: new TypeError(child.string + ' is not iterable'),
is_error_origin: true,
} }
} }
} else if([ } else if([
@@ -598,6 +599,7 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
return { return {
ok: false, ok: false,
error: context.calltree_node.error, error: context.calltree_node.error,
is_error_origin: true,
children, children,
calls, calls,
} }
@@ -611,6 +613,7 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
call: c, call: c,
value: c.value, value: c.value,
error: c.error, error: c.error,
is_error_origin: !c.ok,
children, children,
calls: calls.slice(1) calls: calls.slice(1)
} }
@@ -687,7 +690,7 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
return {ok: false, children, calls} return {ok: false, children, calls}
} else { } else {
const expr = children[0] const expr = children[0]
let ok, value, error let ok, value, error, is_error_origin
if(node.operator == '!') { if(node.operator == '!') {
ok = true ok = true
value = !expr.result.value value = !expr.result.value
@@ -707,6 +710,7 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
ok = status.ok ok = status.ok
error = status.error error = status.error
value = status.value value = status.value
is_error_origin = !ok
} }
} else { } else {
ok = true ok = true
@@ -715,7 +719,7 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
} else { } else {
throw new Error('unknown op') throw new Error('unknown op')
} }
return {ok, children, calls, value, error} return {ok, children, calls, value, error, is_error_origin}
} }
} else if(node.type == 'binary' && !['&&', '||', '??'].includes(node.operator)){ } else if(node.type == 'binary' && !['&&', '||', '??'].includes(node.operator)){
@@ -783,7 +787,7 @@ const eval_children = (node, scope, calls, context) => {
} }
const eval_frame_expr = (node, scope, callsleft, context) => { const eval_frame_expr = (node, scope, callsleft, context) => {
const {ok, error, value, call, children, calls} const {ok, error, is_error_origin, value, call, children, calls}
= do_eval_frame_expr(node, scope, callsleft, context) = do_eval_frame_expr(node, scope, callsleft, context)
if(callsleft != null && calls == null) { if(callsleft != null && calls == null) {
// TODO remove it, just for debug // TODO remove it, just for debug
@@ -795,7 +799,7 @@ const eval_frame_expr = (node, scope, callsleft, context) => {
...node, ...node,
children, children,
// Add `call` for step_into // Add `call` for step_into
result: {ok, error, value, call} result: {ok, error, value, call, is_error_origin}
}, },
calls, calls,
} }
@@ -974,7 +978,7 @@ const eval_statement = (s, scope, calls, context) => {
return { return {
ok: false, ok: false,
// TODO assign error to node where destructuring failed, not to every node // TODO assign error to node where destructuring failed, not to every node
node: {...s_evaled, result: {ok, error}}, node: {...s_evaled, result: {ok, error, is_error_origin: true}},
scope, scope,
calls, calls,
} }
@@ -1135,6 +1139,7 @@ const eval_statement = (s, scope, calls, context) => {
children: [node], children: [node],
result: { result: {
ok: false, ok: false,
is_error_origin: node.result.ok,
error: node.result.ok ? node.result.value : null, error: node.result.ok ? node.result.value : null,
} }
}, },
@@ -1188,7 +1193,8 @@ export const eval_frame = (calltree_node, modules) => {
a => ({...a, a => ({...a,
result: { result: {
ok: args_scope_result.ok, ok: args_scope_result.ok,
error: args_scope_result.ok ? null : args_scope_result.error, error: args_scope_result.ok ? null : args_scope_result.error,
is_error_origin: !args_scope_result.ok,
value: !args_scope_result.ok ? null : args_scope_result.value[a.value], value: !args_scope_result.ok ? null : args_scope_result.value[a.value],
} }
}) })

View File

@@ -1504,7 +1504,7 @@ export const tests = [
{ {
index: code.indexOf('x()'), index: code.indexOf('x()'),
length: 'x()'.length, length: 'x()'.length,
result: { ok: false, error_origin: true } result: { ok: false, is_error_origin: true }
} }
] ]
) )
@@ -1527,12 +1527,12 @@ export const tests = [
{ {
index: code.indexOf('throw'), index: code.indexOf('throw'),
length: 'throw new Error()'.length, length: 'throw new Error()'.length,
result: { ok: false, error_origin: true } result: { ok: false, is_error_origin: true }
}, },
{ {
index: code.indexOf('x()'), index: code.indexOf('x()'),
length: "x()".length, length: "x()".length,
result: { ok: false, error_origin: true } result: { ok: false, is_error_origin: true }
} }
] ]
) )
@@ -1544,7 +1544,7 @@ export const tests = [
// Color only index access, not grouping braces // Color only index access, not grouping braces
assert_equal( assert_equal(
color_file(initial, ''), color_file(initial, ''),
[ { index: 1, length: 7, result: { ok: false, error_origin: true } } ], [ { index: 1, length: 7, result: { ok: false, is_error_origin: true } } ],
) )
}), }),
@@ -1603,7 +1603,7 @@ export const tests = [
{ {
index: 0, index: 0,
length: code.length, length: code.length,
result: { ok: false, error_origin: true } result: { ok: false, is_error_origin: true }
} }
] ]
) )
@@ -1667,7 +1667,7 @@ const y = x()`
const i = test_initial_state(code) const i = test_initial_state(code)
const coloring = color_file(i, '') const coloring = color_file(i, '')
const result = {ok: false, error_origin: true} const result = {ok: false, is_error_origin: true}
assert_equal( assert_equal(
coloring, coloring,
[ [