refactor globals

This commit is contained in:
Dmitry Vasilev
2023-06-15 23:55:06 +03:00
parent fdbe01249d
commit 2f5db0452c
7 changed files with 75 additions and 40 deletions

View File

@@ -77,7 +77,7 @@ const run_code = (s, dirty_files) => {
return s.files[module] return s.files[module]
} }
}) }, s.globals)
const state = { const state = {
...s, ...s,
@@ -839,12 +839,15 @@ const create_file = (state, dir, current_module) => {
return {...load_dir(state, dir), current_module} return {...load_dir(state, dir), current_module}
} }
const open_run_window = state => { const open_run_window = (state, globals) => {
// After we reopen run window, we should reload external modules in the // After we reopen run window, we should reload external modules in the
// context of new window. Clear external_imports_cache // context of new window. Clear external_imports_cache
return run_code({ return run_code({
...state, ...state,
globals,
external_imports_cache: null, external_imports_cache: null,
// Bust parse result cache because list of globals may change
parse_result: null,
}) })
} }

View File

@@ -3,8 +3,6 @@
import {set_push, set_diff, set_union, map_object, map_find, uniq} from './utils.js' import {set_push, set_diff, set_union, map_object, map_find, uniq} from './utils.js'
import {collect_destructuring_identifiers, collect_imports, ancestry, find_leaf} from './ast_utils.js' import {collect_destructuring_identifiers, collect_imports, ancestry, find_leaf} from './ast_utils.js'
import {globals} from './globals.js'
const map_find_definitions = (nodes, mapper) => { const map_find_definitions = (nodes, mapper) => {
const result = nodes.map(mapper) const result = nodes.map(mapper)
const undeclared = result.reduce( const undeclared = result.reduce(
@@ -74,7 +72,14 @@ const add_trivial_definition = node => {
*/ */
// TODO in same pass find already declared // TODO in same pass find already declared
export const find_definitions = (ast, scope = {}, closure_scope = {}, module_name) => { export const find_definitions = (ast, globals, scope = {}, closure_scope = {}, module_name) => {
// sanity check
if(!globals instanceof Set) {
throw new Error('not a set')
}
if(ast.type == 'identifier'){ if(ast.type == 'identifier'){
if(ast.definition != null) { if(ast.definition != null) {
// Definition previously added by add_trivial_definition // Definition previously added by add_trivial_definition
@@ -112,7 +117,7 @@ export const find_definitions = (ast, scope = {}, closure_scope = {}, module_nam
) )
const local_scope = children_with_scope.scope const local_scope = children_with_scope.scope
const {nodes, undeclared, closed} = map_find_definitions(children_with_scope.children, cs => const {nodes, undeclared, closed} = map_find_definitions(children_with_scope.children, cs =>
find_definitions(cs.node, {...scope, ...cs.scope}, local_scope, module_name) find_definitions(cs.node, globals, {...scope, ...cs.scope}, local_scope, module_name)
) )
return { return {
node: {...ast, children: nodes}, node: {...ast, children: nodes},
@@ -125,7 +130,7 @@ export const find_definitions = (ast, scope = {}, closure_scope = {}, module_nam
a.value, a a.value, a
])) ]))
const {nodes, undeclared, closed} = map_find_definitions(ast.children, const {nodes, undeclared, closed} = map_find_definitions(ast.children,
node => find_definitions(node, {...scope, ...closure_scope, ...args_scope}) node => find_definitions(node, globals, {...scope, ...closure_scope, ...args_scope})
) )
const next_closed = set_diff(closed, new Set(args_identifiers.map(a => a.value))) const next_closed = set_diff(closed, new Set(args_identifiers.map(a => a.value)))
return { return {
@@ -153,7 +158,7 @@ export const find_definitions = (ast, scope = {}, closure_scope = {}, module_nam
} }
const {nodes, undeclared, closed} = map_find_definitions(children, const {nodes, undeclared, closed} = map_find_definitions(children,
c => find_definitions(c, scope, closure_scope) c => find_definitions(c, globals, scope, closure_scope)
) )
return { return {

View File

@@ -1,4 +0,0 @@
export const globals = new Set(Object.getOwnPropertyNames(globalThis))
// Not available in node.js, but add to use in tests
globals.add('fetch')

View File

@@ -67,7 +67,10 @@ export const open_run_window = state => {
// event fired. TODO: better register SW explicitly and don't rely on // event fired. TODO: better register SW explicitly and don't rely on
// already registered SW? // already registered SW?
const onload = () => { const onload = () => {
exec('open_run_window') exec(
'open_run_window',
new Set(Object.getOwnPropertyNames(globalThis.run_window))
)
} }
const add_load_handler = () => { const add_load_handler = () => {
@@ -170,7 +173,10 @@ export const init = (container, _COMMANDS) => {
render_initial_state(ui, state) render_initial_state(ui, state)
open_run_iframe(state, () => { open_run_iframe(state, () => {
exec('open_run_window') exec(
'open_run_window',
new Set(Object.getOwnPropertyNames(globalThis.run_window))
)
}) })
}) })
} }

View File

@@ -1580,7 +1580,7 @@ const deduce_fn_names = node => {
return do_deduce_fn_names(node, null)[0] return do_deduce_fn_names(node, null)[0]
} }
export const parse = (str, is_module = false, module_name) => { export const parse = (str, globals, is_module = false, module_name) => {
// Add string to node for debugging // Add string to node for debugging
// TODO remove, only need for debug // TODO remove, only need for debug
@@ -1629,6 +1629,7 @@ export const parse = (str, is_module = false, module_name) => {
} else { } else {
const {node, undeclared} = find_definitions( const {node, undeclared} = find_definitions(
update_children(result.value), update_children(result.value),
globals,
null, null,
null, null,
module_name module_name
@@ -1675,7 +1676,7 @@ export const print_debug_node = node => {
return stringify(do_print_debug_node(node)) return stringify(do_print_debug_node(node))
} }
const do_load_modules = (module_names, loader, already_loaded) => { const do_load_modules = (module_names, loader, already_loaded, globals) => {
const parsed = module_names const parsed = module_names
.filter(module_name => already_loaded[module_name] == null) .filter(module_name => already_loaded[module_name] == null)
.map(module_name => { .map(module_name => {
@@ -1686,7 +1687,7 @@ const do_load_modules = (module_names, loader, already_loaded) => {
// Allows cache parse result // Allows cache parse result
return [module_name, m] return [module_name, m]
} else if(typeof(m) == 'string') { } else if(typeof(m) == 'string') {
return [module_name, parse(m, true, module_name)] return [module_name, parse(m, globals, true, module_name)]
} else { } else {
throw new Error('illegal state') throw new Error('illegal state')
} }
@@ -1728,7 +1729,12 @@ const do_load_modules = (module_names, loader, already_loaded) => {
// TODO when refactor this function to async, do not forget that different // TODO when refactor this function to async, do not forget that different
// deps can be loaded independently. So dont just put `await Promise.all(` // deps can be loaded independently. So dont just put `await Promise.all(`
// here // here
const loaded_deps = do_load_modules(deps, loader, {...already_loaded, ...modules}) const loaded_deps = do_load_modules(
deps,
loader,
{...already_loaded, ...modules},
globals
)
if(loaded_deps.ok) { if(loaded_deps.ok) {
return { return {
ok: true, ok: true,
@@ -1770,11 +1776,11 @@ const do_load_modules = (module_names, loader, already_loaded) => {
} }
} }
export const load_modules = (entry_module, loader) => { export const load_modules = (entry_module, loader, globals) => {
// TODO check_imports. detect cycles while modules are loading, in // TODO check_imports. detect cycles while modules are loading, in
// do_load_modules // do_load_modules
const result = do_load_modules([entry_module], loader, {}) const result = do_load_modules([entry_module], loader, {}, globals)
if(!result.ok) { if(!result.ok) {
return result return result
} else { } else {

View File

@@ -1,5 +1,5 @@
import {find_leaf, ancestry, find_node} from '../src/ast_utils.js' import {find_leaf, ancestry, find_node} from '../src/ast_utils.js'
import {parse, print_debug_node} from '../src/parse_js.js' import {print_debug_node} from '../src/parse_js.js'
import {eval_frame, eval_modules} from '../src/eval.js' import {eval_frame, eval_modules} from '../src/eval.js'
import {COMMANDS} from '../src/cmd.js' import {COMMANDS} from '../src/cmd.js'
import { import {
@@ -15,6 +15,7 @@ import {
test_only, test_only,
assert_equal, assert_equal,
stringify, stringify,
do_parse,
eval_tree, eval_tree,
assert_code_evals_to, assert_code_evals_to_async, assert_code_evals_to, assert_code_evals_to_async,
assert_code_error, assert_code_error_async, assert_code_error, assert_code_error_async,
@@ -30,7 +31,7 @@ import {
export const tests = [ export const tests = [
test('invalid token in the beginning', () => { test('invalid token in the beginning', () => {
const result = parse('# import') const result = do_parse('# import')
assert_equal(result, { assert_equal(result, {
ok: false, ok: false,
problems: [ { message: 'unexpected lexical token', index: 0 } ] problems: [ { message: 'unexpected lexical token', index: 0 } ]
@@ -38,7 +39,7 @@ export const tests = [
}), }),
test('invalid token in the middle', () => { test('invalid token in the middle', () => {
const result = parse(': # import') const result = do_parse(': # import')
assert_equal(result, { assert_equal(result, {
ok: false, ok: false,
problems: [ { message: 'unexpected lexical token', index: 2 } ] problems: [ { message: 'unexpected lexical token', index: 2 } ]
@@ -46,7 +47,7 @@ export const tests = [
}), }),
test('invalid token in the end', () => { test('invalid token in the end', () => {
const result = parse(': ^') const result = do_parse(': ^')
assert_equal(result, { assert_equal(result, {
ok: false, ok: false,
problems: [ { message: 'unexpected lexical token', index: 2 } ] problems: [ { message: 'unexpected lexical token', index: 2 } ]
@@ -61,7 +62,7 @@ export const tests = [
}), }),
test('empty if branch', () => { test('empty if branch', () => {
const r = parse(` const r = do_parse(`
if(true) { if(true) {
} else { } else {
} }
@@ -70,7 +71,7 @@ export const tests = [
}), }),
test('Must be finished by eof', () => { test('Must be finished by eof', () => {
const result = parse('}') const result = do_parse('}')
assert_equal(result.ok, false) assert_equal(result.ok, false)
}), }),
@@ -330,7 +331,7 @@ export const tests = [
}; };
x x
` `
const parse_result = parse(code) const parse_result = do_parse(code)
const assignment = find_leaf( const assignment = find_leaf(
parse_result.node, parse_result.node,
code.indexOf('x = 0') code.indexOf('x = 0')
@@ -397,7 +398,7 @@ export const tests = [
}), }),
test('ASI_1', () => { test('ASI_1', () => {
const parse_result = parse(` const parse_result = do_parse(`
1 1
const y = 2; const y = 2;
`) `)
@@ -409,7 +410,7 @@ export const tests = [
}), }),
test('ASI_2', () => { test('ASI_2', () => {
const parse_result = parse(` const parse_result = do_parse(`
1 1
2 2
`) `)
@@ -423,14 +424,14 @@ export const tests = [
test('ASI_restrited', () => { test('ASI_restrited', () => {
// Currently we forbid bare return statement, TODO // Currently we forbid bare return statement, TODO
assert_equal( assert_equal(
parse(` do_parse(`
return return
1 1
`).ok, `).ok,
false false
) )
assert_equal( assert_equal(
parse(` do_parse(`
throw throw
1 1
`).ok, `).ok,
@@ -921,7 +922,7 @@ export const tests = [
}), }),
test('bug parser pragma external', () => { test('bug parser pragma external', () => {
const result = parse(` const result = do_parse(`
// external // external
`) `)
assert_equal(result.ok, true) assert_equal(result.ok, true)
@@ -1158,7 +1159,7 @@ export const tests = [
const bar = baz => qux(foo, bar, baz, quux); const bar = baz => qux(foo, bar, baz, quux);
const qux = 3; const qux = 3;
` `
const result = parse(undeclared_test) const result = do_parse(undeclared_test)
assert_equal(result.problems.length, 1) assert_equal(result.problems.length, 1)
assert_equal(result.problems[0].message, 'undeclared identifier: quux') assert_equal(result.problems[0].message, 'undeclared identifier: quux')
}), }),
@@ -1177,7 +1178,7 @@ export const tests = [
const code = ` const code = `
const x = x; const x = x;
` `
return assert_equal(parse(code).problems[0].message, 'undeclared identifier: x') return assert_equal(do_parse(code).problems[0].message, 'undeclared identifier: x')
}), }),
test('function hoisting', () => { test('function hoisting', () => {
@@ -1220,6 +1221,18 @@ export const tests = [
assert_equal(s2.active_calltree_node.value, 5) assert_equal(s2.active_calltree_node.value, 5)
}), }),
/*
test('await only in async', () => {
const code = `
() => {
await 1
}
`
console.log(do_parse(code).problems[0])
//return assert_equal(do_parse(code).problems[0].message, 'undeclared identifier: x')
}),
*/
/* /*
TODO use before assignment TODO use before assignment
test('no use before assignment', () => { test('no use before assignment', () => {
@@ -1227,7 +1240,7 @@ export const tests = [
let x; let x;
x; x;
` `
return assert_equal(parse(test).problems[0].message, 'undeclared identifier: x') return assert_equal(do_parse(test).problems[0].message, 'undeclared identifier: x')
}), }),
*/ */
@@ -1513,7 +1526,7 @@ const y = x()`
1 2 1 2
} }
` `
const r = parse(code) const r = do_parse(code)
assert_equal(r.ok, false) assert_equal(r.ok, false)
const p = r.problems[0] const p = r.problems[0]
assert_equal(p.index, code.indexOf('2')) assert_equal(p.index, code.indexOf('2'))
@@ -1527,7 +1540,7 @@ const y = x()`
, ,
} }
` `
const r = parse(code) const r = do_parse(code)
assert_equal(r.ok, false) assert_equal(r.ok, false)
const p = r.problems[0] const p = r.problems[0]
assert_equal(p.index, code.indexOf(',')) assert_equal(p.index, code.indexOf(','))
@@ -1535,7 +1548,7 @@ const y = x()`
test('better parse errors 3', () => { test('better parse errors 3', () => {
const code = `[() => { , }] ` const code = `[() => { , }] `
const r = parse(code) const r = do_parse(code)
const p = r.problems[0] const p = r.problems[0]
assert_equal(p.index, code.indexOf(',')) assert_equal(p.index, code.indexOf(','))
}), }),

View File

@@ -46,11 +46,16 @@ export const patch_builtin = new Function(`
export const original_setTimeout = globalThis.run_window.__original_setTimeout export const original_setTimeout = globalThis.run_window.__original_setTimeout
export const do_parse = code => parse(
code,
new Set(Object.getOwnPropertyNames(globalThis.run_window))
)
export const parse_modules = (entry, modules) => export const parse_modules = (entry, modules) =>
load_modules(entry, module_name => modules[module_name]) load_modules(entry, module_name => modules[module_name])
export const eval_tree = code => { export const eval_tree = code => {
const parse_result = parse(code) const parse_result = do_parse(code)
assert_equal(parse_result.ok, true) assert_equal(parse_result.ok, true)
return eval_modules( return eval_modules(
{ {
@@ -102,7 +107,8 @@ export const test_initial_state = (code, state) => {
current_module: '', current_module: '',
...state, ...state,
}, },
) ),
new Set(Object.getOwnPropertyNames(globalThis.run_window))
) )
} }