From 2f5db0452cc35743da3585e47d41753a7b087a06 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilev Date: Thu, 15 Jun 2023 23:55:06 +0300 Subject: [PATCH] refactor globals --- src/cmd.js | 7 ++++-- src/find_definitions.js | 17 +++++++++----- src/globals.js | 4 ---- src/index.js | 10 +++++++-- src/parse_js.js | 18 ++++++++++----- test/test.js | 49 ++++++++++++++++++++++++++--------------- test/utils.js | 10 +++++++-- 7 files changed, 75 insertions(+), 40 deletions(-) delete mode 100644 src/globals.js diff --git a/src/cmd.js b/src/cmd.js index 1eb9846..31c4ab0 100644 --- a/src/cmd.js +++ b/src/cmd.js @@ -77,7 +77,7 @@ const run_code = (s, dirty_files) => { return s.files[module] } - }) + }, s.globals) const state = { ...s, @@ -839,12 +839,15 @@ const create_file = (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 // context of new window. Clear external_imports_cache return run_code({ ...state, + globals, external_imports_cache: null, + // Bust parse result cache because list of globals may change + parse_result: null, }) } diff --git a/src/find_definitions.js b/src/find_definitions.js index 88564b5..1e28bed 100644 --- a/src/find_definitions.js +++ b/src/find_definitions.js @@ -3,8 +3,6 @@ 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 {globals} from './globals.js' - const map_find_definitions = (nodes, mapper) => { const result = nodes.map(mapper) const undeclared = result.reduce( @@ -74,7 +72,14 @@ const add_trivial_definition = node => { */ // 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.definition != null) { // 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 {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 { node: {...ast, children: nodes}, @@ -125,7 +130,7 @@ export const find_definitions = (ast, scope = {}, closure_scope = {}, module_nam a.value, a ])) 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))) return { @@ -153,7 +158,7 @@ export const find_definitions = (ast, scope = {}, closure_scope = {}, module_nam } const {nodes, undeclared, closed} = map_find_definitions(children, - c => find_definitions(c, scope, closure_scope) + c => find_definitions(c, globals, scope, closure_scope) ) return { diff --git a/src/globals.js b/src/globals.js deleted file mode 100644 index 60ef7e8..0000000 --- a/src/globals.js +++ /dev/null @@ -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') diff --git a/src/index.js b/src/index.js index 34cb23c..44089da 100644 --- a/src/index.js +++ b/src/index.js @@ -67,7 +67,10 @@ export const open_run_window = state => { // event fired. TODO: better register SW explicitly and don't rely on // already registered SW? const onload = () => { - exec('open_run_window') + exec( + 'open_run_window', + new Set(Object.getOwnPropertyNames(globalThis.run_window)) + ) } const add_load_handler = () => { @@ -170,7 +173,10 @@ export const init = (container, _COMMANDS) => { render_initial_state(ui, state) open_run_iframe(state, () => { - exec('open_run_window') + exec( + 'open_run_window', + new Set(Object.getOwnPropertyNames(globalThis.run_window)) + ) }) }) } diff --git a/src/parse_js.js b/src/parse_js.js index a541abd..1559d0b 100644 --- a/src/parse_js.js +++ b/src/parse_js.js @@ -1580,7 +1580,7 @@ const deduce_fn_names = node => { 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 // TODO remove, only need for debug @@ -1629,6 +1629,7 @@ export const parse = (str, is_module = false, module_name) => { } else { const {node, undeclared} = find_definitions( update_children(result.value), + globals, null, null, module_name @@ -1675,7 +1676,7 @@ export const 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 .filter(module_name => already_loaded[module_name] == null) .map(module_name => { @@ -1686,7 +1687,7 @@ const do_load_modules = (module_names, loader, already_loaded) => { // Allows cache parse result return [module_name, m] } else if(typeof(m) == 'string') { - return [module_name, parse(m, true, module_name)] + return [module_name, parse(m, globals, true, module_name)] } else { 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 // deps can be loaded independently. So dont just put `await Promise.all(` // 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) { return { 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 // do_load_modules - const result = do_load_modules([entry_module], loader, {}) + const result = do_load_modules([entry_module], loader, {}, globals) if(!result.ok) { return result } else { diff --git a/test/test.js b/test/test.js index 0eb2f93..62bf1cc 100644 --- a/test/test.js +++ b/test/test.js @@ -1,5 +1,5 @@ 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 {COMMANDS} from '../src/cmd.js' import { @@ -15,6 +15,7 @@ import { test_only, assert_equal, stringify, + do_parse, eval_tree, assert_code_evals_to, assert_code_evals_to_async, assert_code_error, assert_code_error_async, @@ -30,7 +31,7 @@ import { export const tests = [ test('invalid token in the beginning', () => { - const result = parse('# import') + const result = do_parse('# import') assert_equal(result, { ok: false, problems: [ { message: 'unexpected lexical token', index: 0 } ] @@ -38,7 +39,7 @@ export const tests = [ }), test('invalid token in the middle', () => { - const result = parse(': # import') + const result = do_parse(': # import') assert_equal(result, { ok: false, problems: [ { message: 'unexpected lexical token', index: 2 } ] @@ -46,7 +47,7 @@ export const tests = [ }), test('invalid token in the end', () => { - const result = parse(': ^') + const result = do_parse(': ^') assert_equal(result, { ok: false, problems: [ { message: 'unexpected lexical token', index: 2 } ] @@ -61,7 +62,7 @@ export const tests = [ }), test('empty if branch', () => { - const r = parse(` + const r = do_parse(` if(true) { } else { } @@ -70,7 +71,7 @@ export const tests = [ }), test('Must be finished by eof', () => { - const result = parse('}') + const result = do_parse('}') assert_equal(result.ok, false) }), @@ -330,7 +331,7 @@ export const tests = [ }; x ` - const parse_result = parse(code) + const parse_result = do_parse(code) const assignment = find_leaf( parse_result.node, code.indexOf('x = 0') @@ -397,7 +398,7 @@ export const tests = [ }), test('ASI_1', () => { - const parse_result = parse(` + const parse_result = do_parse(` 1 const y = 2; `) @@ -409,7 +410,7 @@ export const tests = [ }), test('ASI_2', () => { - const parse_result = parse(` + const parse_result = do_parse(` 1 2 `) @@ -423,14 +424,14 @@ export const tests = [ test('ASI_restrited', () => { // Currently we forbid bare return statement, TODO assert_equal( - parse(` + do_parse(` return 1 `).ok, false ) assert_equal( - parse(` + do_parse(` throw 1 `).ok, @@ -921,7 +922,7 @@ export const tests = [ }), test('bug parser pragma external', () => { - const result = parse(` + const result = do_parse(` // external `) assert_equal(result.ok, true) @@ -1158,7 +1159,7 @@ export const tests = [ const bar = baz => qux(foo, bar, baz, quux); const qux = 3; ` - const result = parse(undeclared_test) + const result = do_parse(undeclared_test) assert_equal(result.problems.length, 1) assert_equal(result.problems[0].message, 'undeclared identifier: quux') }), @@ -1177,7 +1178,7 @@ export const tests = [ const code = ` 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', () => { @@ -1220,6 +1221,18 @@ export const tests = [ 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 test('no use before assignment', () => { @@ -1227,7 +1240,7 @@ export const tests = [ let 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 } ` - const r = parse(code) + const r = do_parse(code) assert_equal(r.ok, false) const p = r.problems[0] 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) const p = r.problems[0] assert_equal(p.index, code.indexOf(',')) @@ -1535,7 +1548,7 @@ const y = x()` test('better parse errors 3', () => { const code = `[() => { , }] ` - const r = parse(code) + const r = do_parse(code) const p = r.problems[0] assert_equal(p.index, code.indexOf(',')) }), diff --git a/test/utils.js b/test/utils.js index c55046c..94f1a62 100644 --- a/test/utils.js +++ b/test/utils.js @@ -46,11 +46,16 @@ export const patch_builtin = new Function(` 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) => load_modules(entry, module_name => modules[module_name]) export const eval_tree = code => { - const parse_result = parse(code) + const parse_result = do_parse(code) assert_equal(parse_result.ok, true) return eval_modules( { @@ -102,7 +107,8 @@ export const test_initial_state = (code, state) => { current_module: '', ...state, }, - ) + ), + new Set(Object.getOwnPropertyNames(globalThis.run_window)) ) }