mirror of
https://github.com/leporello-js/leporello-js
synced 2026-01-13 21:14:28 -08:00
record constructor calls in calltree
This commit is contained in:
@@ -750,7 +750,7 @@ const select_return_value = state => {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
result_node = find_node(frame, n =>
|
result_node = find_node(frame, n =>
|
||||||
n.type == 'function_call'
|
(n.type == 'function_call' || n.type == 'new')
|
||||||
&& n.result != null
|
&& n.result != null
|
||||||
&& n.result.call.id == state.current_calltree_node.id
|
&& n.result.call.id == state.current_calltree_node.id
|
||||||
)
|
)
|
||||||
@@ -790,7 +790,7 @@ const select_arguments = (state, with_focus = true) => {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
const call = find_node(frame, n =>
|
const call = find_node(frame, n =>
|
||||||
n.type == 'function_call'
|
(n.type == 'function_call' || n.type == 'new')
|
||||||
&& n.result != null
|
&& n.result != null
|
||||||
&& n.result.call.id == state.current_calltree_node.id
|
&& n.result.call.id == state.current_calltree_node.id
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -112,8 +112,8 @@ export class CallTree {
|
|||||||
+ (n.fn.__location == null ? ' native' : '')
|
+ (n.fn.__location == null ? ' native' : '')
|
||||||
,
|
,
|
||||||
// TODO show `this` argument
|
// TODO show `this` argument
|
||||||
n.fn.name
|
(n.is_new ? 'new ' : ''),
|
||||||
,
|
n.fn.name,
|
||||||
'(' ,
|
'(' ,
|
||||||
...join(
|
...join(
|
||||||
n.args.map(
|
n.args.map(
|
||||||
|
|||||||
34
src/eval.js
34
src/eval.js
@@ -222,7 +222,8 @@ const codegen = (node, cxt, parent) => {
|
|||||||
} else if(node.type == 'spread'){
|
} else if(node.type == 'spread'){
|
||||||
return '...(' + do_codegen(node.expr) + ')'
|
return '...(' + do_codegen(node.expr) + ')'
|
||||||
} else if(node.type == 'new') {
|
} else if(node.type == 'new') {
|
||||||
return '(new (' + codegen(node.constructor) + ')(' + node.args.map(do_codegen).join(',') + '))'
|
const args = `[${node.args.children.map(do_codegen).join(',')}]`
|
||||||
|
return `trace_call(${do_codegen(node.constructor)}, null, ${args}, true)`
|
||||||
} else if(node.type == 'grouping'){
|
} else if(node.type == 'grouping'){
|
||||||
return '(' + do_codegen(node.expr) + ')'
|
return '(' + do_codegen(node.expr) + ')'
|
||||||
} else if(node.type == 'array_destructuring') {
|
} else if(node.type == 'array_destructuring') {
|
||||||
@@ -441,13 +442,20 @@ export const eval_modules = (
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
const trace_call = (fn, context, args) => {
|
const trace_call = (fn, context, args, is_new = false) => {
|
||||||
if(fn != null && fn.__location != null) {
|
if(fn != null && fn.__location != null && !is_new) {
|
||||||
|
// Call will be traced, because tracing code is already embedded inside
|
||||||
|
// fn
|
||||||
return fn(...args)
|
return fn(...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(typeof(fn) != 'function') {
|
if(typeof(fn) != 'function') {
|
||||||
return fn.apply(context, args)
|
// Raise error
|
||||||
|
if(is_new) {
|
||||||
|
return new fn(...args)
|
||||||
|
} else {
|
||||||
|
return fn.apply(context, args)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const children_copy = children
|
const children_copy = children
|
||||||
@@ -465,7 +473,11 @@ export const eval_modules = (
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if(!is_log) {
|
if(!is_log) {
|
||||||
value = fn.apply(context, args)
|
if(is_new) {
|
||||||
|
value = new fn(...args)
|
||||||
|
} else {
|
||||||
|
value = fn.apply(context, args)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
value = undefined
|
value = undefined
|
||||||
}
|
}
|
||||||
@@ -489,6 +501,7 @@ export const eval_modules = (
|
|||||||
args,
|
args,
|
||||||
context,
|
context,
|
||||||
is_log,
|
is_log,
|
||||||
|
is_new,
|
||||||
}
|
}
|
||||||
|
|
||||||
const should_record_call = stack.pop()
|
const should_record_call = stack.pop()
|
||||||
@@ -799,7 +812,7 @@ const do_eval_frame_expr = (node, scope, callsleft) => {
|
|||||||
{}
|
{}
|
||||||
)
|
)
|
||||||
return {ok, children, value, calls}
|
return {ok, children, value, calls}
|
||||||
} else if(node.type == 'function_call'){
|
} else if(node.type == 'function_call' || node.type == 'new'){
|
||||||
const {ok, children, calls} = eval_children(node, scope, callsleft)
|
const {ok, children, calls} = eval_children(node, scope, callsleft)
|
||||||
if(!ok) {
|
if(!ok) {
|
||||||
return {ok: false, children, calls}
|
return {ok: false, children, calls}
|
||||||
@@ -940,15 +953,6 @@ const do_eval_frame_expr = (node, scope, callsleft) => {
|
|||||||
return eval_binary_expr(node, scope, callsleft)
|
return eval_binary_expr(node, scope, callsleft)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if(node.type == 'new') {
|
|
||||||
const {ok, children, calls} = eval_children(node, scope, callsleft)
|
|
||||||
if(!ok) {
|
|
||||||
return {ok, children, calls}
|
|
||||||
} else {
|
|
||||||
const [constructor, ...args] = children
|
|
||||||
const value = new (constructor.result.value)(...args.map(a => a.result.value))
|
|
||||||
return {ok, children, value, calls}
|
|
||||||
}
|
|
||||||
} else if(node.type == 'grouping'){
|
} else if(node.type == 'grouping'){
|
||||||
const {ok, children, calls} = eval_children(node, scope, callsleft)
|
const {ok, children, calls} = eval_children(node, scope, callsleft)
|
||||||
if(!ok) {
|
if(!ok) {
|
||||||
|
|||||||
@@ -984,11 +984,20 @@ const new_expr = if_ok(
|
|||||||
array_element,
|
array_element,
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
({value, ...node}) => ({
|
({value, ...node}) => {
|
||||||
...node,
|
const {value: args, ..._call_args} = value[2]
|
||||||
type: 'new',
|
const call_args = {
|
||||||
children: [value[1], ...value[2].value],
|
..._call_args,
|
||||||
})
|
children: args,
|
||||||
|
not_evaluatable: args.length == 0,
|
||||||
|
type: 'call_args',
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
type: 'new',
|
||||||
|
children: [value[1], call_args],
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const primary = if_fail(
|
const primary = if_fail(
|
||||||
@@ -1384,7 +1393,7 @@ const update_children_not_rec = (node, children = node.children) => {
|
|||||||
expr: children[0]
|
expr: children[0]
|
||||||
}
|
}
|
||||||
} else if(node.type == 'new') {
|
} else if(node.type == 'new') {
|
||||||
return {...node, constructor: children[0], args: children.slice(1)}
|
return {...node, constructor: children[0], args: children[1]}
|
||||||
} else if(node.type == 'grouping') {
|
} else if(node.type == 'grouping') {
|
||||||
return {...node, expr: children[0]}
|
return {...node, expr: children[0]}
|
||||||
} else if(node.type == 'return') {
|
} else if(node.type == 'return') {
|
||||||
|
|||||||
49
test/test.js
49
test/test.js
@@ -423,6 +423,35 @@ export const tests = [
|
|||||||
`, 'test')
|
`, 'test')
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
test('new calls are recorded in calltree', () => {
|
||||||
|
const code = `
|
||||||
|
const make_class = new Function("return class { constructor(x) { x() } }")
|
||||||
|
const clazz = make_class()
|
||||||
|
const x = () => 1
|
||||||
|
new clazz(x)
|
||||||
|
`
|
||||||
|
const i = test_initial_state(code)
|
||||||
|
const find_call = COMMANDS.move_cursor(i, code.indexOf('1')).state
|
||||||
|
assert_equal(root_calltree_node(find_call).children.length, 3)
|
||||||
|
const x_call = root_calltree_node(find_call).children[2].children[0]
|
||||||
|
assert_equal(x_call.fn.name, 'x')
|
||||||
|
}),
|
||||||
|
|
||||||
|
test('new calls step into', () => {
|
||||||
|
const code = `new Set()`
|
||||||
|
const i = test_initial_state(code)
|
||||||
|
const into = COMMANDS.calltree.arrow_down(i)
|
||||||
|
assert_equal(into.state.current_calltree_node.fn.name, 'Set')
|
||||||
|
assert_equal(into.state.current_calltree_node.is_new, true)
|
||||||
|
}),
|
||||||
|
|
||||||
|
test('new call non-constructor', () => {
|
||||||
|
assert_code_error(
|
||||||
|
`const x = () => 1; new x()`,
|
||||||
|
'TypeError: fn is not a constructor'
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
test('method chaining', () => {
|
test('method chaining', () => {
|
||||||
assert_code_evals_to(
|
assert_code_evals_to(
|
||||||
`
|
`
|
||||||
@@ -1676,7 +1705,7 @@ const y = x()`
|
|||||||
const y = () => 1
|
const y = () => 1
|
||||||
const deep_error = x => {
|
const deep_error = x => {
|
||||||
if(x == 10) {
|
if(x == 10) {
|
||||||
throw new Error('deep_error')
|
throw 'deep_error'
|
||||||
} else {
|
} else {
|
||||||
y()
|
y()
|
||||||
deep_error(x + 1)
|
deep_error(x + 1)
|
||||||
@@ -1701,7 +1730,7 @@ const y = x()`
|
|||||||
|
|
||||||
assert_equal(depth(first), 10)
|
assert_equal(depth(first), 10)
|
||||||
assert_equal(first.ok, false)
|
assert_equal(first.ok, false)
|
||||||
assert_equal(first.error.message, 'deep_error')
|
assert_equal(first.error, 'deep_error')
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/* Test when node where error occured has subcalls */
|
/* Test when node where error occured has subcalls */
|
||||||
@@ -1959,6 +1988,14 @@ const y = x()`
|
|||||||
assert_equal(s3.selection_state.result.value, [1, 1, 1])
|
assert_equal(s3.selection_state.result.value, [1, 1, 1])
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
test('select_return_value new call', () => {
|
||||||
|
const code = `new String('1')`
|
||||||
|
const s1 = test_initial_state(code)
|
||||||
|
const s2 = COMMANDS.calltree.arrow_right(s1).state
|
||||||
|
const {state: s3, effects} = COMMANDS.calltree.select_return_value(s2)
|
||||||
|
assert_equal(s3.selection_state.result.value, '1')
|
||||||
|
}),
|
||||||
|
|
||||||
test('select_arguments not_expanded', () => {
|
test('select_arguments not_expanded', () => {
|
||||||
const code = `
|
const code = `
|
||||||
const x = (a) => { 1 }
|
const x = (a) => { 1 }
|
||||||
@@ -1989,6 +2026,14 @@ const y = x()`
|
|||||||
assert_equal(s3.effects, {type: 'set_focus'})
|
assert_equal(s3.effects, {type: 'set_focus'})
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
test('select_arguments new call', () => {
|
||||||
|
const code = `new String("1")`
|
||||||
|
const s1 = test_initial_state(code)
|
||||||
|
const s2 = COMMANDS.calltree.arrow_right(s1).state
|
||||||
|
const s3 = COMMANDS.calltree.select_arguments(s2).state
|
||||||
|
assert_equal(s3.selection_state.result, {ok: true, value: ["1"]})
|
||||||
|
}),
|
||||||
|
|
||||||
test('move_cursor arguments', () => {
|
test('move_cursor arguments', () => {
|
||||||
const code = `
|
const code = `
|
||||||
const x = (a, b) => { }
|
const x = (a, b) => { }
|
||||||
|
|||||||
Reference in New Issue
Block a user