@tishlang/tish 1.13.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.toml +2 -0
- package/bin/tish +0 -0
- package/crates/js_to_tish/src/transform/expr.rs +1 -0
- package/crates/tish/Cargo.toml +11 -3
- package/crates/tish/build.rs +21 -0
- package/crates/tish/src/cli_help.rs +15 -4
- package/crates/tish/src/main.rs +93 -21
- package/crates/tish/src/repl_completion.rs +0 -1
- package/crates/tish/tests/error_source_location.rs +36 -0
- package/crates/tish/tests/fixtures/runtime_error_location.tish +5 -0
- package/crates/tish/tests/fixtures/trycatch_runtime_errors.tish +15 -0
- package/crates/tish/tests/fixtures/tty_capability.tish +9 -0
- package/crates/tish/tests/integration_test.rs +402 -91
- package/crates/tish/tests/trycatch_runtime_errors.rs +45 -0
- package/crates/tish/tests/tty_capability.rs +43 -0
- package/crates/tish_ast/src/ast.rs +37 -8
- package/crates/tish_builtins/Cargo.toml +2 -0
- package/crates/tish_builtins/src/array.rs +375 -13
- package/crates/tish_builtins/src/collections.rs +481 -0
- package/crates/tish_builtins/src/construct.rs +59 -19
- package/crates/tish_builtins/src/date.rs +538 -0
- package/crates/tish_builtins/src/globals.rs +86 -6
- package/crates/tish_builtins/src/iterator.rs +129 -0
- package/crates/tish_builtins/src/lib.rs +5 -0
- package/crates/tish_builtins/src/number.rs +96 -0
- package/crates/tish_builtins/src/object.rs +2 -2
- package/crates/tish_builtins/src/string.rs +19 -20
- package/crates/tish_builtins/src/symbol.rs +1 -1
- package/crates/tish_builtins/src/typedarrays.rs +298 -0
- package/crates/tish_bytecode/src/chunk.rs +69 -1
- package/crates/tish_bytecode/src/compiler.rs +933 -89
- package/crates/tish_bytecode/src/encoding.rs +2 -0
- package/crates/tish_bytecode/src/lib.rs +2 -1
- package/crates/tish_bytecode/src/opcode.rs +47 -4
- package/crates/tish_bytecode/src/serialize.rs +31 -1
- package/crates/tish_compile/Cargo.toml +1 -0
- package/crates/tish_compile/src/check.rs +774 -0
- package/crates/tish_compile/src/codegen.rs +2334 -349
- package/crates/tish_compile/src/infer.rs +1395 -6
- package/crates/tish_compile/src/lib.rs +50 -8
- package/crates/tish_compile/src/resolve.rs +584 -21
- package/crates/tish_compile/src/types.rs +106 -2
- package/crates/tish_compile_js/src/codegen.rs +67 -0
- package/crates/tish_compile_js/src/tests_jsx.rs +64 -0
- package/crates/tish_core/Cargo.toml +7 -1
- package/crates/tish_core/src/console_style.rs +11 -1
- package/crates/tish_core/src/json.rs +81 -38
- package/crates/tish_core/src/lib.rs +3 -0
- package/crates/tish_core/src/shape.rs +85 -0
- package/crates/tish_core/src/value.rs +679 -25
- package/crates/tish_core/src/vmref.rs +13 -8
- package/crates/tish_cranelift/src/link.rs +17 -4
- package/crates/tish_cranelift_runtime/Cargo.toml +1 -0
- package/crates/tish_eval/Cargo.toml +6 -0
- package/crates/tish_eval/src/eval.rs +665 -117
- package/crates/tish_eval/src/http.rs +4 -1
- package/crates/tish_eval/src/natives.rs +165 -13
- package/crates/tish_eval/src/value.rs +31 -13
- package/crates/tish_eval/src/value_convert.rs +10 -4
- package/crates/tish_ffi/Cargo.toml +26 -0
- package/crates/tish_ffi/src/lib.rs +518 -0
- package/crates/tish_ffi/tests/fixtures/testmod/Cargo.toml +18 -0
- package/crates/tish_ffi/tests/fixtures/testmod/src/lib.rs +46 -0
- package/crates/tish_ffi/tests/loader.rs +65 -0
- package/crates/tish_fmt/src/lib.rs +43 -5
- package/crates/tish_lexer/src/lib.rs +397 -9
- package/crates/tish_lexer/src/token.rs +7 -0
- package/crates/tish_lint/src/lib.rs +2 -10
- package/crates/tish_lsp/src/import_goto.rs +2 -0
- package/crates/tish_lsp/src/main.rs +439 -26
- package/crates/tish_native/src/build.rs +55 -1
- package/crates/tish_opt/src/lib.rs +126 -23
- package/crates/tish_parser/src/lib.rs +55 -1
- package/crates/tish_parser/src/parser.rs +456 -34
- package/crates/tish_pg/src/lib.rs +3 -3
- package/crates/tish_resolve/src/lib.rs +99 -59
- package/crates/tish_runtime/Cargo.toml +4 -0
- package/crates/tish_runtime/src/http.rs +66 -17
- package/crates/tish_runtime/src/http_fetch.rs +29 -8
- package/crates/tish_runtime/src/http_hyper.rs +25 -2
- package/crates/tish_runtime/src/lib.rs +299 -44
- package/crates/tish_runtime/src/promise.rs +328 -18
- package/crates/tish_runtime/src/timers.rs +13 -7
- package/crates/tish_runtime/src/tty.rs +226 -0
- package/crates/tish_runtime/src/ws.rs +35 -18
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +2 -2
- package/crates/tish_ui/src/jsx.rs +10 -0
- package/crates/tish_ui/src/runtime/hooks.rs +19 -15
- package/crates/tish_ui/src/runtime/mod.rs +15 -12
- package/crates/tish_vm/Cargo.toml +14 -1
- package/crates/tish_vm/src/jit.rs +1050 -0
- package/crates/tish_vm/src/lib.rs +2 -0
- package/crates/tish_vm/src/vm.rs +1546 -202
- package/crates/tish_vm/tests/concurrent_shared_state.rs +140 -0
- package/crates/tish_wasm/src/lib.rs +6 -2
- package/crates/tish_wasm_runtime/src/gpu.rs +17 -1
- package/crates/tishlang_cargo_bindgen/src/classify.rs +1 -3
- package/crates/tishlang_cargo_bindgen/src/lib.rs +2 -2
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +1 -1
- package/justfile +8 -0
- package/package.json +1 -1
- package/platform/darwin-arm64/tish +0 -0
- package/platform/darwin-x64/tish +0 -0
- package/platform/linux-arm64/tish +0 -0
- package/platform/linux-x64/tish +0 -0
- package/platform/win32-x64/tish.exe +0 -0
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
use std::collections::{HashMap, HashSet};
|
|
5
5
|
use std::path::{Path, PathBuf};
|
|
6
6
|
use std::sync::Arc;
|
|
7
|
-
use tishlang_ast::{
|
|
7
|
+
use tishlang_ast::{
|
|
8
|
+
ArrayElement, ArrowBody, CallArg, DestructElement, DestructPattern, ExportDeclaration, Expr,
|
|
9
|
+
FunParam, ImportSpecifier, JsxAttrValue, JsxChild, JsxProp, MemberProp, ObjectProp, Program,
|
|
10
|
+
Statement,
|
|
11
|
+
};
|
|
8
12
|
|
|
9
13
|
/// Resolved native module: crate path and init expression.
|
|
10
14
|
#[derive(Debug, Clone)]
|
|
@@ -69,8 +73,8 @@ pub fn normalize_builtin_spec(spec: &str) -> Option<String> {
|
|
|
69
73
|
pub fn is_builtin_native_spec(spec: &str) -> bool {
|
|
70
74
|
matches!(
|
|
71
75
|
spec,
|
|
72
|
-
"tish:fs" | "tish:http" | "tish:timers" | "tish:process" | "tish:ws"
|
|
73
|
-
) || matches!(spec, "fs" | "http" | "timers" | "process" | "ws")
|
|
76
|
+
"tish:fs" | "tish:http" | "tish:timers" | "tish:process" | "tish:ws" | "tish:tty"
|
|
77
|
+
) || matches!(spec, "fs" | "http" | "timers" | "process" | "ws" | "tty")
|
|
74
78
|
}
|
|
75
79
|
|
|
76
80
|
/// Resolve all native imports in a merged program via package.json lookup.
|
|
@@ -128,6 +132,7 @@ pub fn program_uses_document(program: &Program) -> bool {
|
|
|
128
132
|
Expr::Unary { operand, .. } | Expr::TypeOf { operand, .. } => {
|
|
129
133
|
expr_uses_document(operand)
|
|
130
134
|
}
|
|
135
|
+
Expr::Delete { target, .. } => expr_uses_document(target),
|
|
131
136
|
Expr::Call { callee, args, .. } => {
|
|
132
137
|
expr_uses_document(callee)
|
|
133
138
|
|| args.iter().any(|a| match a {
|
|
@@ -206,10 +211,10 @@ pub fn program_uses_document(program: &Program) -> bool {
|
|
|
206
211
|
|
|
207
212
|
fn stmt_uses_document(s: &Statement) -> bool {
|
|
208
213
|
match s {
|
|
209
|
-
Statement::VarDecl { init, .. } => init.as_ref().is_some_and(
|
|
214
|
+
Statement::VarDecl { init, .. } => init.as_ref().is_some_and(expr_uses_document),
|
|
210
215
|
Statement::VarDeclDestructure { init, .. } => expr_uses_document(init),
|
|
211
216
|
Statement::ExprStmt { expr, .. } => expr_uses_document(expr),
|
|
212
|
-
Statement::Return { value, .. } => value.as_ref().is_some_and(
|
|
217
|
+
Statement::Return { value, .. } => value.as_ref().is_some_and(expr_uses_document),
|
|
213
218
|
Statement::Throw { value, .. } => expr_uses_document(value),
|
|
214
219
|
Statement::If {
|
|
215
220
|
cond,
|
|
@@ -229,8 +234,8 @@ pub fn program_uses_document(program: &Program) -> bool {
|
|
|
229
234
|
}
|
|
230
235
|
Statement::For { init, cond, update, body, .. } => {
|
|
231
236
|
init.as_ref().is_some_and(|s| stmt_uses_document(s.as_ref()))
|
|
232
|
-
|| cond.as_ref().is_some_and(
|
|
233
|
-
|| update.as_ref().is_some_and(
|
|
237
|
+
|| cond.as_ref().is_some_and(expr_uses_document)
|
|
238
|
+
|| update.as_ref().is_some_and(expr_uses_document)
|
|
234
239
|
|| stmt_uses_document(body)
|
|
235
240
|
}
|
|
236
241
|
Statement::ForOf { iterable, body, .. } => {
|
|
@@ -244,14 +249,16 @@ pub fn program_uses_document(program: &Program) -> bool {
|
|
|
244
249
|
} => {
|
|
245
250
|
expr_uses_document(expr)
|
|
246
251
|
|| cases.iter().any(|(e, stmts)| {
|
|
247
|
-
e.as_ref().is_some_and(
|
|
252
|
+
e.as_ref().is_some_and(expr_uses_document)
|
|
248
253
|
|| stmts.iter().any(stmt_uses_document)
|
|
249
254
|
})
|
|
250
255
|
|| default_body
|
|
251
256
|
.as_ref()
|
|
252
257
|
.is_some_and(|stmts| stmts.iter().any(stmt_uses_document))
|
|
253
258
|
}
|
|
254
|
-
Statement::Block { statements, .. }
|
|
259
|
+
Statement::Block { statements, .. } | Statement::Multi { statements, .. } => {
|
|
260
|
+
statements.iter().any(stmt_uses_document)
|
|
261
|
+
}
|
|
255
262
|
Statement::FunDecl { body, .. } => stmt_uses_document(body),
|
|
256
263
|
Statement::Try {
|
|
257
264
|
body,
|
|
@@ -603,7 +610,10 @@ pub fn generate_native_wrapper_rs(
|
|
|
603
610
|
key_lit, shim_crate, rust_fn
|
|
604
611
|
));
|
|
605
612
|
}
|
|
606
|
-
|
|
613
|
+
// `Value::object(m)` wraps the `ObjectMap` into the `ObjectData` that `Value::Object`
|
|
614
|
+
// now holds; `Value::Object(VmRef::new(m))` (raw map) stopped type-checking after the
|
|
615
|
+
// PropMap/ObjectData refactor (#78).
|
|
616
|
+
file.push_str(" Value::object(m)\n}\n\n");
|
|
607
617
|
}
|
|
608
618
|
if !any {
|
|
609
619
|
return String::new();
|
|
@@ -747,7 +757,9 @@ pub fn has_native_imports(program: &Program) -> bool {
|
|
|
747
757
|
pub fn has_external_native_imports(program: &Program) -> bool {
|
|
748
758
|
for stmt in &program.statements {
|
|
749
759
|
for spec in stmt_native_specs(stmt) {
|
|
750
|
-
|
|
760
|
+
// `ffi:` is portable (loadable on every backend via the C ABI), so it is NOT an
|
|
761
|
+
// "external native import" that non-rust backends must reject — only `cargo:` is.
|
|
762
|
+
if !is_builtin_native_spec(spec.as_ref()) && !is_ffi_native_spec(&spec) {
|
|
751
763
|
return true;
|
|
752
764
|
}
|
|
753
765
|
}
|
|
@@ -755,6 +767,20 @@ pub fn has_external_native_imports(program: &Program) -> bool {
|
|
|
755
767
|
false
|
|
756
768
|
}
|
|
757
769
|
|
|
770
|
+
/// Every `ffi:…` spec imported anywhere in `program` (deduplicated, in first-seen order). The CLI
|
|
771
|
+
/// loads each cdylib with `tish_ffi::load_module` and registers it before running.
|
|
772
|
+
pub fn ffi_native_specs(program: &Program) -> Vec<String> {
|
|
773
|
+
let mut out: Vec<String> = Vec::new();
|
|
774
|
+
for stmt in &program.statements {
|
|
775
|
+
for spec in stmt_native_specs(stmt) {
|
|
776
|
+
if is_ffi_native_spec(&spec) && !out.contains(&spec) {
|
|
777
|
+
out.push(spec);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
out
|
|
782
|
+
}
|
|
783
|
+
|
|
758
784
|
/// A resolved module: path and its parsed program.
|
|
759
785
|
#[derive(Debug, Clone)]
|
|
760
786
|
pub struct ResolvedModule {
|
|
@@ -907,28 +933,47 @@ fn load_module_recursive(
|
|
|
907
933
|
/// - fs, http, timers, process, ws (Node-compatible aliases for tish:*)
|
|
908
934
|
/// - tish:egui, tish:polars, etc.
|
|
909
935
|
/// - cargo:… (Cargo `rustDependencies` + generated wrapper; Rust native backend)
|
|
936
|
+
/// - ffi:… (a C-ABI cdylib loaded via `tish_ffi::load_module` — portable across backends)
|
|
910
937
|
///
|
|
911
938
|
/// Scoped npm packages (`@scope/pkg`) are merged as Tish source unless imported via `tish:…`.
|
|
912
939
|
pub fn is_native_import(spec: &str) -> bool {
|
|
913
940
|
spec.starts_with("tish:")
|
|
914
941
|
|| spec.starts_with("cargo:")
|
|
942
|
+
|| spec.starts_with("ffi:")
|
|
915
943
|
|| matches!(spec, "fs" | "http" | "timers" | "process" | "ws")
|
|
916
944
|
}
|
|
917
945
|
|
|
946
|
+
/// True for `ffi:…` specs (portable C-ABI cdylib extensions, loadable on every backend). The
|
|
947
|
+
/// path after `ffi:` is resolved relative to the importing program and loaded with
|
|
948
|
+
/// `tish_ffi::load_module`. Unlike `cargo:` (rust-AOT only), `ffi:` is allowed everywhere.
|
|
949
|
+
pub fn is_ffi_native_spec(spec: &str) -> bool {
|
|
950
|
+
spec.starts_with("ffi:")
|
|
951
|
+
}
|
|
952
|
+
|
|
918
953
|
/// Map native spec to Cargo feature name for built-in tish:* modules.
|
|
919
954
|
pub fn native_spec_to_feature(spec: &str) -> Option<String> {
|
|
920
955
|
let canonical = normalize_builtin_spec(spec)?;
|
|
921
956
|
canonical.strip_prefix("tish:").map(|s| s.to_string())
|
|
922
957
|
}
|
|
923
958
|
|
|
924
|
-
/// Resolve `package.json` at `pkg_root` to the package's main `.tish` entry
|
|
925
|
-
|
|
959
|
+
/// Resolve `package.json` at `pkg_root` to the package's main `.tish` entry.
|
|
960
|
+
///
|
|
961
|
+
/// `require_name`: when `Some(spec)`, the package's `package.json` `name` must equal `spec` (used for
|
|
962
|
+
/// the sibling / walk-up heuristic, where a coincidental directory name would otherwise false-match).
|
|
963
|
+
/// When `None`, the directory is authoritative — used for a `node_modules/<spec>` lookup, because npm
|
|
964
|
+
/// installs a dependency under its *dependency key* (the directory), not the package's internal `name`
|
|
965
|
+
/// (e.g. an aliased / scoped package, or a `file:`/workspace link). This matches Node's resolution and
|
|
966
|
+
/// tish's own path-dep rewriting, so a workspace package linked in as `lattish` resolves even though its
|
|
967
|
+
/// own `name` is `@tishlang/lattish`.
|
|
968
|
+
fn resolve_package_entry(pkg_root: &Path, require_name: Option<&str>) -> Option<PathBuf> {
|
|
926
969
|
let pkg_json = pkg_root.join("package.json");
|
|
927
970
|
if !pkg_json.exists() {
|
|
928
971
|
return None;
|
|
929
972
|
}
|
|
930
|
-
if
|
|
931
|
-
|
|
973
|
+
if let Some(spec) = require_name {
|
|
974
|
+
if read_package_name(&pkg_json).as_deref() != Some(spec) {
|
|
975
|
+
return None;
|
|
976
|
+
}
|
|
932
977
|
}
|
|
933
978
|
let content = std::fs::read_to_string(&pkg_json).ok()?;
|
|
934
979
|
let json: serde_json::Value = serde_json::from_str(&content).ok()?;
|
|
@@ -958,15 +1003,18 @@ fn resolve_package_root_to_entry(pkg_root: &Path, spec: &str) -> Option<PathBuf>
|
|
|
958
1003
|
pub fn resolve_bare_spec(spec: &str, from_dir: &Path, _project_root: &Path) -> Option<PathBuf> {
|
|
959
1004
|
let mut search = from_dir.to_path_buf();
|
|
960
1005
|
loop {
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
1006
|
+
// node_modules/<spec>: the directory is authoritative (npm installs by dependency key, like
|
|
1007
|
+
// Node) — do NOT require the package's internal `name` to match, so aliased/scoped/`file:`
|
|
1008
|
+
// workspace packages linked in under this name resolve.
|
|
1009
|
+
if let Some(p) = resolve_package_entry(&search.join("node_modules").join(spec), None) {
|
|
964
1010
|
return Some(p);
|
|
965
1011
|
}
|
|
966
|
-
|
|
1012
|
+
// sibling <spec>/ and the search dir itself: require name match (a bare directory name is a
|
|
1013
|
+
// weaker signal — guard against a coincidental same-named dir in a monorepo walk).
|
|
1014
|
+
if let Some(p) = resolve_package_entry(&search.join(spec), Some(spec)) {
|
|
967
1015
|
return Some(p);
|
|
968
1016
|
}
|
|
969
|
-
if let Some(p) =
|
|
1017
|
+
if let Some(p) = resolve_package_entry(&search, Some(spec)) {
|
|
970
1018
|
return Some(p);
|
|
971
1019
|
}
|
|
972
1020
|
if let Some(parent) = search.parent() {
|
|
@@ -1110,10 +1158,525 @@ fn merge_push(
|
|
|
1110
1158
|
statement_sources.push(source);
|
|
1111
1159
|
}
|
|
1112
1160
|
|
|
1161
|
+
// ── #97: module-private top-level binding isolation ─────────────────────────────────────
|
|
1162
|
+
//
|
|
1163
|
+
// All modules are concatenated into one flat program, so two modules that declare the same
|
|
1164
|
+
// *non-exported* top-level name (`let SHARED`, `fn err`, …) collide: a silent wrong value at
|
|
1165
|
+
// runtime, and a duplicate `let` (SyntaxError) in the `--target js` bundle. We give each such
|
|
1166
|
+
// private binding a module-unique name and rewrite references within that module. Exported
|
|
1167
|
+
// and imported names are never touched, so the import/export resolution below is unaffected —
|
|
1168
|
+
// and a name that doesn't collide is left exactly as-is (zero blast radius).
|
|
1169
|
+
|
|
1170
|
+
/// Names a module contributes to the merged flat namespace at its top level.
|
|
1171
|
+
/// `decls` = names declared (`let`/`const`/`fn`/destructure), `exported` = the subset that is
|
|
1172
|
+
/// exported, `imports` = local names bound by `import`. Type-only declarations are erased.
|
|
1173
|
+
fn collect_module_top_level_names(
|
|
1174
|
+
stmts: &[Statement],
|
|
1175
|
+
decls: &mut HashSet<String>,
|
|
1176
|
+
exported: &mut HashSet<String>,
|
|
1177
|
+
imports: &mut HashSet<String>,
|
|
1178
|
+
) {
|
|
1179
|
+
for stmt in stmts {
|
|
1180
|
+
match stmt {
|
|
1181
|
+
Statement::VarDecl { name, .. } | Statement::FunDecl { name, .. } => {
|
|
1182
|
+
decls.insert(name.to_string());
|
|
1183
|
+
}
|
|
1184
|
+
Statement::VarDeclDestructure { pattern, .. } => {
|
|
1185
|
+
collect_destructure_names(pattern, decls);
|
|
1186
|
+
}
|
|
1187
|
+
Statement::Multi { statements, .. } => {
|
|
1188
|
+
collect_module_top_level_names(statements, decls, exported, imports);
|
|
1189
|
+
}
|
|
1190
|
+
Statement::Export { declaration, .. } => {
|
|
1191
|
+
if let ExportDeclaration::Named(inner) = declaration.as_ref() {
|
|
1192
|
+
match inner.as_ref() {
|
|
1193
|
+
Statement::VarDecl { name, .. } | Statement::FunDecl { name, .. } => {
|
|
1194
|
+
decls.insert(name.to_string());
|
|
1195
|
+
exported.insert(name.to_string());
|
|
1196
|
+
}
|
|
1197
|
+
Statement::VarDeclDestructure { pattern, .. } => {
|
|
1198
|
+
let mut names = HashSet::new();
|
|
1199
|
+
collect_destructure_names(pattern, &mut names);
|
|
1200
|
+
for n in names {
|
|
1201
|
+
decls.insert(n.clone());
|
|
1202
|
+
exported.insert(n);
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
_ => {}
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
Statement::Import { specifiers, .. } => {
|
|
1210
|
+
for spec in specifiers {
|
|
1211
|
+
let n = match spec {
|
|
1212
|
+
ImportSpecifier::Named { name, alias, .. } => {
|
|
1213
|
+
alias.as_deref().unwrap_or(name).to_string()
|
|
1214
|
+
}
|
|
1215
|
+
ImportSpecifier::Namespace { name, .. }
|
|
1216
|
+
| ImportSpecifier::Default { name, .. } => name.to_string(),
|
|
1217
|
+
};
|
|
1218
|
+
imports.insert(n);
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
_ => {}
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
fn collect_destructure_names(pattern: &DestructPattern, out: &mut HashSet<String>) {
|
|
1227
|
+
let push = |el: &DestructElement, out: &mut HashSet<String>| match el {
|
|
1228
|
+
DestructElement::Ident(n, _) | DestructElement::Rest(n, _) => {
|
|
1229
|
+
out.insert(n.to_string());
|
|
1230
|
+
}
|
|
1231
|
+
DestructElement::Pattern(p) => collect_destructure_names(p, out),
|
|
1232
|
+
};
|
|
1233
|
+
match pattern {
|
|
1234
|
+
DestructPattern::Array(elements) => {
|
|
1235
|
+
for el in elements.iter().flatten() {
|
|
1236
|
+
push(el, out);
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
DestructPattern::Object(props) => {
|
|
1240
|
+
for p in props {
|
|
1241
|
+
push(&p.value, out);
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
/// Rename each module's non-exported top-level bindings whose name also occurs as a top-level
|
|
1248
|
+
/// name in another module, isolating module-private declarations (#97).
|
|
1249
|
+
fn isolate_private_top_level_bindings(modules: &mut [ResolvedModule]) {
|
|
1250
|
+
let n = modules.len();
|
|
1251
|
+
if n < 2 {
|
|
1252
|
+
return; // a single module cannot collide with another
|
|
1253
|
+
}
|
|
1254
|
+
let mut decls: Vec<HashSet<String>> = vec![HashSet::new(); n];
|
|
1255
|
+
let mut exported: Vec<HashSet<String>> = vec![HashSet::new(); n];
|
|
1256
|
+
// `occupancy[i]` = every top-level name module i contributes (decls ∪ import bindings).
|
|
1257
|
+
let mut occupancy: Vec<HashSet<String>> = vec![HashSet::new(); n];
|
|
1258
|
+
for (i, m) in modules.iter().enumerate() {
|
|
1259
|
+
let mut imports = HashSet::new();
|
|
1260
|
+
collect_module_top_level_names(
|
|
1261
|
+
&m.program.statements,
|
|
1262
|
+
&mut decls[i],
|
|
1263
|
+
&mut exported[i],
|
|
1264
|
+
&mut imports,
|
|
1265
|
+
);
|
|
1266
|
+
occupancy[i] = decls[i].union(&imports).cloned().collect();
|
|
1267
|
+
}
|
|
1268
|
+
// How many modules contribute each top-level name.
|
|
1269
|
+
let mut count: HashMap<&str, usize> = HashMap::new();
|
|
1270
|
+
for occ in &occupancy {
|
|
1271
|
+
for name in occ {
|
|
1272
|
+
*count.entry(name.as_str()).or_insert(0) += 1;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
for (i, m) in modules.iter_mut().enumerate() {
|
|
1276
|
+
let mut renames: HashMap<String, Arc<str>> = HashMap::new();
|
|
1277
|
+
for name in &decls[i] {
|
|
1278
|
+
if exported[i].contains(name) {
|
|
1279
|
+
continue; // exported names stay stable so imports keep resolving
|
|
1280
|
+
}
|
|
1281
|
+
if count.get(name.as_str()).copied().unwrap_or(0) > 1 {
|
|
1282
|
+
renames.insert(name.clone(), Arc::from(format!("{name}__m{i}")));
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
if renames.is_empty() {
|
|
1286
|
+
continue;
|
|
1287
|
+
}
|
|
1288
|
+
for stmt in &mut m.program.statements {
|
|
1289
|
+
// Each top-level statement starts from the full rename set (module top level is a
|
|
1290
|
+
// single scope; nested scopes shadow within their own cloned set).
|
|
1291
|
+
let mut active = renames.clone();
|
|
1292
|
+
rewrite_stmt_scope(stmt, &mut active, true);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
/// Apply the rename for a *declared* name. At module top level the binding is the canonical
|
|
1298
|
+
/// private one — rename it. In a nested scope the same name is a shadow — drop it from `active`
|
|
1299
|
+
/// so the inner binding and its references keep their own identity.
|
|
1300
|
+
fn apply_binding(name: &mut Arc<str>, active: &mut HashMap<String, Arc<str>>, top_level: bool) {
|
|
1301
|
+
if top_level {
|
|
1302
|
+
if let Some(renamed) = active.get(name.as_ref()) {
|
|
1303
|
+
*name = Arc::clone(renamed);
|
|
1304
|
+
}
|
|
1305
|
+
} else {
|
|
1306
|
+
active.remove(name.as_ref());
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
/// Rename / shadow the names bound by a destructuring pattern (mirrors [`apply_binding`]).
|
|
1311
|
+
fn rewrite_destructure_binding(
|
|
1312
|
+
pattern: &mut DestructPattern,
|
|
1313
|
+
active: &mut HashMap<String, Arc<str>>,
|
|
1314
|
+
top_level: bool,
|
|
1315
|
+
) {
|
|
1316
|
+
fn one(el: &mut DestructElement, active: &mut HashMap<String, Arc<str>>, top_level: bool) {
|
|
1317
|
+
match el {
|
|
1318
|
+
DestructElement::Ident(n, _) | DestructElement::Rest(n, _) => {
|
|
1319
|
+
if top_level {
|
|
1320
|
+
if let Some(renamed) = active.get(n.as_ref()) {
|
|
1321
|
+
*n = Arc::clone(renamed);
|
|
1322
|
+
}
|
|
1323
|
+
} else {
|
|
1324
|
+
active.remove(n.as_ref());
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
DestructElement::Pattern(p) => rewrite_destructure_binding(p, active, top_level),
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
match pattern {
|
|
1331
|
+
DestructPattern::Array(elements) => {
|
|
1332
|
+
for el in elements.iter_mut().flatten() {
|
|
1333
|
+
one(el, active, top_level);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
DestructPattern::Object(props) => {
|
|
1337
|
+
for p in props.iter_mut() {
|
|
1338
|
+
one(&mut p.value, active, top_level); // p.key is the source property — untouched
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
/// Remove function/arrow parameter names from a (child-scope) rename set so the body's
|
|
1345
|
+
/// references to them are not rewritten, and rewrite any default-value expressions in the
|
|
1346
|
+
/// enclosing scope.
|
|
1347
|
+
fn shadow_params(
|
|
1348
|
+
params: &mut [FunParam],
|
|
1349
|
+
child: &mut HashMap<String, Arc<str>>,
|
|
1350
|
+
parent: &HashMap<String, Arc<str>>,
|
|
1351
|
+
) {
|
|
1352
|
+
for p in params.iter_mut() {
|
|
1353
|
+
match p {
|
|
1354
|
+
FunParam::Simple(tp) => {
|
|
1355
|
+
if let Some(d) = &mut tp.default {
|
|
1356
|
+
rewrite_expr_scope(d, parent);
|
|
1357
|
+
}
|
|
1358
|
+
child.remove(tp.name.as_ref());
|
|
1359
|
+
}
|
|
1360
|
+
FunParam::Destructure {
|
|
1361
|
+
pattern, default, ..
|
|
1362
|
+
} => {
|
|
1363
|
+
if let Some(d) = default {
|
|
1364
|
+
rewrite_expr_scope(d, parent);
|
|
1365
|
+
}
|
|
1366
|
+
let mut names = HashSet::new();
|
|
1367
|
+
collect_destructure_names(pattern, &mut names);
|
|
1368
|
+
for n in &names {
|
|
1369
|
+
child.remove(n);
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
/// Scope-aware statement rewriter for [`isolate_private_top_level_bindings`].
|
|
1377
|
+
fn rewrite_stmt_scope(
|
|
1378
|
+
stmt: &mut Statement,
|
|
1379
|
+
active: &mut HashMap<String, Arc<str>>,
|
|
1380
|
+
top_level: bool,
|
|
1381
|
+
) {
|
|
1382
|
+
match stmt {
|
|
1383
|
+
Statement::VarDecl { name, init, .. } => {
|
|
1384
|
+
if let Some(e) = init {
|
|
1385
|
+
rewrite_expr_scope(e, active);
|
|
1386
|
+
}
|
|
1387
|
+
apply_binding(name, active, top_level);
|
|
1388
|
+
}
|
|
1389
|
+
Statement::VarDeclDestructure { pattern, init, .. } => {
|
|
1390
|
+
rewrite_expr_scope(init, active);
|
|
1391
|
+
rewrite_destructure_binding(pattern, active, top_level);
|
|
1392
|
+
}
|
|
1393
|
+
Statement::Multi { statements, .. } => {
|
|
1394
|
+
// Same-scope group (`let a = 1, b = 2`): thread `active`, keep `top_level`.
|
|
1395
|
+
for s in statements {
|
|
1396
|
+
rewrite_stmt_scope(s, active, top_level);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
Statement::ExprStmt { expr, .. } => rewrite_expr_scope(expr, active),
|
|
1400
|
+
Statement::Block { statements, .. } => {
|
|
1401
|
+
let mut child = active.clone();
|
|
1402
|
+
for s in statements {
|
|
1403
|
+
rewrite_stmt_scope(s, &mut child, false);
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
Statement::If {
|
|
1407
|
+
cond,
|
|
1408
|
+
then_branch,
|
|
1409
|
+
else_branch,
|
|
1410
|
+
..
|
|
1411
|
+
} => {
|
|
1412
|
+
rewrite_expr_scope(cond, active);
|
|
1413
|
+
rewrite_stmt_scope(then_branch, &mut active.clone(), false);
|
|
1414
|
+
if let Some(e) = else_branch {
|
|
1415
|
+
rewrite_stmt_scope(e, &mut active.clone(), false);
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
Statement::While { cond, body, .. } => {
|
|
1419
|
+
rewrite_expr_scope(cond, active);
|
|
1420
|
+
rewrite_stmt_scope(body, &mut active.clone(), false);
|
|
1421
|
+
}
|
|
1422
|
+
Statement::DoWhile { body, cond, .. } => {
|
|
1423
|
+
rewrite_stmt_scope(body, &mut active.clone(), false);
|
|
1424
|
+
rewrite_expr_scope(cond, active);
|
|
1425
|
+
}
|
|
1426
|
+
Statement::For {
|
|
1427
|
+
init,
|
|
1428
|
+
cond,
|
|
1429
|
+
update,
|
|
1430
|
+
body,
|
|
1431
|
+
..
|
|
1432
|
+
} => {
|
|
1433
|
+
let mut child = active.clone();
|
|
1434
|
+
if let Some(i) = init {
|
|
1435
|
+
rewrite_stmt_scope(i, &mut child, false);
|
|
1436
|
+
}
|
|
1437
|
+
if let Some(e) = cond {
|
|
1438
|
+
rewrite_expr_scope(e, &child);
|
|
1439
|
+
}
|
|
1440
|
+
if let Some(e) = update {
|
|
1441
|
+
rewrite_expr_scope(e, &child);
|
|
1442
|
+
}
|
|
1443
|
+
rewrite_stmt_scope(body, &mut child, false);
|
|
1444
|
+
}
|
|
1445
|
+
Statement::ForOf {
|
|
1446
|
+
name,
|
|
1447
|
+
iterable,
|
|
1448
|
+
body,
|
|
1449
|
+
..
|
|
1450
|
+
} => {
|
|
1451
|
+
rewrite_expr_scope(iterable, active);
|
|
1452
|
+
let mut child = active.clone();
|
|
1453
|
+
child.remove(name.as_ref()); // loop variable shadows
|
|
1454
|
+
rewrite_stmt_scope(body, &mut child, false);
|
|
1455
|
+
}
|
|
1456
|
+
Statement::Return { value, .. } => {
|
|
1457
|
+
if let Some(e) = value {
|
|
1458
|
+
rewrite_expr_scope(e, active);
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
Statement::Throw { value, .. } => rewrite_expr_scope(value, active),
|
|
1462
|
+
Statement::Break { .. } | Statement::Continue { .. } => {}
|
|
1463
|
+
Statement::FunDecl {
|
|
1464
|
+
name,
|
|
1465
|
+
params,
|
|
1466
|
+
rest_param,
|
|
1467
|
+
body,
|
|
1468
|
+
..
|
|
1469
|
+
} => {
|
|
1470
|
+
apply_binding(name, active, top_level);
|
|
1471
|
+
let mut child = active.clone();
|
|
1472
|
+
shadow_params(params, &mut child, active);
|
|
1473
|
+
if let Some(rp) = rest_param {
|
|
1474
|
+
child.remove(rp.name.as_ref());
|
|
1475
|
+
}
|
|
1476
|
+
rewrite_stmt_scope(body, &mut child, false);
|
|
1477
|
+
}
|
|
1478
|
+
Statement::Switch {
|
|
1479
|
+
expr,
|
|
1480
|
+
cases,
|
|
1481
|
+
default_body,
|
|
1482
|
+
..
|
|
1483
|
+
} => {
|
|
1484
|
+
rewrite_expr_scope(expr, active);
|
|
1485
|
+
// A switch body shares one block scope across all cases.
|
|
1486
|
+
let mut child = active.clone();
|
|
1487
|
+
for (test, body) in cases.iter_mut() {
|
|
1488
|
+
if let Some(t) = test {
|
|
1489
|
+
rewrite_expr_scope(t, &child);
|
|
1490
|
+
}
|
|
1491
|
+
for s in body {
|
|
1492
|
+
rewrite_stmt_scope(s, &mut child, false);
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
if let Some(body) = default_body {
|
|
1496
|
+
for s in body {
|
|
1497
|
+
rewrite_stmt_scope(s, &mut child, false);
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
Statement::Try {
|
|
1502
|
+
body,
|
|
1503
|
+
catch_param,
|
|
1504
|
+
catch_body,
|
|
1505
|
+
finally_body,
|
|
1506
|
+
..
|
|
1507
|
+
} => {
|
|
1508
|
+
rewrite_stmt_scope(body, &mut active.clone(), false);
|
|
1509
|
+
if let Some(cb) = catch_body {
|
|
1510
|
+
let mut child = active.clone();
|
|
1511
|
+
if let Some(p) = catch_param {
|
|
1512
|
+
child.remove(p.as_ref()); // catch binding shadows
|
|
1513
|
+
}
|
|
1514
|
+
rewrite_stmt_scope(cb, &mut child, false);
|
|
1515
|
+
}
|
|
1516
|
+
if let Some(fb) = finally_body {
|
|
1517
|
+
rewrite_stmt_scope(fb, &mut active.clone(), false);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
Statement::Export { declaration, .. } => match declaration.as_mut() {
|
|
1521
|
+
// Exported declarations keep their name (not in `active`), but their initializer /
|
|
1522
|
+
// body can still reference module-private names that were renamed.
|
|
1523
|
+
ExportDeclaration::Named(inner) => rewrite_stmt_scope(inner, active, top_level),
|
|
1524
|
+
ExportDeclaration::Default(e) => rewrite_expr_scope(e, active),
|
|
1525
|
+
},
|
|
1526
|
+
Statement::Import { .. }
|
|
1527
|
+
| Statement::TypeAlias { .. }
|
|
1528
|
+
| Statement::DeclareVar { .. }
|
|
1529
|
+
| Statement::DeclareFun { .. } => {}
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
/// Scope-aware expression rewriter: rename every free reference to a name in `active`.
|
|
1534
|
+
/// Expressions never declare module-level bindings, so `active` is read-only here; arrow
|
|
1535
|
+
/// functions clone it for their own (shadowed) parameter scope.
|
|
1536
|
+
fn rewrite_expr_scope(expr: &mut Expr, active: &HashMap<String, Arc<str>>) {
|
|
1537
|
+
let rename = |name: &mut Arc<str>| {
|
|
1538
|
+
if let Some(renamed) = active.get(name.as_ref()) {
|
|
1539
|
+
*name = Arc::clone(renamed);
|
|
1540
|
+
}
|
|
1541
|
+
};
|
|
1542
|
+
match expr {
|
|
1543
|
+
Expr::Ident { name, .. } => rename(name),
|
|
1544
|
+
Expr::Assign { name, value, .. }
|
|
1545
|
+
| Expr::CompoundAssign { name, value, .. }
|
|
1546
|
+
| Expr::LogicalAssign { name, value, .. } => {
|
|
1547
|
+
rename(name);
|
|
1548
|
+
rewrite_expr_scope(value, active);
|
|
1549
|
+
}
|
|
1550
|
+
Expr::PostfixInc { name, .. }
|
|
1551
|
+
| Expr::PostfixDec { name, .. }
|
|
1552
|
+
| Expr::PrefixInc { name, .. }
|
|
1553
|
+
| Expr::PrefixDec { name, .. } => rename(name),
|
|
1554
|
+
Expr::Binary { left, right, .. } => {
|
|
1555
|
+
rewrite_expr_scope(left, active);
|
|
1556
|
+
rewrite_expr_scope(right, active);
|
|
1557
|
+
}
|
|
1558
|
+
Expr::Unary { operand, .. } | Expr::TypeOf { operand, .. } | Expr::Await { operand, .. } => {
|
|
1559
|
+
rewrite_expr_scope(operand, active)
|
|
1560
|
+
}
|
|
1561
|
+
Expr::Delete { target, .. } => rewrite_expr_scope(target, active),
|
|
1562
|
+
Expr::Call { callee, args, .. } | Expr::New { callee, args, .. } => {
|
|
1563
|
+
rewrite_expr_scope(callee, active);
|
|
1564
|
+
for a in args {
|
|
1565
|
+
match a {
|
|
1566
|
+
CallArg::Expr(e) | CallArg::Spread(e) => rewrite_expr_scope(e, active),
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
Expr::Member { object, prop, .. } => {
|
|
1571
|
+
rewrite_expr_scope(object, active);
|
|
1572
|
+
if let MemberProp::Expr(e) = prop {
|
|
1573
|
+
rewrite_expr_scope(e, active); // computed key; `obj.name` is untouched
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
Expr::Index { object, index, .. } => {
|
|
1577
|
+
rewrite_expr_scope(object, active);
|
|
1578
|
+
rewrite_expr_scope(index, active);
|
|
1579
|
+
}
|
|
1580
|
+
Expr::Conditional {
|
|
1581
|
+
cond,
|
|
1582
|
+
then_branch,
|
|
1583
|
+
else_branch,
|
|
1584
|
+
..
|
|
1585
|
+
} => {
|
|
1586
|
+
rewrite_expr_scope(cond, active);
|
|
1587
|
+
rewrite_expr_scope(then_branch, active);
|
|
1588
|
+
rewrite_expr_scope(else_branch, active);
|
|
1589
|
+
}
|
|
1590
|
+
Expr::NullishCoalesce { left, right, .. } => {
|
|
1591
|
+
rewrite_expr_scope(left, active);
|
|
1592
|
+
rewrite_expr_scope(right, active);
|
|
1593
|
+
}
|
|
1594
|
+
Expr::Array { elements, .. } => {
|
|
1595
|
+
for el in elements {
|
|
1596
|
+
match el {
|
|
1597
|
+
ArrayElement::Expr(e) | ArrayElement::Spread(e) => rewrite_expr_scope(e, active),
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
Expr::Object { props, .. } => {
|
|
1602
|
+
for p in props {
|
|
1603
|
+
match p {
|
|
1604
|
+
ObjectProp::KeyValue(_, e) | ObjectProp::Spread(e) => {
|
|
1605
|
+
rewrite_expr_scope(e, active) // key is a property name; value recurses
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
Expr::MemberAssign { object, value, .. } => {
|
|
1611
|
+
rewrite_expr_scope(object, active);
|
|
1612
|
+
rewrite_expr_scope(value, active);
|
|
1613
|
+
}
|
|
1614
|
+
Expr::IndexAssign {
|
|
1615
|
+
object,
|
|
1616
|
+
index,
|
|
1617
|
+
value,
|
|
1618
|
+
..
|
|
1619
|
+
} => {
|
|
1620
|
+
rewrite_expr_scope(object, active);
|
|
1621
|
+
rewrite_expr_scope(index, active);
|
|
1622
|
+
rewrite_expr_scope(value, active);
|
|
1623
|
+
}
|
|
1624
|
+
Expr::ArrowFunction { params, body, .. } => {
|
|
1625
|
+
let mut child = active.clone();
|
|
1626
|
+
shadow_params(params, &mut child, active);
|
|
1627
|
+
match body {
|
|
1628
|
+
ArrowBody::Expr(e) => rewrite_expr_scope(e, &child),
|
|
1629
|
+
ArrowBody::Block(s) => rewrite_stmt_scope(s, &mut child, false),
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
Expr::TemplateLiteral { exprs, .. } => {
|
|
1633
|
+
for e in exprs {
|
|
1634
|
+
rewrite_expr_scope(e, active);
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
Expr::JsxElement {
|
|
1638
|
+
tag,
|
|
1639
|
+
props,
|
|
1640
|
+
children,
|
|
1641
|
+
..
|
|
1642
|
+
} => {
|
|
1643
|
+
// `<Component …>` references a binding; lowercase HTML tags aren't in `active`.
|
|
1644
|
+
if let Some(renamed) = active.get(tag.as_ref()) {
|
|
1645
|
+
*tag = Arc::clone(renamed);
|
|
1646
|
+
}
|
|
1647
|
+
for prop in props {
|
|
1648
|
+
match prop {
|
|
1649
|
+
JsxProp::Attr { value, .. } => match value {
|
|
1650
|
+
JsxAttrValue::Expr(e) => rewrite_expr_scope(e, active),
|
|
1651
|
+
JsxAttrValue::String(_) | JsxAttrValue::ImplicitTrue => {}
|
|
1652
|
+
},
|
|
1653
|
+
JsxProp::Spread(e) => rewrite_expr_scope(e, active),
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
for child in children {
|
|
1657
|
+
if let JsxChild::Expr(e) = child {
|
|
1658
|
+
rewrite_expr_scope(e, active);
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
Expr::JsxFragment { children, .. } => {
|
|
1663
|
+
for child in children {
|
|
1664
|
+
if let JsxChild::Expr(e) = child {
|
|
1665
|
+
rewrite_expr_scope(e, active);
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
Expr::Literal { .. } | Expr::NativeModuleLoad { .. } => {}
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1113
1673
|
/// Merge all resolved modules into a single program. Dependencies are emitted first.
|
|
1114
1674
|
/// Import statements are rewritten as bindings from already-emitted dep exports.
|
|
1115
1675
|
/// Export statements are unwrapped (the inner declaration is emitted).
|
|
1116
|
-
pub fn merge_modules(modules: Vec<ResolvedModule>) -> Result<MergedProgram, String> {
|
|
1676
|
+
pub fn merge_modules(mut modules: Vec<ResolvedModule>) -> Result<MergedProgram, String> {
|
|
1677
|
+
// #97: isolate module-private top-level bindings before they are flattened together.
|
|
1678
|
+
isolate_private_top_level_bindings(&mut modules);
|
|
1679
|
+
|
|
1117
1680
|
let path_to_idx: HashMap<PathBuf, usize> = modules
|
|
1118
1681
|
.iter()
|
|
1119
1682
|
.enumerate()
|