mirror of
https://github.com/leporello-js/leporello-js
synced 2026-01-13 13:04:30 -08:00
async calls WIP
This commit is contained in:
@@ -185,7 +185,8 @@ const do_external_imports_loaded = (
|
|||||||
const result = eval_modules(
|
const result = eval_modules(
|
||||||
state.parse_result.modules,
|
state.parse_result.modules,
|
||||||
state.parse_result.sorted,
|
state.parse_result.sorted,
|
||||||
external_imports
|
external_imports,
|
||||||
|
state.on_async_call,
|
||||||
)
|
)
|
||||||
const next = apply_eval_result(state, result)
|
const next = apply_eval_result(state, result)
|
||||||
|
|
||||||
@@ -208,6 +209,7 @@ const do_external_imports_loaded = (
|
|||||||
state.parse_result.modules,
|
state.parse_result.modules,
|
||||||
state.parse_result.sorted,
|
state.parse_result.sorted,
|
||||||
external_imports,
|
external_imports,
|
||||||
|
state.on_async_call,
|
||||||
{index: node.index, module: state.current_module},
|
{index: node.index, module: state.current_module},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -737,6 +739,10 @@ const move_cursor = (s, index) => {
|
|||||||
return do_move_cursor(state, index)
|
return do_move_cursor(state, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const on_async_call = (state, ...args) => {
|
||||||
|
console.log('on_async_call', state, args)
|
||||||
|
}
|
||||||
|
|
||||||
const load_dir = (state, dir) => {
|
const load_dir = (state, dir) => {
|
||||||
const collect_files = dir => dir.kind == 'file'
|
const collect_files = dir => dir.kind == 'file'
|
||||||
? [dir]
|
? [dir]
|
||||||
@@ -792,5 +798,6 @@ export const COMMANDS = {
|
|||||||
move_cursor,
|
move_cursor,
|
||||||
eval_selection,
|
eval_selection,
|
||||||
external_imports_loaded,
|
external_imports_loaded,
|
||||||
|
on_async_call,
|
||||||
calltree: calltree_commands,
|
calltree: calltree_commands,
|
||||||
}
|
}
|
||||||
|
|||||||
73
src/eval.js
73
src/eval.js
@@ -56,6 +56,16 @@ type Call = {
|
|||||||
type Node = ToplevelCall | Call
|
type Node = ToplevelCall | Call
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// TODO just export const Iframe_Function?
|
||||||
|
const make_function = globalThis.process != null
|
||||||
|
// Tests are run in Node.js, no iframe
|
||||||
|
? (...args) => new Function(...args)
|
||||||
|
// Browser context, run code in iframe
|
||||||
|
: (...args) => {
|
||||||
|
const fn_constructor = globalThis.run_code.contentWindow.Function
|
||||||
|
return new fn_constructor(...args)
|
||||||
|
}
|
||||||
|
|
||||||
const codegen_function_expr = (node, cxt, name) => {
|
const codegen_function_expr = (node, cxt, name) => {
|
||||||
const do_codegen = n => codegen(n, cxt)
|
const do_codegen = n => codegen(n, cxt)
|
||||||
|
|
||||||
@@ -235,7 +245,13 @@ const codegen = (node, cxt, parent) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const eval_modules = (modules, sorted, external_imports, location) => {
|
export const eval_modules = (
|
||||||
|
modules,
|
||||||
|
sorted,
|
||||||
|
external_imports,
|
||||||
|
on_async_call,
|
||||||
|
location
|
||||||
|
) => {
|
||||||
// TODO gensym __modules, __exports
|
// TODO gensym __modules, __exports
|
||||||
|
|
||||||
// TODO bug if module imported twice, once as external and as regular
|
// TODO bug if module imported twice, once as external and as regular
|
||||||
@@ -243,7 +259,7 @@ export const eval_modules = (modules, sorted, external_imports, location) => {
|
|||||||
const codestring =
|
const codestring =
|
||||||
`
|
`
|
||||||
let children, prev_children
|
let children, prev_children
|
||||||
// TODO use native array for stack?
|
// TODO use native array for stack for perf?
|
||||||
const stack = new Array()
|
const stack = new Array()
|
||||||
|
|
||||||
let call_counter = 0
|
let call_counter = 0
|
||||||
@@ -252,6 +268,9 @@ export const eval_modules = (modules, sorted, external_imports, location) => {
|
|||||||
let searched_location
|
let searched_location
|
||||||
let found_call
|
let found_call
|
||||||
|
|
||||||
|
let is_recording_async_calls
|
||||||
|
let is_toplevel_call = true
|
||||||
|
|
||||||
const set_record_call = () => {
|
const set_record_call = () => {
|
||||||
for(let i = 0; i < stack.length; i++) {
|
for(let i = 0; i < stack.length; i++) {
|
||||||
stack[i] = true
|
stack[i] = true
|
||||||
@@ -259,12 +278,14 @@ export const eval_modules = (modules, sorted, external_imports, location) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const expand_calltree_node = (node) => {
|
const expand_calltree_node = (node) => {
|
||||||
|
is_recording_async_calls = false
|
||||||
children = null
|
children = null
|
||||||
try {
|
try {
|
||||||
node.fn.apply(node.context, node.args)
|
node.fn.apply(node.context, node.args)
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
// do nothing. Exception was caught and recorded inside 'trace'
|
// do nothing. Exception was caught and recorded inside 'trace'
|
||||||
}
|
}
|
||||||
|
is_recording_async_calls = true
|
||||||
if(node.fn.__location != null) {
|
if(node.fn.__location != null) {
|
||||||
// fn is hosted, it created call, this time with children
|
// fn is hosted, it created call, this time with children
|
||||||
const result = children[0]
|
const result = children[0]
|
||||||
@@ -319,6 +340,9 @@ export const eval_modules = (modules, sorted, external_imports, location) => {
|
|||||||
|
|
||||||
let ok, value, error
|
let ok, value, error
|
||||||
|
|
||||||
|
const is_toplevel_call_copy = is_toplevel_call
|
||||||
|
is_toplevel_call = false
|
||||||
|
|
||||||
try {
|
try {
|
||||||
value = fn(...args)
|
value = fn(...args)
|
||||||
ok = true
|
ok = true
|
||||||
@@ -361,6 +385,12 @@ export const eval_modules = (modules, sorted, external_imports, location) => {
|
|||||||
children = []
|
children = []
|
||||||
}
|
}
|
||||||
children.push(call)
|
children.push(call)
|
||||||
|
|
||||||
|
is_toplevel_call = is_toplevel_call_copy
|
||||||
|
|
||||||
|
if(is_recording_async_calls && is_toplevel_call) {
|
||||||
|
on_async_call(children)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,7 +467,14 @@ export const eval_modules = (modules, sorted, external_imports, location) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const run = entrypoint => {
|
const run = entrypoint => {
|
||||||
const __modules = {...external_imports}
|
|
||||||
|
is_recording_async_calls = false
|
||||||
|
|
||||||
|
const __modules = {
|
||||||
|
/* external_imports passed as an argument to function generated with
|
||||||
|
* 'new Function' constructor */
|
||||||
|
...external_imports
|
||||||
|
}
|
||||||
let current_call
|
let current_call
|
||||||
|
|
||||||
`
|
`
|
||||||
@@ -468,6 +505,8 @@ export const eval_modules = (modules, sorted, external_imports, location) => {
|
|||||||
})()
|
})()
|
||||||
current_call.children = children
|
current_call.children = children
|
||||||
if(!__modules['${m}'].calls.ok) {
|
if(!__modules['${m}'].calls.ok) {
|
||||||
|
is_recording_async_calls = true
|
||||||
|
children = null
|
||||||
return __modules
|
return __modules
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
@@ -475,6 +514,8 @@ export const eval_modules = (modules, sorted, external_imports, location) => {
|
|||||||
.join('')
|
.join('')
|
||||||
+
|
+
|
||||||
`
|
`
|
||||||
|
is_recording_async_calls = true
|
||||||
|
children = null
|
||||||
return __modules
|
return __modules
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -485,12 +526,27 @@ export const eval_modules = (modules, sorted, external_imports, location) => {
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const actions = new Function('external_imports', codestring)(
|
const actions = make_function(
|
||||||
|
'external_imports',
|
||||||
|
'on_async_call',
|
||||||
|
codestring
|
||||||
|
)(
|
||||||
|
/* external_imports */
|
||||||
external_imports == null
|
external_imports == null
|
||||||
? null
|
? null
|
||||||
: map_object(external_imports, (name, {module}) =>
|
: map_object(external_imports, (name, {module}) =>
|
||||||
({exports: module, is_external: true})
|
({exports: module, is_external: true})
|
||||||
|
)
|
||||||
|
,
|
||||||
|
|
||||||
|
/* on_async_call */
|
||||||
|
calls => {
|
||||||
|
on_async_call(
|
||||||
|
calls.map(c =>
|
||||||
|
assign_code(modules, c)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
const calltree_actions = {
|
const calltree_actions = {
|
||||||
@@ -581,6 +637,9 @@ account
|
|||||||
|
|
||||||
// Workaround with statement forbidden in strict mode (imposed by ES6 modules)
|
// Workaround with statement forbidden in strict mode (imposed by ES6 modules)
|
||||||
// Also currently try/catch is not implemented TODO
|
// Also currently try/catch is not implemented TODO
|
||||||
|
|
||||||
|
|
||||||
|
// TODO also create in Iframe Context?
|
||||||
const eval_codestring = new Function('codestring', 'scope',
|
const eval_codestring = new Function('codestring', 'scope',
|
||||||
// Make a copy of `scope` to not mutate it with assignments
|
// Make a copy of `scope` to not mutate it with assignments
|
||||||
`
|
`
|
||||||
|
|||||||
@@ -46,7 +46,10 @@ export const init = (container) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
read_modules().then(initial_state => {
|
read_modules().then(initial_state => {
|
||||||
state = get_initial_state(initial_state)
|
state = get_initial_state({
|
||||||
|
...initial_state,
|
||||||
|
on_async_call: (...args) => exec('on_async_call', ...args)
|
||||||
|
})
|
||||||
// Expose state for debugging
|
// Expose state for debugging
|
||||||
globalThis.__state = state
|
globalThis.__state = state
|
||||||
ui = new UI(container, state)
|
ui = new UI(container, state)
|
||||||
|
|||||||
14
test/test.js
14
test/test.js
@@ -2227,4 +2227,18 @@ const y = x()`
|
|||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
test_only('async calls', () => {
|
||||||
|
const code = `
|
||||||
|
const fn = () => {
|
||||||
|
}
|
||||||
|
// Use Function constructor to exec impure code for testing
|
||||||
|
new Function('fn', 'globalThis.__run_async_call = fn')(fn)
|
||||||
|
`
|
||||||
|
const i = test_initial_state(code, {
|
||||||
|
on_async_call: (calls) => {console.log('test on async call', calls)}
|
||||||
|
})
|
||||||
|
globalThis.__run_async_call()
|
||||||
|
delete globalThis.__run_async_call
|
||||||
|
}),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -27,12 +27,15 @@ export const assert_code_error = (codestring, error) => {
|
|||||||
assert_equal(result.error, error)
|
assert_equal(result.error, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const test_initial_state = code => {
|
export const test_initial_state = (code, state) => {
|
||||||
return get_initial_state({
|
return get_initial_state(
|
||||||
|
{
|
||||||
|
...state,
|
||||||
files: typeof(code) == 'object' ? code : { '' : code},
|
files: typeof(code) == 'object' ? code : { '' : code},
|
||||||
entrypoint: '',
|
entrypoint: '',
|
||||||
current_module: '',
|
current_module: '',
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const stringify = val =>
|
export const stringify = val =>
|
||||||
|
|||||||
Reference in New Issue
Block a user