[rust] Workspace hygiene

I've primarily used what Cargo calls virtual workspaces, where the top-level 
Cargo.toml just lists a bunch of packages and they each have their own 
dependencies. This is fine, but i've noticed that more repos are using real 
workspaces and they offer a bunch of benefits. You define the dependencies in 
the top-level Cargo.toml and then can easily refer to them from multiple crates, 
ensuring all the versions match up. It makes it easier to refer to other crates 
in the workspace, too, because you define the path once at the root, then every 
other crate can just say `forget_foo = { workspace = true }`. 

I also renamed all the crates to be prefixed with `forget_`, in some cases 
removing the redundant `hir` name, So `hir-optimization` became 
`forget_optimization`, `hir-ssa` became `forget_ssa`. Also note the switch from 
hyphenated names to underscores everywhere, since at the end of the day you have 
to write the name with underscores in source code. 

I also deleted the demo crate that i started with since we don't need it 
anymore. 

And finally, i added an explicit publish = false to all the crates just to 
prevent mistakes.
This commit is contained in:
Joe Savona
2023-07-14 11:39:46 +09:00
parent 8ed6c6ab08
commit 68b0effca3
93 changed files with 1050 additions and 1133 deletions
+151 -285
View File
@@ -233,18 +233,6 @@ dependencies = [
"serde",
]
[[package]]
name = "build-hir"
version = "0.1.0"
dependencies = [
"bumpalo",
"estree",
"hir",
"indexmap 2.0.0",
"miette 5.9.0",
"thiserror",
]
[[package]]
name = "bumpalo"
version = "3.13.0"
@@ -393,15 +381,6 @@ version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]]
name = "elsa"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e0aca8dce8856e420195bd13b6a64de3334235ccc9214e824b86b12bf26283"
dependencies = [
"stable_deref_trait",
]
[[package]]
name = "encode_unicode"
version = "0.3.6"
@@ -456,12 +435,35 @@ dependencies = [
]
[[package]]
name = "estree"
name = "fixedbitset"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "forget_build_hir"
version = "0.1.0"
dependencies = [
"bumpalo",
"elsa",
"estree-codegen",
"forget_estree",
"forget_hir",
"indexmap 2.0.0",
"miette 5.9.0",
"thiserror",
]
[[package]]
name = "forget_estree"
version = "0.1.0"
dependencies = [
"bumpalo",
"forget_estree_codegen",
"insta",
"serde",
"serde_json",
@@ -469,7 +471,7 @@ dependencies = [
]
[[package]]
name = "estree-codegen"
name = "forget_estree_codegen"
version = "0.1.0"
dependencies = [
"indexmap 2.0.0",
@@ -481,40 +483,74 @@ dependencies = [
]
[[package]]
name = "estree-swc"
name = "forget_estree_swc"
version = "0.1.0"
dependencies = [
"estree",
"forget_estree",
"swc",
"swc_core 0.79.9",
"swc_core",
]
[[package]]
name = "fixedbitset"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "fixtures"
name = "forget_fixtures"
version = "0.1.0"
dependencies = [
"build-hir",
"bumpalo",
"estree",
"estree-swc",
"hir",
"hir-optimization",
"hir-ssa",
"forget_build_hir",
"forget_estree",
"forget_estree_swc",
"forget_hir",
"forget_optimization",
"forget_ssa",
"insta",
"miette 5.9.0",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
name = "forget_hir"
version = "0.1.0"
dependencies = [
"bumpalo",
"forget_estree",
"forget_utils",
"indexmap 2.0.0",
"serde",
]
[[package]]
name = "forget_optimization"
version = "0.1.0"
dependencies = [
"bumpalo",
"forget_build_hir",
"forget_estree",
"forget_hir",
"forget_ssa",
"forget_utils",
"indexmap 2.0.0",
"miette 5.9.0",
"thiserror",
]
[[package]]
name = "forget_ssa"
version = "0.1.0"
dependencies = [
"bumpalo",
"forget_hir",
"forget_utils",
"indexmap 2.0.0",
"miette 5.9.0",
"thiserror",
]
[[package]]
name = "forget_utils"
version = "0.1.0"
dependencies = [
"bumpalo",
"stacker",
]
[[package]]
name = "form_urlencoded"
@@ -630,44 +666,6 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
[[package]]
name = "hir"
version = "0.1.0"
dependencies = [
"bumpalo",
"estree",
"indexmap 2.0.0",
"serde",
"utils",
]
[[package]]
name = "hir-optimization"
version = "0.1.0"
dependencies = [
"build-hir",
"bumpalo",
"estree",
"hir",
"hir-ssa",
"indexmap 2.0.0",
"miette 5.9.0",
"thiserror",
"utils",
]
[[package]]
name = "hir-ssa"
version = "0.1.0"
dependencies = [
"bumpalo",
"hir",
"indexmap 2.0.0",
"miette 5.9.0",
"thiserror",
"utils",
]
[[package]]
name = "iana-time-zone"
version = "0.1.57"
@@ -1783,20 +1781,20 @@ dependencies = [
"swc_cached",
"swc_common",
"swc_config",
"swc_ecma_ast 0.107.0",
"swc_ecma_ast",
"swc_ecma_codegen",
"swc_ecma_ext_transforms",
"swc_ecma_lints",
"swc_ecma_loader",
"swc_ecma_minifier",
"swc_ecma_parser 0.137.1",
"swc_ecma_parser",
"swc_ecma_preset_env",
"swc_ecma_transforms",
"swc_ecma_transforms_base 0.130.1",
"swc_ecma_transforms_base",
"swc_ecma_transforms_compat",
"swc_ecma_transforms_optimization",
"swc_ecma_utils 0.120.1",
"swc_ecma_visit 0.93.0",
"swc_ecma_utils",
"swc_ecma_visit",
"swc_error_reporters",
"swc_node_comments",
"swc_timer",
@@ -1805,18 +1803,6 @@ dependencies = [
"url",
]
[[package]]
name = "swc-demo"
version = "0.1.0"
dependencies = [
"swc_common",
"swc_core 0.78.28",
"swc_ecma_parser 0.136.8",
"swc_ecma_transforms_base 0.129.15",
"swc_ecma_visit 0.92.5",
"swc_visit",
]
[[package]]
name = "swc_atoms"
version = "0.5.6"
@@ -1852,9 +1838,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6414bd4e553f5638961d39b07075ffd37a3d63176829592f4a5900260d94ca1"
dependencies = [
"ahash 0.8.3",
"anyhow",
"ast_node",
"atty",
"better_scoped_tls",
"cfg-if",
"either",
@@ -1871,7 +1855,6 @@ dependencies = [
"swc_atoms",
"swc_eq_ignore_macros",
"swc_visit",
"termcolor",
"tracing",
"unicode-width",
"url",
@@ -1902,15 +1885,6 @@ dependencies = [
"syn 2.0.23",
]
[[package]]
name = "swc_core"
version = "0.78.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e38b2fd17f97d84f1152eb82d7ebc089e98ddd02b5b750af37a0c91dec81c87"
dependencies = [
"vergen",
]
[[package]]
name = "swc_core"
version = "0.79.9"
@@ -1920,29 +1894,13 @@ dependencies = [
"swc",
"swc_atoms",
"swc_common",
"swc_ecma_ast 0.107.0",
"swc_ecma_parser 0.137.1",
"swc_ecma_transforms_base 0.130.1",
"swc_ecma_visit 0.93.0",
"swc_ecma_ast",
"swc_ecma_parser",
"swc_ecma_transforms_base",
"swc_ecma_visit",
"vergen",
]
[[package]]
name = "swc_ecma_ast"
version = "0.106.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebf4d6804b1da4146c4c0359d129e3dd43568d321f69d7953d9abbca4ded76ba"
dependencies = [
"bitflags 2.3.3",
"is-macro",
"num-bigint",
"scoped-tls",
"string_enum",
"swc_atoms",
"swc_common",
"unicode-id",
]
[[package]]
name = "swc_ecma_ast"
version = "0.107.0"
@@ -1974,7 +1932,7 @@ dependencies = [
"sourcemap",
"swc_atoms",
"swc_common",
"swc_ecma_ast 0.107.0",
"swc_ecma_ast",
"swc_ecma_codegen_macros",
"tracing",
]
@@ -2001,9 +1959,9 @@ dependencies = [
"phf",
"swc_atoms",
"swc_common",
"swc_ecma_ast 0.107.0",
"swc_ecma_utils 0.120.1",
"swc_ecma_visit 0.93.0",
"swc_ecma_ast",
"swc_ecma_utils",
"swc_ecma_visit",
]
[[package]]
@@ -2022,9 +1980,9 @@ dependencies = [
"swc_atoms",
"swc_common",
"swc_config",
"swc_ecma_ast 0.107.0",
"swc_ecma_utils 0.120.1",
"swc_ecma_visit 0.93.0",
"swc_ecma_ast",
"swc_ecma_utils",
"swc_ecma_visit",
]
[[package]]
@@ -2072,38 +2030,18 @@ dependencies = [
"swc_cached",
"swc_common",
"swc_config",
"swc_ecma_ast 0.107.0",
"swc_ecma_ast",
"swc_ecma_codegen",
"swc_ecma_parser 0.137.1",
"swc_ecma_transforms_base 0.130.1",
"swc_ecma_parser",
"swc_ecma_transforms_base",
"swc_ecma_transforms_optimization",
"swc_ecma_usage_analyzer",
"swc_ecma_utils 0.120.1",
"swc_ecma_visit 0.93.0",
"swc_ecma_utils",
"swc_ecma_visit",
"swc_timer",
"tracing",
]
[[package]]
name = "swc_ecma_parser"
version = "0.136.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45d40421c607d7a48334f78a9b24a5cbde1f36250f9986746ec082208d68b39f"
dependencies = [
"either",
"lexical",
"num-bigint",
"serde",
"smallvec",
"smartstring",
"stacker",
"swc_atoms",
"swc_common",
"swc_ecma_ast 0.106.6",
"tracing",
"typed-arena",
]
[[package]]
name = "swc_ecma_parser"
version = "0.137.1"
@@ -2119,7 +2057,7 @@ dependencies = [
"stacker",
"swc_atoms",
"swc_common",
"swc_ecma_ast 0.107.0",
"swc_ecma_ast",
"tracing",
"typed-arena",
]
@@ -2143,10 +2081,10 @@ dependencies = [
"string_enum",
"swc_atoms",
"swc_common",
"swc_ecma_ast 0.107.0",
"swc_ecma_ast",
"swc_ecma_transforms",
"swc_ecma_utils 0.120.1",
"swc_ecma_visit 0.93.0",
"swc_ecma_utils",
"swc_ecma_visit",
]
[[package]]
@@ -2157,39 +2095,16 @@ checksum = "a4f578a16ff264c1f306d38a10b900256b33f636426ebeb5eab980205e8fc9f5"
dependencies = [
"swc_atoms",
"swc_common",
"swc_ecma_ast 0.107.0",
"swc_ecma_transforms_base 0.130.1",
"swc_ecma_ast",
"swc_ecma_transforms_base",
"swc_ecma_transforms_compat",
"swc_ecma_transforms_module",
"swc_ecma_transforms_optimization",
"swc_ecma_transforms_proposal",
"swc_ecma_transforms_react",
"swc_ecma_transforms_typescript",
"swc_ecma_utils 0.120.1",
"swc_ecma_visit 0.93.0",
]
[[package]]
name = "swc_ecma_transforms_base"
version = "0.129.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7707e724db259cd93150fd5bc04559ace997edbce968d29be9c881e317c9b3fb"
dependencies = [
"better_scoped_tls",
"bitflags 2.3.3",
"indexmap 1.9.3",
"once_cell",
"phf",
"rustc-hash",
"serde",
"smallvec",
"swc_atoms",
"swc_common",
"swc_ecma_ast 0.106.6",
"swc_ecma_parser 0.136.8",
"swc_ecma_utils 0.119.10",
"swc_ecma_visit 0.92.5",
"tracing",
"swc_ecma_utils",
"swc_ecma_visit",
]
[[package]]
@@ -2208,10 +2123,10 @@ dependencies = [
"smallvec",
"swc_atoms",
"swc_common",
"swc_ecma_ast 0.107.0",
"swc_ecma_parser 0.137.1",
"swc_ecma_utils 0.120.1",
"swc_ecma_visit 0.93.0",
"swc_ecma_ast",
"swc_ecma_parser",
"swc_ecma_utils",
"swc_ecma_visit",
"tracing",
]
@@ -2223,10 +2138,10 @@ checksum = "a288e0d1a702f1944df7c1b57468f505a9549d7d7a51d89dfa18d8f956abdbf5"
dependencies = [
"swc_atoms",
"swc_common",
"swc_ecma_ast 0.107.0",
"swc_ecma_transforms_base 0.130.1",
"swc_ecma_utils 0.120.1",
"swc_ecma_visit 0.93.0",
"swc_ecma_ast",
"swc_ecma_transforms_base",
"swc_ecma_utils",
"swc_ecma_visit",
]
[[package]]
@@ -2245,12 +2160,12 @@ dependencies = [
"swc_atoms",
"swc_common",
"swc_config",
"swc_ecma_ast 0.107.0",
"swc_ecma_transforms_base 0.130.1",
"swc_ecma_ast",
"swc_ecma_transforms_base",
"swc_ecma_transforms_classes",
"swc_ecma_transforms_macros",
"swc_ecma_utils 0.120.1",
"swc_ecma_visit 0.93.0",
"swc_ecma_utils",
"swc_ecma_visit",
"swc_trace_macro",
"tracing",
]
@@ -2287,12 +2202,12 @@ dependencies = [
"swc_atoms",
"swc_cached",
"swc_common",
"swc_ecma_ast 0.107.0",
"swc_ecma_ast",
"swc_ecma_loader",
"swc_ecma_parser 0.137.1",
"swc_ecma_transforms_base 0.130.1",
"swc_ecma_utils 0.120.1",
"swc_ecma_visit 0.93.0",
"swc_ecma_parser",
"swc_ecma_transforms_base",
"swc_ecma_utils",
"swc_ecma_visit",
"tracing",
]
@@ -2311,12 +2226,12 @@ dependencies = [
"serde_json",
"swc_atoms",
"swc_common",
"swc_ecma_ast 0.107.0",
"swc_ecma_parser 0.137.1",
"swc_ecma_transforms_base 0.130.1",
"swc_ecma_ast",
"swc_ecma_parser",
"swc_ecma_transforms_base",
"swc_ecma_transforms_macros",
"swc_ecma_utils 0.120.1",
"swc_ecma_visit 0.93.0",
"swc_ecma_utils",
"swc_ecma_visit",
"swc_fast_graph",
"tracing",
]
@@ -2333,12 +2248,12 @@ dependencies = [
"smallvec",
"swc_atoms",
"swc_common",
"swc_ecma_ast 0.107.0",
"swc_ecma_transforms_base 0.130.1",
"swc_ecma_ast",
"swc_ecma_transforms_base",
"swc_ecma_transforms_classes",
"swc_ecma_transforms_macros",
"swc_ecma_utils 0.120.1",
"swc_ecma_visit 0.93.0",
"swc_ecma_utils",
"swc_ecma_visit",
]
[[package]]
@@ -2358,12 +2273,12 @@ dependencies = [
"swc_atoms",
"swc_common",
"swc_config",
"swc_ecma_ast 0.107.0",
"swc_ecma_parser 0.137.1",
"swc_ecma_transforms_base 0.130.1",
"swc_ecma_ast",
"swc_ecma_parser",
"swc_ecma_transforms_base",
"swc_ecma_transforms_macros",
"swc_ecma_utils 0.120.1",
"swc_ecma_visit 0.93.0",
"swc_ecma_utils",
"swc_ecma_visit",
]
[[package]]
@@ -2375,11 +2290,11 @@ dependencies = [
"serde",
"swc_atoms",
"swc_common",
"swc_ecma_ast 0.107.0",
"swc_ecma_transforms_base 0.130.1",
"swc_ecma_ast",
"swc_ecma_transforms_base",
"swc_ecma_transforms_react",
"swc_ecma_utils 0.120.1",
"swc_ecma_visit 0.93.0",
"swc_ecma_utils",
"swc_ecma_visit",
]
[[package]]
@@ -2393,31 +2308,13 @@ dependencies = [
"rustc-hash",
"swc_atoms",
"swc_common",
"swc_ecma_ast 0.107.0",
"swc_ecma_utils 0.120.1",
"swc_ecma_visit 0.93.0",
"swc_ecma_ast",
"swc_ecma_utils",
"swc_ecma_visit",
"swc_timer",
"tracing",
]
[[package]]
name = "swc_ecma_utils"
version = "0.119.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "452c66399edeb88a97bfdc3bbf11e45db85fdf883bfd4fc8bdd93abb92152b9b"
dependencies = [
"indexmap 1.9.3",
"num_cpus",
"once_cell",
"rustc-hash",
"swc_atoms",
"swc_common",
"swc_ecma_ast 0.106.6",
"swc_ecma_visit 0.92.5",
"tracing",
"unicode-id",
]
[[package]]
name = "swc_ecma_utils"
version = "0.120.1"
@@ -2430,26 +2327,12 @@ dependencies = [
"rustc-hash",
"swc_atoms",
"swc_common",
"swc_ecma_ast 0.107.0",
"swc_ecma_visit 0.93.0",
"swc_ecma_ast",
"swc_ecma_visit",
"tracing",
"unicode-id",
]
[[package]]
name = "swc_ecma_visit"
version = "0.92.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f61da6cac0ec3b7e62d367cfbd9e38e078a4601271891ad94f0dac5ff69f839"
dependencies = [
"num-bigint",
"swc_atoms",
"swc_common",
"swc_ecma_ast 0.106.6",
"swc_visit",
"tracing",
]
[[package]]
name = "swc_ecma_visit"
version = "0.93.0"
@@ -2459,7 +2342,7 @@ dependencies = [
"num-bigint",
"swc_atoms",
"swc_common",
"swc_ecma_ast 0.107.0",
"swc_ecma_ast",
"swc_visit",
"tracing",
]
@@ -2591,15 +2474,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "termcolor"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
dependencies = [
"winapi-util",
]
[[package]]
name = "terminal_size"
version = "0.1.17"
@@ -2802,14 +2676,6 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "utils"
version = "0.1.0"
dependencies = [
"bumpalo",
"stacker",
]
[[package]]
name = "vergen"
version = "7.5.1"
+49 -12
View File
@@ -1,17 +1,54 @@
[workspace]
resolver = "2"
members = [
"crates/build-hir",
"crates/fixtures",
"crates/hir",
"crates/hir-ssa",
"crates/hir-optimization",
"crates/swc-demo",
"crates/estree",
"crates/estree-codegen",
"crates/estree-swc",
"crates/utils"
]
members = ["crates/*"]
[workspace.package]
authors = ["The React Team https://react.dev/community/team"]
description = "React Forget Compiler"
edition = "2021"
homepage = "https://github.com/facebook/react-forget"
keywords = ["JavaScript", "TypeScript", "React", "React Forget", "Forget", "Compiler"]
license = "MIT"
repository = "https://github.com/facebook/react-forget"
[workspace.dependencies]
# workspace crates
forget_build_hir = { path = "crates/forget_build_hir" }
forget_estree = { path = "crates/forget_estree" }
forget_estree_codegen = { path = "crates/forget_estree_codegen" }
forget_estree_swc = { path = "crates/forget_estree_swc" }
forget_fixtures = { path = "crates/forget_fixtures" }
forget_hir = { path = "crates/forget_hir" }
forget_optimization = { path = "crates/forget_optimization" }
forget_ssa = { path = "crates/forget_ssa" }
forget_swc_demo = { path = "crates/forget_swc_demo" }
forget_utils = { path = "crates/forget_utils" }
# dependencies
bumpalo = { version = "3.13.0", features = ["boxed", "collections"] }
indexmap = { version = "2.0.0", features = ["serde"] }
insta = { version = "1.30.0", features = ["glob"] }
miette = { version = "5.9.0" }
prettyplease = "0.2.10"
quote = "1.0.29"
serde = { version = "1.0.167", features = ["serde_derive"] }
serde_json = "1.0.100"
stacker = "0.1.15"
static_assertions = "1.1.0"
swc = "0.264.8"
swc_core = { version = "0.79.9", features = ["swc_ecma_visit", "__ecma_transforms", "swc_ecma_ast", "swc_common", "swc", "swc_ecma_parser", "__ecma", "__common", "ecma_ast", "__visit", "__parser"] }
syn = "2.0.23"
thiserror = "1.0.41"
[profile.release]
# configuration adapted from oxc
# https://github.com/Boshen/oxc/blob/ea85ee9f2d64dd284c5b7410f491d81fb879abae/Cargo.toml#L89-L97
opt-level = 3
lto = "fat"
codegen-units = 1
strip = "symbols"
debug = false
panic = "abort" # Let it crash and force ourselves to write safe Rust.
# Make insta run faster by compiling with release mode optimizations
# https://docs.rs/insta/latest/insta/#optional-faster-runs
@@ -1,14 +0,0 @@
[package]
name = "build-hir"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
hir = { path = "../hir" }
estree = { path = "../estree" }
indexmap = "2.0.0"
bumpalo = "3.13.0"
miette = { version = "5.9.0" }
thiserror = "1.0.41"
@@ -1,14 +0,0 @@
[package]
name = "estree-codegen"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
indexmap = { version = "2.0.0", features = ["serde"] }
prettyplease = "0.2.10"
quote = "1.0.29"
serde = { version = "1.0.167", features = ["serde_derive"] }
serde_json = "1.0.100"
syn = "2.0.23"
@@ -1,11 +0,0 @@
[package]
name = "estree-swc"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
estree = { path = "../estree" }
swc = "0.264.8"
swc_core = { version = "0.79.9", features = ["swc_ecma_visit", "__ecma_transforms", "swc_ecma_ast", "swc_common", "swc", "swc_ecma_parser", "__ecma", "__common", "ecma_ast", "__visit", "__parser"] }
@@ -1,522 +0,0 @@
use std::{io::stderr, num::NonZeroU32, sync::Arc};
use estree::{Binding, BindingId};
use swc::Compiler;
use swc_core::common::errors::Handler;
use swc_core::common::source_map::Pos;
use swc_core::common::{FileName, FilePathMapping, Mark, SourceMap, Span, SyntaxContext, GLOBALS};
use swc_core::ecma::ast::{
AssignOp, BinaryOp, BlockStmt, Decl, EsVersion, Expr, Function, Ident, Lit, MemberExpr,
MemberProp, ModuleItem, Pat, PatOrExpr, Program, Stmt, UnaryOp, VarDecl, VarDeclKind,
VarDeclOrExpr,
};
use swc_core::ecma::parser::Syntax;
use swc_core::ecma::transforms::base::resolver;
use swc_core::ecma::visit::FoldWith;
/// Parses source text into an estree::Program via SWC, internally performing the parsing
/// and SWC -> ESTree conversion.
pub fn parse(source: &str, file: &str) -> Result<estree::Program, Box<dyn std::error::Error>> {
GLOBALS.set(&Default::default(), || {
let cm = Arc::new(SourceMap::new(FilePathMapping::empty()));
let c = Compiler::new(cm);
let fm =
c.cm.new_source_file(FileName::Real(file.into()), source.to_string());
let handler = Handler::with_emitter_writer(Box::new(stderr()), Some(c.cm.clone()));
let comments = c.comments().clone();
let module = c.parse_js(
fm.clone(),
&handler,
EsVersion::Es5,
Syntax::Typescript(Default::default()),
swc::config::IsModule::Bool(true),
Some(&comments),
)?;
let context = Context {
top_level_mark: Mark::new(),
unresolved_mark: Mark::new(),
};
let module = c.run_transform(&handler, false, || {
module.fold_with(&mut resolver(
context.unresolved_mark,
context.top_level_mark,
true,
))
});
Ok(convert_program(&context, &module))
})
}
#[derive(Debug)]
struct Context {
unresolved_mark: Mark,
top_level_mark: Mark,
}
fn convert_program(cx: &Context, program: &Program) -> estree::Program {
let mut program_items: Vec<estree::ModuleItem>;
match program {
Program::Module(program) => {
let body = &program.body;
program_items = Vec::with_capacity(body.len());
for item in body {
program_items.push(convert_module_item(cx, item));
}
}
Program::Script(program) => {
let body = &program.body;
program_items = Vec::with_capacity(body.len());
for item in body {
program_items.push(estree::ModuleItem::Statement(convert_statement(cx, item)));
}
}
};
estree::Program {
source_type: if program.is_script() {
estree::SourceType::Script
} else {
estree::SourceType::Module
},
body: program_items,
// comments: None,
loc: None,
range: None,
}
}
fn convert_module_item(cx: &Context, item: &ModuleItem) -> estree::ModuleItem {
match item {
ModuleItem::Stmt(item) => estree::ModuleItem::Statement(convert_statement(cx, item)),
_ => todo!("translate module item {:#?}", item),
}
}
fn convert_span(span: &Span) -> Option<estree::SourceRange> {
Some(estree::SourceRange {
start: span.lo().to_u32(),
end: NonZeroU32::new(span.hi().to_u32())?,
})
}
fn convert_decl_kind(kind: &VarDeclKind) -> estree::VariableDeclarationKind {
match kind {
VarDeclKind::Const => estree::VariableDeclarationKind::Const,
VarDeclKind::Let => estree::VariableDeclarationKind::Let,
VarDeclKind::Var => estree::VariableDeclarationKind::Var,
}
}
fn convert_block_statement(cx: &Context, stmt: &BlockStmt) -> estree::BlockStatement {
let mut body: Vec<estree::Statement> = Vec::with_capacity(stmt.stmts.len());
for stmt in &stmt.stmts {
body.push(convert_statement(cx, stmt));
}
estree::BlockStatement {
body,
loc: None,
range: convert_span(&stmt.span),
}
}
fn convert_function(cx: &Context, id: Option<&Ident>, fun: &Function) -> estree::Function {
estree::Function {
id: id.map(|id| estree::Identifier {
name: id.sym.to_string(),
binding: convert_binding(cx, id.span.ctxt),
loc: None,
range: convert_span(&id.span),
}),
params: fun
.params
.iter()
.map(|param| convert_pattern(cx, &param.pat))
.collect(),
body: fun.body.as_ref().map(|body| {
estree::FunctionBody::BlockStatement(Box::new(convert_block_statement(cx, body)))
}),
is_async: fun.is_async,
is_generator: fun.is_generator,
loc: None,
range: convert_span(&fun.span),
}
}
fn convert_statement(cx: &Context, stmt: &Stmt) -> estree::Statement {
match stmt {
Stmt::Decl(Decl::Fn(item)) => {
estree::Statement::FunctionDeclaration(Box::new(estree::FunctionDeclaration {
function: convert_function(cx, Some(&item.ident), &item.function),
loc: None,
range: convert_span(&item.function.span),
}))
}
Stmt::Decl(Decl::Var(item)) => {
estree::Statement::VariableDeclaration(Box::new(convert_variable_declaration(cx, item)))
}
Stmt::Block(item) => {
estree::Statement::BlockStatement(Box::new(convert_block_statement(cx, item)))
}
Stmt::Break(item) => estree::Statement::BreakStatement(Box::new(estree::BreakStatement {
label: item
.label
.as_ref()
.map(|label| convert_identifier(cx, label)),
loc: None,
range: convert_span(&item.span),
})),
Stmt::Continue(item) => {
estree::Statement::ContinueStatement(Box::new(estree::ContinueStatement {
label: item
.label
.as_ref()
.map(|label| convert_identifier(cx, label)),
loc: None,
range: convert_span(&item.span),
}))
}
Stmt::Debugger(item) => {
estree::Statement::DebuggerStatement(Box::new(estree::DebuggerStatement {
loc: None,
range: convert_span(&item.span),
}))
}
Stmt::DoWhile(item) => {
estree::Statement::DoWhileStatement(Box::new(estree::DoWhileStatement {
body: convert_statement(cx, &item.body),
test: convert_expression(cx, &item.test),
loc: None,
range: convert_span(&item.span),
}))
}
Stmt::Empty(item) => estree::Statement::EmptyStatement(Box::new(estree::EmptyStatement {
loc: None,
range: convert_span(&item.span),
})),
Stmt::Expr(item) => {
let expression = convert_expression(cx, &item.expr);
estree::Statement::ExpressionStatement(Box::new(estree::ExpressionStatement {
expression,
directive: None,
loc: None,
range: convert_span(&item.span),
}))
}
Stmt::For(item) => estree::Statement::ForStatement(Box::new(estree::ForStatement {
init: item.init.as_ref().map(|init| match init {
VarDeclOrExpr::Expr(init) => {
estree::ForInit::Expression(convert_expression(cx, init))
}
VarDeclOrExpr::VarDecl(init) => {
assert_eq!(init.decls.len(), 1);
estree::ForInit::VariableDeclaration(Box::new(convert_variable_declaration(
cx, init,
)))
}
}),
test: item.test.as_ref().map(|test| convert_expression(cx, test)),
update: item
.update
.as_ref()
.map(|update| convert_expression(cx, update)),
body: convert_statement(cx, &item.body),
loc: None,
range: convert_span(&item.span),
})),
Stmt::Return(item) => {
estree::Statement::ReturnStatement(Box::new(estree::ReturnStatement {
argument: item.arg.as_ref().map(|arg| convert_expression(cx, arg)),
loc: None,
range: convert_span(&item.span),
}))
}
Stmt::Throw(item) => estree::Statement::ThrowStatement(Box::new(estree::ThrowStatement {
argument: convert_expression(cx, &item.arg),
loc: None,
range: convert_span(&item.span),
})),
Stmt::If(item) => estree::Statement::IfStatement(Box::new(estree::IfStatement {
test: convert_expression(cx, &item.test),
consequent: convert_statement(cx, &item.cons),
alternate: item.alt.as_ref().map(|alt| convert_statement(cx, alt)),
loc: None,
range: convert_span(&item.span),
})),
_ => todo!("translate statement {:#?}", stmt),
}
}
fn convert_variable_declaration(cx: &Context, decl: &VarDecl) -> estree::VariableDeclaration {
estree::VariableDeclaration {
kind: convert_decl_kind(&decl.kind),
declarations: decl
.decls
.iter()
.map(|declarator| estree::VariableDeclarator {
id: convert_pattern(cx, &declarator.name),
init: declarator
.init
.as_ref()
.map(|init| convert_expression(cx, init)),
loc: None,
range: convert_span(&decl.span),
})
.collect(),
loc: None,
range: convert_span(&decl.span),
}
}
fn convert_expression(cx: &Context, expr: &Expr) -> estree::Expression {
match expr {
Expr::Ident(expr) => estree::Expression::Identifier(Box::new(convert_identifier(cx, expr))),
Expr::Array(expr) => {
estree::Expression::ArrayExpression(Box::new(estree::ArrayExpression {
elements: expr
.elems
.iter()
.map(|item| {
// TODO: represent holes in array expressions
let value = item.as_ref()?;
match value.spread {
Some(spread) => Some(estree::ExpressionOrSpread::SpreadElement(
Box::new(estree::SpreadElement {
argument: convert_expression(cx, &value.expr),
loc: None,
range: convert_span(&spread),
}),
)),
None => Some(estree::ExpressionOrSpread::Expression(
convert_expression(cx, &value.expr),
)),
}
})
.collect(),
loc: None,
range: convert_span(&expr.span),
}))
}
Expr::Await(_expr) => {
// estree::Expression::AwaitExpression(Box::new(estree::AwaitExpression {
// argument: convert_expression(cx, &expr.arg),
// loc: None,
// range: convert_span(&expr.span),
// }))
todo!("await expression")
}
Expr::Unary(expr) => {
estree::Expression::UnaryExpression(Box::new(estree::UnaryExpression {
operator: convert_unary_operator(expr.op),
prefix: false,
argument: convert_expression(cx, &expr.arg),
loc: None,
range: convert_span(&expr.span),
}))
}
Expr::Bin(expr) => match convert_binary_operator(expr.op) {
Operator::Binary(op) => {
estree::Expression::BinaryExpression(Box::new(estree::BinaryExpression {
operator: op,
left: convert_expression(cx, &expr.left),
right: convert_expression(cx, &expr.right),
loc: None,
range: convert_span(&expr.span),
}))
}
Operator::Logical(op) => {
estree::Expression::LogicalExpression(Box::new(estree::LogicalExpression {
operator: op,
left: convert_expression(cx, &expr.left),
right: convert_expression(cx, &expr.right),
loc: None,
range: convert_span(&expr.span),
}))
}
},
Expr::Lit(expr) => {
let (value, range) = match expr {
Lit::Bool(expr) => (estree::JsValue::Bool(expr.value), convert_span(&expr.span)),
Lit::Num(expr) => (
estree::JsValue::Number(expr.value.into()),
convert_span(&expr.span),
),
Lit::Str(expr) => (
estree::JsValue::String(expr.value.to_string()),
convert_span(&expr.span),
),
Lit::Null(expr) => (estree::JsValue::Null, convert_span(&expr.span)),
_ => todo!(),
};
estree::Expression::Literal(Box::new(estree::Literal {
value,
raw: None,
loc: None,
regex: None,
range,
}))
}
Expr::Assign(expr) => {
estree::Expression::AssignmentExpression(Box::new(estree::AssignmentExpression {
operator: convert_assignment_operator(expr.op),
left: convert_assignment_target(cx, &expr.left),
right: convert_expression(cx, &expr.right),
loc: None,
range: convert_span(&expr.span),
}))
}
Expr::Member(expr) => {
estree::Expression::MemberExpression(Box::new(convert_member_expression(cx, expr)))
}
Expr::Fn(expr) => {
estree::Expression::FunctionExpression(Box::new(estree::FunctionExpression {
function: convert_function(cx, expr.ident.as_ref(), &expr.function),
loc: None,
range: convert_span(&expr.function.span),
}))
}
_ => todo!("translate expression {:#?}", expr),
}
}
fn convert_assignment_target(cx: &Context, target: &PatOrExpr) -> estree::AssignmentTarget {
match target {
PatOrExpr::Pat(target) => estree::AssignmentTarget::Pattern(convert_pattern(cx, target)),
PatOrExpr::Expr(target) => {
match target.as_ref() {
Expr::Member(target) => {
estree::AssignmentTarget::Expression(estree::Expression::MemberExpression(
Box::new(convert_member_expression(cx, target)),
))
}
Expr::Ident(target) => estree::AssignmentTarget::Pattern(
estree::Pattern::Identifier(Box::new(convert_identifier(cx, target))),
),
_ => {
panic!("Expected assignment target to be member expression or identifier, got {:#?}", target)
}
}
}
}
}
fn convert_member_expression(cx: &Context, expr: &MemberExpr) -> estree::MemberExpression {
let (is_computed, property) = match &expr.prop {
MemberProp::Ident(prop) => (
false,
estree::Expression::Identifier(Box::new(convert_identifier(cx, prop))),
),
MemberProp::Computed(prop) => (true, convert_expression(cx, &prop.expr)),
_ => {
panic!("PrivateName member expression properties are not supported")
}
};
estree::MemberExpression {
object: estree::ExpressionOrSuper::Expression(convert_expression(cx, &expr.obj)),
property,
computed: is_computed,
// optional: false, // TODO
loc: None,
range: convert_span(&expr.span),
}
}
fn convert_unary_operator(op: UnaryOp) -> estree::UnaryOperator {
match op {
UnaryOp::Bang => estree::UnaryOperator::Negation,
UnaryOp::Delete => estree::UnaryOperator::Delete,
UnaryOp::Minus => estree::UnaryOperator::Minus,
UnaryOp::Plus => estree::UnaryOperator::Plus,
UnaryOp::Tilde => estree::UnaryOperator::Tilde,
UnaryOp::TypeOf => estree::UnaryOperator::Typeof,
UnaryOp::Void => estree::UnaryOperator::Void,
}
}
fn convert_assignment_operator(op: AssignOp) -> estree::AssignmentOperator {
match op {
AssignOp::Assign => estree::AssignmentOperator::Equals,
AssignOp::AddAssign => estree::AssignmentOperator::PlusEquals,
_ => todo!("translate assignment operator"),
}
}
enum Operator {
Binary(estree::BinaryOperator),
Logical(estree::LogicalOperator),
}
fn convert_binary_operator(op: BinaryOp) -> Operator {
match op {
BinaryOp::Add => Operator::Binary(estree::BinaryOperator::Add),
BinaryOp::BitAnd => Operator::Binary(estree::BinaryOperator::BinaryAnd),
BinaryOp::BitOr => Operator::Binary(estree::BinaryOperator::BinaryOr),
BinaryOp::BitXor => Operator::Binary(estree::BinaryOperator::BinaryXor),
BinaryOp::Div => Operator::Binary(estree::BinaryOperator::Divide),
BinaryOp::EqEq => Operator::Binary(estree::BinaryOperator::Equals),
BinaryOp::EqEqEq => Operator::Binary(estree::BinaryOperator::StrictEquals),
// BinaryOp::Exp => Operator::Binary(estree::BinaryOperator::AsteriskAsterisk),
BinaryOp::Gt => Operator::Binary(estree::BinaryOperator::GreaterThan),
BinaryOp::GtEq => Operator::Binary(estree::BinaryOperator::GreaterThanOrEqual),
BinaryOp::In => Operator::Binary(estree::BinaryOperator::In),
BinaryOp::InstanceOf => Operator::Binary(estree::BinaryOperator::Instanceof),
BinaryOp::LShift => Operator::Binary(estree::BinaryOperator::ShiftLeft),
BinaryOp::Lt => Operator::Binary(estree::BinaryOperator::LessThan),
BinaryOp::LtEq => Operator::Binary(estree::BinaryOperator::LessThanOrEqual),
BinaryOp::Mod => Operator::Binary(estree::BinaryOperator::Modulo),
BinaryOp::Mul => Operator::Binary(estree::BinaryOperator::Multiply),
BinaryOp::NotEq => Operator::Binary(estree::BinaryOperator::NotEquals),
BinaryOp::NotEqEq => Operator::Binary(estree::BinaryOperator::NotStrictEquals),
BinaryOp::RShift => Operator::Binary(estree::BinaryOperator::ShiftRight),
BinaryOp::Sub => Operator::Binary(estree::BinaryOperator::Subtract),
BinaryOp::ZeroFillRShift => Operator::Binary(estree::BinaryOperator::UnsignedShiftRight),
BinaryOp::LogicalAnd => Operator::Logical(estree::LogicalOperator::And),
BinaryOp::LogicalOr => Operator::Logical(estree::LogicalOperator::Or),
BinaryOp::NullishCoalescing => Operator::Logical(estree::LogicalOperator::NullCoalescing),
_ => panic!("Unsupported binary operator `{}`", op),
}
}
fn convert_pattern(cx: &Context, pat: &Pat) -> estree::Pattern {
match pat {
Pat::Ident(pat) => estree::Pattern::Identifier(Box::new(estree::Identifier {
name: pat.id.sym.to_string(),
binding: convert_binding(cx, pat.id.span.ctxt),
loc: None,
range: convert_span(&pat.span),
})),
_ => todo!("translate pattern {:#?}", pat),
}
}
fn convert_binding(context: &Context, binding_cx: SyntaxContext) -> Option<Binding> {
let id = BindingId::new(binding_cx.as_u32());
if binding_cx.as_u32() == context.top_level_mark.as_u32() {
Some(Binding::Global)
} else if binding_cx.as_u32() == context.unresolved_mark.as_u32() {
Some(Binding::Module(id))
} else {
Some(Binding::Local(id))
}
}
fn convert_identifier(cx: &Context, identifier: &Ident) -> estree::Identifier {
let name = identifier.sym.as_ref().to_string();
estree::Identifier {
name,
binding: convert_binding(cx, identifier.span.ctxt),
loc: None,
range: convert_span(&identifier.span),
}
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {}
}
-17
View File
@@ -1,17 +0,0 @@
[package]
name = "estree"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bumpalo = { version = "3.13.0", features = ["boxed", "collections"] }
elsa = "1.8.1"
insta = { version = "1.30.0", features = ["glob"] }
serde = { version = "1.0.164", features = ["derive"] }
serde_json = "1.0.99"
static_assertions = "1.1.0"
[build-dependencies]
estree-codegen = { path = "../estree-codegen" }
-13
View File
@@ -1,13 +0,0 @@
use estree_codegen::estree;
// Example custom build script.
fn main() {
// Re-run if the codegen files change
println!("cargo:rerun-if-changed=../estree-codegen/src/codegen.rs");
println!("cargo:rerun-if-changed=../estree-codegen/src/lib.rs");
println!("cargo:rerun-if-changed=../estree-codegen/src/ecmascript.json");
println!("cargo:rerun-if-changed=../estree-codegen");
let src = estree();
std::fs::write("src/generated.rs", src).unwrap();
}
@@ -1,18 +0,0 @@
[package]
name = "fixtures"
version = "0.1.0"
edition = "2021"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
insta = "1.30.0"
estree = { path = "../estree" }
estree-swc = { path = "../estree-swc" }
hir = { path = "../hir" }
hir-optimization = { path = "../hir-optimization" }
hir-ssa = { path = "../hir-ssa" }
build-hir = { path = "../build-hir" }
bumpalo = { version = "3.13.0", features = ["collections"] }
miette = { version = "5.9.0", features = ["backtrace", "fancy"] }
@@ -0,0 +1,21 @@
[package]
name = "forget_build_hir"
version = "0.1.0"
publish = false
authors.workspace = true
description.workspace = true
edition.workspace = true
homepage.workspace = true
keywords.workspace = true
license.workspace = true
repository.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
forget_hir = { workspace = true }
forget_estree = { workspace = true}
indexmap = { workspace = true }
bumpalo = { workspace = true }
miette = { workspace = true }
thiserror = { workspace = true }
@@ -1,12 +1,12 @@
use std::collections::HashSet;
use bumpalo::{boxed::Box, collections::String};
use estree::{
use forget_estree::{
AssignmentTarget, BinaryExpression, BlockStatement, Expression, ForInit, ForStatement,
Function, FunctionExpression, IfStatement, JsValue, Literal, Pattern, Statement,
VariableDeclarationKind,
};
use hir::{
use forget_hir::{
ArrayElement, BlockKind, BranchTerminal, Environment, ForTerminal, GotoKind, IdentifierOperand,
InstrIx, InstructionKind, InstructionValue, LValue, LoadGlobal, LoadLocal, Operand,
PrimitiveValue, TerminalValue,
@@ -28,14 +28,14 @@ use crate::{
pub fn build<'a>(
env: &'a Environment<'a>,
fun: Function,
) -> Result<Box<'a, hir::Function<'a>>, BuildDiagnostic> {
) -> Result<Box<'a, forget_hir::Function<'a>>, BuildDiagnostic> {
let mut builder = Builder::new(env);
match fun.body {
Some(estree::FunctionBody::BlockStatement(body)) => {
Some(forget_estree::FunctionBody::BlockStatement(body)) => {
lower_block_statement(env, &mut builder, *body)?
}
Some(estree::FunctionBody::Expression(body)) => {
Some(forget_estree::FunctionBody::Expression(body)) => {
lower_expression(env, &mut builder, body)?;
}
None => {
@@ -65,21 +65,21 @@ pub fn build<'a>(
// In case the function did not explicitly return, terminate the final
// block with an explicit `return undefined`. If the function *did* return,
// this will be unreachable and get pruned later.
let implicit_return_value = builder.push(InstructionValue::Primitive(hir::Primitive {
let implicit_return_value = builder.push(InstructionValue::Primitive(forget_hir::Primitive {
value: PrimitiveValue::Undefined,
}));
builder.terminate(
TerminalValue::Return(hir::ReturnTerminal {
TerminalValue::Return(forget_hir::ReturnTerminal {
value: Operand {
ix: implicit_return_value,
effect: None,
},
}),
hir::BlockKind::Block,
forget_hir::BlockKind::Block,
);
let body = builder.build()?;
Ok(env.box_new(hir::Function {
Ok(env.box_new(forget_hir::Function {
id: fun
.id
.map(|id| String::from_str_in(&id.name, &env.allocator)),
@@ -118,7 +118,7 @@ fn lower_statement<'a>(
Statement::BreakStatement(stmt) => {
let block = builder.resolve_break(stmt.label.as_ref())?;
builder.terminate(
TerminalValue::Goto(hir::GotoTerminal {
TerminalValue::Goto(forget_hir::GotoTerminal {
block,
kind: GotoKind::Break,
}),
@@ -128,7 +128,7 @@ fn lower_statement<'a>(
Statement::ContinueStatement(stmt) => {
let block = builder.resolve_continue(stmt.label.as_ref())?;
builder.terminate(
TerminalValue::Goto(hir::GotoTerminal {
TerminalValue::Goto(forget_hir::GotoTerminal {
block,
kind: GotoKind::Continue,
}),
@@ -138,12 +138,12 @@ fn lower_statement<'a>(
Statement::ReturnStatement(stmt) => {
let ix = match stmt.argument {
Some(argument) => lower_expression(env, builder, argument)?,
None => builder.push(InstructionValue::Primitive(hir::Primitive {
None => builder.push(InstructionValue::Primitive(forget_hir::Primitive {
value: PrimitiveValue::Undefined,
})),
};
builder.terminate(
TerminalValue::Return(hir::ReturnTerminal {
TerminalValue::Return(forget_hir::ReturnTerminal {
value: Operand { ix, effect: None },
}),
BlockKind::Block,
@@ -191,7 +191,7 @@ fn lower_statement<'a>(
));
}
};
builder.push(InstructionValue::DeclareLocal(hir::DeclareLocal {
builder.push(InstructionValue::DeclareLocal(forget_hir::DeclareLocal {
lvalue: LValue {
identifier: IdentifierOperand {
identifier,
@@ -218,7 +218,7 @@ fn lower_statement<'a>(
let consequent_block = builder.enter(BlockKind::Block, |builder| {
lower_statement(env, builder, consequent, None)?;
Ok(TerminalValue::Goto(hir::GotoTerminal {
Ok(TerminalValue::Goto(forget_hir::GotoTerminal {
block: fallthrough_block.id,
kind: GotoKind::Break,
}))
@@ -228,14 +228,14 @@ fn lower_statement<'a>(
if let Some(alternate) = alternate {
lower_statement(env, builder, alternate, None)?;
}
Ok(TerminalValue::Goto(hir::GotoTerminal {
Ok(TerminalValue::Goto(forget_hir::GotoTerminal {
block: fallthrough_block.id,
kind: GotoKind::Break,
}))
})?;
let test = lower_expression(env, builder, test)?;
let terminal = TerminalValue::If(hir::IfTerminal {
let terminal = TerminalValue::If(forget_hir::IfTerminal {
test: Operand {
ix: test,
effect: None,
@@ -264,7 +264,7 @@ fn lower_statement<'a>(
let init_block = builder.enter(BlockKind::Loop, |builder| {
if let Some(ForInit::VariableDeclaration(decl)) = init {
lower_statement(env, builder, Statement::VariableDeclaration(decl), None)?;
Ok(TerminalValue::Goto(hir::GotoTerminal {
Ok(TerminalValue::Goto(forget_hir::GotoTerminal {
block: test_block.id,
kind: GotoKind::Break,
}))
@@ -281,7 +281,7 @@ fn lower_statement<'a>(
.map(|update| {
builder.enter(BlockKind::Loop, |builder| {
lower_expression(env, builder, update)?;
Ok(TerminalValue::Goto(hir::GotoTerminal {
Ok(TerminalValue::Goto(forget_hir::GotoTerminal {
block: test_block.id,
kind: GotoKind::Break,
}))
@@ -297,7 +297,7 @@ fn lower_statement<'a>(
};
builder.enter_loop(loop_, |builder| {
lower_statement(env, builder, body, None)?;
Ok(TerminalValue::Goto(hir::GotoTerminal {
Ok(TerminalValue::Goto(forget_hir::GotoTerminal {
block: update_block.unwrap_or(test_block.id),
kind: GotoKind::Continue,
}))
@@ -363,20 +363,20 @@ fn lower_expression<'a>(
}),
}
}
Expression::Literal(expr) => InstructionValue::Primitive(hir::Primitive {
Expression::Literal(expr) => InstructionValue::Primitive(forget_hir::Primitive {
value: lower_primitive(env, builder, *expr),
}),
Expression::ArrayExpression(expr) => {
let mut elements = env.vec_with_capacity(expr.elements.len());
for expr in expr.elements {
let element = match expr {
Some(estree::ExpressionOrSpread::SpreadElement(expr)) => {
Some(forget_estree::ExpressionOrSpread::SpreadElement(expr)) => {
Some(ArrayElement::Spread(Operand {
ix: lower_expression(env, builder, expr.argument)?,
effect: None,
}))
}
Some(estree::ExpressionOrSpread::Expression(expr)) => {
Some(forget_estree::ExpressionOrSpread::Expression(expr)) => {
Some(ArrayElement::Place(Operand {
ix: lower_expression(env, builder, expr)?,
effect: None,
@@ -386,11 +386,11 @@ fn lower_expression<'a>(
};
elements.push(element);
}
InstructionValue::Array(hir::Array { elements })
InstructionValue::Array(forget_hir::Array { elements })
}
Expression::AssignmentExpression(expr) => match expr.operator {
estree::AssignmentOperator::Equals => {
forget_estree::AssignmentOperator::Equals => {
let right = lower_expression(env, builder, expr.right)?;
return Ok(lower_assignment(
env,
@@ -412,7 +412,7 @@ fn lower_expression<'a>(
} = *expr;
let left = lower_expression(env, builder, left)?;
let right = lower_expression(env, builder, right)?;
InstructionValue::Binary(hir::Binary {
InstructionValue::Binary(forget_hir::Binary {
left: Operand {
ix: left,
effect: None,
@@ -438,7 +438,7 @@ fn lower_function<'a>(
env: &'a Environment<'a>,
builder: &mut Builder<'a>,
expr: FunctionExpression,
) -> Result<hir::FunctionExpression<'a>, BuildDiagnostic> {
) -> Result<forget_hir::FunctionExpression<'a>, BuildDiagnostic> {
let FunctionExpression { function, .. } = expr;
println!("get_context_identifiers() ...");
let context_identifiers = get_context_identifiers(env, &function);
@@ -461,7 +461,7 @@ fn lower_function<'a>(
}
let mut fun = build(env, function)?;
fun.context = context;
Ok(hir::FunctionExpression {
Ok(forget_hir::FunctionExpression {
// TODO: collect dependencies!
dependencies: env.vec_new(),
lowered_function: fun,
@@ -479,7 +479,7 @@ fn lower_assignment<'a>(
AssignmentTarget::Pattern(lvalue) => match lvalue {
Pattern::Identifier(lvalue) => {
let identifier = lower_identifier_for_assignment(env, builder, kind, *lvalue)?;
builder.push(InstructionValue::StoreLocal(hir::StoreLocal {
builder.push(InstructionValue::StoreLocal(forget_hir::StoreLocal {
lvalue: LValue { identifier, kind },
value: Operand {
ix: value,
@@ -497,7 +497,7 @@ fn lower_identifier_for_assignment<'a>(
_env: &'a Environment<'a>,
builder: &mut Builder<'a>,
_kind: InstructionKind,
identifier: estree::Identifier,
identifier: forget_estree::Identifier,
) -> Result<IdentifierOperand<'a>, BuildDiagnostic> {
let binding = builder.resolve_identifier(&identifier)?;
match binding {
@@ -4,7 +4,7 @@ use bumpalo::{
};
use std::{cell::RefCell, collections::HashSet, rc::Rc};
use hir::{
use forget_hir::{
BasicBlock, BlockId, BlockKind, Environment, GotoKind, Identifier, IdentifierData, InstrIx,
Instruction, InstructionIdGenerator, InstructionValue, Terminal, TerminalValue, Type, HIR,
};
@@ -197,7 +197,7 @@ impl<'a> Builder<'a> {
Err(error) => (
Err(error),
// TODO: add a `Terminal::Error` variant
TerminalValue::Goto(hir::GotoTerminal {
TerminalValue::Goto(forget_hir::GotoTerminal {
block: current.id,
kind: GotoKind::Break,
}),
@@ -242,8 +242,8 @@ impl<'a> Builder<'a> {
/// we synthesize a temporary identifier to store the possibly-missing value
/// into, and emit a later StoreLocal for the original identifier
#[allow(dead_code)]
pub(crate) fn make_temporary(&self) -> hir::Identifier<'a> {
hir::Identifier {
pub(crate) fn make_temporary(&self) -> forget_hir::Identifier<'a> {
forget_hir::Identifier {
id: self.environment.next_identifier_id(),
name: None,
data: Rc::new(RefCell::new(IdentifierData {
@@ -259,7 +259,7 @@ impl<'a> Builder<'a> {
/// provided but cannot be resolved.
pub(crate) fn resolve_break(
&self,
label: Option<&estree::Identifier>,
label: Option<&forget_estree::Identifier>,
) -> Result<BlockId, BuildDiagnostic> {
for scope in self.scopes.iter().rev() {
match (label, scope.label()) {
@@ -285,7 +285,7 @@ impl<'a> Builder<'a> {
/// provided but cannot be resolved.
pub(crate) fn resolve_continue(
&self,
label: Option<&estree::Identifier>,
label: Option<&forget_estree::Identifier>,
) -> Result<BlockId, BuildDiagnostic> {
for scope in self.scopes.iter().rev() {
match scope {
@@ -327,16 +327,16 @@ impl<'a> Builder<'a> {
pub(crate) fn resolve_identifier(
&mut self,
identifier: &estree::Identifier,
identifier: &forget_estree::Identifier,
) -> Result<Binding<'a>, BuildDiagnostic> {
match &identifier.binding {
Some(binding) => Ok(match binding {
estree::Binding::Global => Binding::Global,
estree::Binding::Local(id) => Binding::Local(
forget_estree::Binding::Global => Binding::Global,
forget_estree::Binding::Local(id) => Binding::Local(
self.environment
.resolve_binding_identifier(&identifier.name, *id),
),
estree::Binding::Module(id) => Binding::Module(
forget_estree::Binding::Module(id) => Binding::Module(
self.environment
.resolve_binding_identifier(&identifier.name, *id),
),
@@ -454,7 +454,7 @@ pub fn remove_unreachable_do_while_statements<'a>(hir: &mut HIR<'a>) {
for block in hir.blocks.values_mut() {
if let TerminalValue::DoWhile(terminal) = &mut block.terminal.value {
if !block_ids.contains(&terminal.test) {
block.terminal.value = TerminalValue::Goto(hir::GotoTerminal {
block.terminal.value = TerminalValue::Goto(forget_hir::GotoTerminal {
block: terminal.body,
kind: GotoKind::Break,
});
@@ -1,7 +1,7 @@
use std::collections::HashSet;
use estree::{Binding, BindingId, Expression, ExpressionOrSuper, Function, Identifier, Visitor};
use hir::Environment;
use forget_estree::{Binding, BindingId, Function, Identifier, Visitor};
use forget_hir::Environment;
pub(crate) fn get_context_identifiers<'a, 'ast>(
_env: &'a Environment<'a>,
@@ -1,5 +1,5 @@
use estree::SourceRange;
use hir::BlockId;
use forget_estree::SourceRange;
use forget_hir::BlockId;
use miette::{ByteOffset, Diagnostic, SourceSpan};
use thiserror::Error;
@@ -0,0 +1,23 @@
[package]
name = "forget_estree"
version = "0.1.0"
publish = false
authors.workspace = true
description.workspace = true
edition.workspace = true
homepage.workspace = true
keywords.workspace = true
license.workspace = true
repository.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bumpalo = { workspace = true }
insta = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
static_assertions = { workspace = true }
[build-dependencies]
forget_estree_codegen = { workspace = true }
@@ -0,0 +1,13 @@
use forget_estree_codegen::estree;
// Example custom build script.
fn main() {
// Re-run if the codegen files change
println!("cargo:rerun-if-changed=../forget_estree_codegen/src/codegen.rs");
println!("cargo:rerun-if-changed=../forget_estree_codegen/src/lib.rs");
println!("cargo:rerun-if-changed=../forget_estree_codegen/src/ecmascript.json");
println!("cargo:rerun-if-changed=../forget_estree_codegen");
let src = estree();
std::fs::write("src/generated.rs", src).unwrap();
}
@@ -1,7 +1,7 @@
---
source: crates/estree/src/lib.rs
source: crates/forget_estree/src/lib.rs
expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{serialized}\")"
input_file: crates/estree/src/fixtures/for-statement.json
input_file: crates/forget_estree/src/fixtures/for-statement.json
---
Input:
{
@@ -1,7 +1,7 @@
---
source: crates/estree/src/lib.rs
source: crates/forget_estree/src/lib.rs
expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{serialized}\")"
input_file: crates/estree/src/fixtures/import.json
input_file: crates/forget_estree/src/fixtures/import.json
---
Input:
{
@@ -1,7 +1,7 @@
---
source: crates/estree/src/lib.rs
source: crates/forget_estree/src/lib.rs
expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{serialized}\")"
input_file: crates/estree/src/fixtures/simple.json
input_file: crates/forget_estree/src/fixtures/simple.json
---
Input:
{
@@ -1,7 +1,7 @@
---
source: crates/estree/src/lib.rs
source: crates/forget_estree/src/lib.rs
expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{serialized}\")"
input_file: crates/estree/src/fixtures/test.json
input_file: crates/forget_estree/src/fixtures/test.json
---
Input:
{
@@ -0,0 +1,21 @@
[package]
name = "forget_estree_codegen"
version = "0.1.0"
publish = false
authors.workspace = true
description.workspace = true
edition.workspace = true
homepage.workspace = true
keywords.workspace = true
license.workspace = true
repository.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
indexmap = { workspace = true }
prettyplease = { workspace = true }
quote = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
syn = { workspace = true }
@@ -0,0 +1,18 @@
[package]
name = "forget_estree_swc"
version = "0.1.0"
publish = false
authors.workspace = true
description.workspace = true
edition.workspace = true
homepage.workspace = true
keywords.workspace = true
license.workspace = true
repository.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
forget_estree = { workspace = true }
swc = { workspace = true }
swc_core = { workspace = true }
@@ -0,0 +1,555 @@
use std::{io::stderr, num::NonZeroU32, sync::Arc};
use forget_estree::{Binding, BindingId};
use swc::Compiler;
use swc_core::common::errors::Handler;
use swc_core::common::source_map::Pos;
use swc_core::common::{FileName, FilePathMapping, Mark, SourceMap, Span, SyntaxContext, GLOBALS};
use swc_core::ecma::ast::{
AssignOp, BinaryOp, BlockStmt, Decl, EsVersion, Expr, Function, Ident, Lit, MemberExpr,
MemberProp, ModuleItem, Pat, PatOrExpr, Program, Stmt, UnaryOp, VarDecl, VarDeclKind,
VarDeclOrExpr,
};
use swc_core::ecma::parser::Syntax;
use swc_core::ecma::transforms::base::resolver;
use swc_core::ecma::visit::FoldWith;
/// Parses source text into an forget_estree::Program via SWC, internally performing the parsing
/// and SWC -> ESTree conversion.
pub fn parse(
source: &str,
file: &str,
) -> Result<forget_estree::Program, Box<dyn std::error::Error>> {
GLOBALS.set(&Default::default(), || {
let cm = Arc::new(SourceMap::new(FilePathMapping::empty()));
let c = Compiler::new(cm);
let fm =
c.cm.new_source_file(FileName::Real(file.into()), source.to_string());
let handler = Handler::with_emitter_writer(Box::new(stderr()), Some(c.cm.clone()));
let comments = c.comments().clone();
let module = c.parse_js(
fm.clone(),
&handler,
EsVersion::Es5,
Syntax::Typescript(Default::default()),
swc::config::IsModule::Bool(true),
Some(&comments),
)?;
let context = Context {
top_level_mark: Mark::new(),
unresolved_mark: Mark::new(),
};
let module = c.run_transform(&handler, false, || {
module.fold_with(&mut resolver(
context.unresolved_mark,
context.top_level_mark,
true,
))
});
Ok(convert_program(&context, &module))
})
}
#[derive(Debug)]
struct Context {
unresolved_mark: Mark,
top_level_mark: Mark,
}
fn convert_program(cx: &Context, program: &Program) -> forget_estree::Program {
let mut program_items: Vec<forget_estree::ModuleItem>;
match program {
Program::Module(program) => {
let body = &program.body;
program_items = Vec::with_capacity(body.len());
for item in body {
program_items.push(convert_module_item(cx, item));
}
}
Program::Script(program) => {
let body = &program.body;
program_items = Vec::with_capacity(body.len());
for item in body {
program_items.push(forget_estree::ModuleItem::Statement(convert_statement(
cx, item,
)));
}
}
};
forget_estree::Program {
source_type: if program.is_script() {
forget_estree::SourceType::Script
} else {
forget_estree::SourceType::Module
},
body: program_items,
// comments: None,
loc: None,
range: None,
}
}
fn convert_module_item(cx: &Context, item: &ModuleItem) -> forget_estree::ModuleItem {
match item {
ModuleItem::Stmt(item) => forget_estree::ModuleItem::Statement(convert_statement(cx, item)),
_ => todo!("translate module item {:#?}", item),
}
}
fn convert_span(span: &Span) -> Option<forget_estree::SourceRange> {
Some(forget_estree::SourceRange {
start: span.lo().to_u32(),
end: NonZeroU32::new(span.hi().to_u32())?,
})
}
fn convert_decl_kind(kind: &VarDeclKind) -> forget_estree::VariableDeclarationKind {
match kind {
VarDeclKind::Const => forget_estree::VariableDeclarationKind::Const,
VarDeclKind::Let => forget_estree::VariableDeclarationKind::Let,
VarDeclKind::Var => forget_estree::VariableDeclarationKind::Var,
}
}
fn convert_block_statement(cx: &Context, stmt: &BlockStmt) -> forget_estree::BlockStatement {
let mut body: Vec<forget_estree::Statement> = Vec::with_capacity(stmt.stmts.len());
for stmt in &stmt.stmts {
body.push(convert_statement(cx, stmt));
}
forget_estree::BlockStatement {
body,
loc: None,
range: convert_span(&stmt.span),
}
}
fn convert_function(cx: &Context, id: Option<&Ident>, fun: &Function) -> forget_estree::Function {
forget_estree::Function {
id: id.map(|id| forget_estree::Identifier {
name: id.sym.to_string(),
binding: convert_binding(cx, id.span.ctxt),
loc: None,
range: convert_span(&id.span),
}),
params: fun
.params
.iter()
.map(|param| convert_pattern(cx, &param.pat))
.collect(),
body: fun.body.as_ref().map(|body| {
forget_estree::FunctionBody::BlockStatement(Box::new(convert_block_statement(cx, body)))
}),
is_async: fun.is_async,
is_generator: fun.is_generator,
loc: None,
range: convert_span(&fun.span),
}
}
fn convert_statement(cx: &Context, stmt: &Stmt) -> forget_estree::Statement {
match stmt {
Stmt::Decl(Decl::Fn(item)) => forget_estree::Statement::FunctionDeclaration(Box::new(
forget_estree::FunctionDeclaration {
function: convert_function(cx, Some(&item.ident), &item.function),
loc: None,
range: convert_span(&item.function.span),
},
)),
Stmt::Decl(Decl::Var(item)) => forget_estree::Statement::VariableDeclaration(Box::new(
convert_variable_declaration(cx, item),
)),
Stmt::Block(item) => {
forget_estree::Statement::BlockStatement(Box::new(convert_block_statement(cx, item)))
}
Stmt::Break(item) => {
forget_estree::Statement::BreakStatement(Box::new(forget_estree::BreakStatement {
label: item
.label
.as_ref()
.map(|label| convert_identifier(cx, label)),
loc: None,
range: convert_span(&item.span),
}))
}
Stmt::Continue(item) => forget_estree::Statement::ContinueStatement(Box::new(
forget_estree::ContinueStatement {
label: item
.label
.as_ref()
.map(|label| convert_identifier(cx, label)),
loc: None,
range: convert_span(&item.span),
},
)),
Stmt::Debugger(item) => forget_estree::Statement::DebuggerStatement(Box::new(
forget_estree::DebuggerStatement {
loc: None,
range: convert_span(&item.span),
},
)),
Stmt::DoWhile(item) => {
forget_estree::Statement::DoWhileStatement(Box::new(forget_estree::DoWhileStatement {
body: convert_statement(cx, &item.body),
test: convert_expression(cx, &item.test),
loc: None,
range: convert_span(&item.span),
}))
}
Stmt::Empty(item) => {
forget_estree::Statement::EmptyStatement(Box::new(forget_estree::EmptyStatement {
loc: None,
range: convert_span(&item.span),
}))
}
Stmt::Expr(item) => {
let expression = convert_expression(cx, &item.expr);
forget_estree::Statement::ExpressionStatement(Box::new(
forget_estree::ExpressionStatement {
expression,
directive: None,
loc: None,
range: convert_span(&item.span),
},
))
}
Stmt::For(item) => {
forget_estree::Statement::ForStatement(Box::new(forget_estree::ForStatement {
init: item.init.as_ref().map(|init| match init {
VarDeclOrExpr::Expr(init) => {
forget_estree::ForInit::Expression(convert_expression(cx, init))
}
VarDeclOrExpr::VarDecl(init) => {
assert_eq!(init.decls.len(), 1);
forget_estree::ForInit::VariableDeclaration(Box::new(
convert_variable_declaration(cx, init),
))
}
}),
test: item.test.as_ref().map(|test| convert_expression(cx, test)),
update: item
.update
.as_ref()
.map(|update| convert_expression(cx, update)),
body: convert_statement(cx, &item.body),
loc: None,
range: convert_span(&item.span),
}))
}
Stmt::Return(item) => {
forget_estree::Statement::ReturnStatement(Box::new(forget_estree::ReturnStatement {
argument: item.arg.as_ref().map(|arg| convert_expression(cx, arg)),
loc: None,
range: convert_span(&item.span),
}))
}
Stmt::Throw(item) => {
forget_estree::Statement::ThrowStatement(Box::new(forget_estree::ThrowStatement {
argument: convert_expression(cx, &item.arg),
loc: None,
range: convert_span(&item.span),
}))
}
Stmt::If(item) => {
forget_estree::Statement::IfStatement(Box::new(forget_estree::IfStatement {
test: convert_expression(cx, &item.test),
consequent: convert_statement(cx, &item.cons),
alternate: item.alt.as_ref().map(|alt| convert_statement(cx, alt)),
loc: None,
range: convert_span(&item.span),
}))
}
_ => todo!("translate statement {:#?}", stmt),
}
}
fn convert_variable_declaration(
cx: &Context,
decl: &VarDecl,
) -> forget_estree::VariableDeclaration {
forget_estree::VariableDeclaration {
kind: convert_decl_kind(&decl.kind),
declarations: decl
.decls
.iter()
.map(|declarator| forget_estree::VariableDeclarator {
id: convert_pattern(cx, &declarator.name),
init: declarator
.init
.as_ref()
.map(|init| convert_expression(cx, init)),
loc: None,
range: convert_span(&decl.span),
})
.collect(),
loc: None,
range: convert_span(&decl.span),
}
}
fn convert_expression(cx: &Context, expr: &Expr) -> forget_estree::Expression {
match expr {
Expr::Ident(expr) => {
forget_estree::Expression::Identifier(Box::new(convert_identifier(cx, expr)))
}
Expr::Array(expr) => {
forget_estree::Expression::ArrayExpression(Box::new(forget_estree::ArrayExpression {
elements: expr
.elems
.iter()
.map(|item| {
// TODO: represent holes in array expressions
let value = item.as_ref()?;
match value.spread {
Some(spread) => Some(forget_estree::ExpressionOrSpread::SpreadElement(
Box::new(forget_estree::SpreadElement {
argument: convert_expression(cx, &value.expr),
loc: None,
range: convert_span(&spread),
}),
)),
None => Some(forget_estree::ExpressionOrSpread::Expression(
convert_expression(cx, &value.expr),
)),
}
})
.collect(),
loc: None,
range: convert_span(&expr.span),
}))
}
Expr::Await(_expr) => {
// forget_estree::Expression::AwaitExpression(Box::new(forget_estree::AwaitExpression {
// argument: convert_expression(cx, &expr.arg),
// loc: None,
// range: convert_span(&expr.span),
// }))
todo!("await expression")
}
Expr::Unary(expr) => {
forget_estree::Expression::UnaryExpression(Box::new(forget_estree::UnaryExpression {
operator: convert_unary_operator(expr.op),
prefix: false,
argument: convert_expression(cx, &expr.arg),
loc: None,
range: convert_span(&expr.span),
}))
}
Expr::Bin(expr) => match convert_binary_operator(expr.op) {
Operator::Binary(op) => forget_estree::Expression::BinaryExpression(Box::new(
forget_estree::BinaryExpression {
operator: op,
left: convert_expression(cx, &expr.left),
right: convert_expression(cx, &expr.right),
loc: None,
range: convert_span(&expr.span),
},
)),
Operator::Logical(op) => forget_estree::Expression::LogicalExpression(Box::new(
forget_estree::LogicalExpression {
operator: op,
left: convert_expression(cx, &expr.left),
right: convert_expression(cx, &expr.right),
loc: None,
range: convert_span(&expr.span),
},
)),
},
Expr::Lit(expr) => {
let (value, range) = match expr {
Lit::Bool(expr) => (
forget_estree::JsValue::Bool(expr.value),
convert_span(&expr.span),
),
Lit::Num(expr) => (
forget_estree::JsValue::Number(expr.value.into()),
convert_span(&expr.span),
),
Lit::Str(expr) => (
forget_estree::JsValue::String(expr.value.to_string()),
convert_span(&expr.span),
),
Lit::Null(expr) => (forget_estree::JsValue::Null, convert_span(&expr.span)),
_ => todo!(),
};
forget_estree::Expression::Literal(Box::new(forget_estree::Literal {
value,
raw: None,
loc: None,
regex: None,
range,
}))
}
Expr::Assign(expr) => forget_estree::Expression::AssignmentExpression(Box::new(
forget_estree::AssignmentExpression {
operator: convert_assignment_operator(expr.op),
left: convert_assignment_target(cx, &expr.left),
right: convert_expression(cx, &expr.right),
loc: None,
range: convert_span(&expr.span),
},
)),
Expr::Member(expr) => forget_estree::Expression::MemberExpression(Box::new(
convert_member_expression(cx, expr),
)),
Expr::Fn(expr) => forget_estree::Expression::FunctionExpression(Box::new(
forget_estree::FunctionExpression {
function: convert_function(cx, expr.ident.as_ref(), &expr.function),
loc: None,
range: convert_span(&expr.function.span),
},
)),
_ => todo!("translate expression {:#?}", expr),
}
}
fn convert_assignment_target(cx: &Context, target: &PatOrExpr) -> forget_estree::AssignmentTarget {
match target {
PatOrExpr::Pat(target) => {
forget_estree::AssignmentTarget::Pattern(convert_pattern(cx, target))
}
PatOrExpr::Expr(target) => {
match target.as_ref() {
Expr::Member(target) => forget_estree::AssignmentTarget::Expression(
forget_estree::Expression::MemberExpression(Box::new(
convert_member_expression(cx, target),
)),
),
Expr::Ident(target) => forget_estree::AssignmentTarget::Pattern(
forget_estree::Pattern::Identifier(Box::new(convert_identifier(cx, target))),
),
_ => {
panic!("Expected assignment target to be member expression or identifier, got {:#?}", target)
}
}
}
}
}
fn convert_member_expression(cx: &Context, expr: &MemberExpr) -> forget_estree::MemberExpression {
let (is_computed, property) = match &expr.prop {
MemberProp::Ident(prop) => (
false,
forget_estree::Expression::Identifier(Box::new(convert_identifier(cx, prop))),
),
MemberProp::Computed(prop) => (true, convert_expression(cx, &prop.expr)),
_ => {
panic!("PrivateName member expression properties are not supported")
}
};
forget_estree::MemberExpression {
object: forget_estree::ExpressionOrSuper::Expression(convert_expression(cx, &expr.obj)),
property,
computed: is_computed,
// optional: false, // TODO
loc: None,
range: convert_span(&expr.span),
}
}
fn convert_unary_operator(op: UnaryOp) -> forget_estree::UnaryOperator {
match op {
UnaryOp::Bang => forget_estree::UnaryOperator::Negation,
UnaryOp::Delete => forget_estree::UnaryOperator::Delete,
UnaryOp::Minus => forget_estree::UnaryOperator::Minus,
UnaryOp::Plus => forget_estree::UnaryOperator::Plus,
UnaryOp::Tilde => forget_estree::UnaryOperator::Tilde,
UnaryOp::TypeOf => forget_estree::UnaryOperator::Typeof,
UnaryOp::Void => forget_estree::UnaryOperator::Void,
}
}
fn convert_assignment_operator(op: AssignOp) -> forget_estree::AssignmentOperator {
match op {
AssignOp::Assign => forget_estree::AssignmentOperator::Equals,
AssignOp::AddAssign => forget_estree::AssignmentOperator::PlusEquals,
_ => todo!("translate assignment operator"),
}
}
enum Operator {
Binary(forget_estree::BinaryOperator),
Logical(forget_estree::LogicalOperator),
}
fn convert_binary_operator(op: BinaryOp) -> Operator {
match op {
BinaryOp::Add => Operator::Binary(forget_estree::BinaryOperator::Add),
BinaryOp::BitAnd => Operator::Binary(forget_estree::BinaryOperator::BinaryAnd),
BinaryOp::BitOr => Operator::Binary(forget_estree::BinaryOperator::BinaryOr),
BinaryOp::BitXor => Operator::Binary(forget_estree::BinaryOperator::BinaryXor),
BinaryOp::Div => Operator::Binary(forget_estree::BinaryOperator::Divide),
BinaryOp::EqEq => Operator::Binary(forget_estree::BinaryOperator::Equals),
BinaryOp::EqEqEq => Operator::Binary(forget_estree::BinaryOperator::StrictEquals),
// BinaryOp::Exp => Operator::Binary(forget_estree::BinaryOperator::AsteriskAsterisk),
BinaryOp::Gt => Operator::Binary(forget_estree::BinaryOperator::GreaterThan),
BinaryOp::GtEq => Operator::Binary(forget_estree::BinaryOperator::GreaterThanOrEqual),
BinaryOp::In => Operator::Binary(forget_estree::BinaryOperator::In),
BinaryOp::InstanceOf => Operator::Binary(forget_estree::BinaryOperator::Instanceof),
BinaryOp::LShift => Operator::Binary(forget_estree::BinaryOperator::ShiftLeft),
BinaryOp::Lt => Operator::Binary(forget_estree::BinaryOperator::LessThan),
BinaryOp::LtEq => Operator::Binary(forget_estree::BinaryOperator::LessThanOrEqual),
BinaryOp::Mod => Operator::Binary(forget_estree::BinaryOperator::Modulo),
BinaryOp::Mul => Operator::Binary(forget_estree::BinaryOperator::Multiply),
BinaryOp::NotEq => Operator::Binary(forget_estree::BinaryOperator::NotEquals),
BinaryOp::NotEqEq => Operator::Binary(forget_estree::BinaryOperator::NotStrictEquals),
BinaryOp::RShift => Operator::Binary(forget_estree::BinaryOperator::ShiftRight),
BinaryOp::Sub => Operator::Binary(forget_estree::BinaryOperator::Subtract),
BinaryOp::ZeroFillRShift => {
Operator::Binary(forget_estree::BinaryOperator::UnsignedShiftRight)
}
BinaryOp::LogicalAnd => Operator::Logical(forget_estree::LogicalOperator::And),
BinaryOp::LogicalOr => Operator::Logical(forget_estree::LogicalOperator::Or),
BinaryOp::NullishCoalescing => {
Operator::Logical(forget_estree::LogicalOperator::NullCoalescing)
}
_ => panic!("Unsupported binary operator `{}`", op),
}
}
fn convert_pattern(cx: &Context, pat: &Pat) -> forget_estree::Pattern {
match pat {
Pat::Ident(pat) => {
forget_estree::Pattern::Identifier(Box::new(forget_estree::Identifier {
name: pat.id.sym.to_string(),
binding: convert_binding(cx, pat.id.span.ctxt),
loc: None,
range: convert_span(&pat.span),
}))
}
_ => todo!("translate pattern {:#?}", pat),
}
}
fn convert_binding(context: &Context, binding_cx: SyntaxContext) -> Option<Binding> {
let id = BindingId::new(binding_cx.as_u32());
if binding_cx.as_u32() == context.top_level_mark.as_u32() {
Some(Binding::Global)
} else if binding_cx.as_u32() == context.unresolved_mark.as_u32() {
Some(Binding::Module(id))
} else {
Some(Binding::Local(id))
}
}
fn convert_identifier(cx: &Context, identifier: &Ident) -> forget_estree::Identifier {
let name = identifier.sym.as_ref().to_string();
forget_estree::Identifier {
name,
binding: convert_binding(cx, identifier.span.ctxt),
loc: None,
range: convert_span(&identifier.span),
}
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {}
}
@@ -0,0 +1,24 @@
[package]
name = "forget_fixtures"
version = "0.1.0"
publish = false
authors.workspace = true
description.workspace = true
edition.workspace = true
homepage.workspace = true
keywords.workspace = true
license.workspace = true
repository.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
insta = { workspace = true }
forget_estree = { workspace = true }
forget_estree_swc = { workspace = true }
forget_hir = { workspace = true }
forget_optimization = { workspace = true }
forget_ssa = { workspace = true }
forget_build_hir = { workspace = true }
bumpalo = { workspace = true }
miette = { workspace = true, features = ["backtrace", "fancy"] }
@@ -1,12 +1,12 @@
use std::{env, fmt::Write};
use build_hir::build;
use bumpalo::Bump;
use estree::{ModuleItem, Statement};
use estree_swc::parse;
use hir::{Environment, Print, Registry};
use hir_optimization::constant_propagation;
use hir_ssa::{eliminate_redundant_phis, enter_ssa};
use forget_build_hir::build;
use forget_estree::{ModuleItem, Statement};
use forget_estree_swc::parse;
use forget_hir::{Environment, Features, Print, Registry};
use forget_optimization::constant_propagation;
use forget_ssa::{eliminate_redundant_phis, enter_ssa};
use insta::{assert_snapshot, glob};
use miette::{NamedSource, Report};
@@ -26,7 +26,7 @@ fn fixtures() {
let allocator = Bump::new();
let environment = allocator.alloc(Environment::new(
&allocator,
hir::Features {
Features {
validate_frozen_lambdas: true,
},
Registry,
@@ -1,7 +1,7 @@
---
source: crates/fixtures/tests/fixtures_test.rs
source: crates/forget_fixtures/tests/fixtures_test.rs
expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{output}\")"
input_file: crates/fixtures/tests/fixtures/constant-propagation-constant-if-condition.js
input_file: crates/forget_fixtures/tests/fixtures/constant-propagation-constant-if-condition.js
---
Input:
function Component() {
@@ -95,4 +95,3 @@ bb10 (block)
predecessors: bb11
[32] #32 = "ok"
[33] Return unknown #32
@@ -1,7 +1,7 @@
---
source: crates/fixtures/tests/fixtures_test.rs
source: crates/forget_fixtures/tests/fixtures_test.rs
expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{output}\")"
input_file: crates/fixtures/tests/fixtures/constant-propagation.js
input_file: crates/forget_fixtures/tests/fixtures/constant-propagation.js
---
Input:
function Component(props) {
@@ -196,4 +196,3 @@ bb4 (block)
[107] #101 = 18000000
[108] #102 = <undefined>
[109] Return unknown #102
@@ -1,7 +1,7 @@
---
source: crates/fixtures/tests/fixtures_test.rs
source: crates/forget_fixtures/tests/fixtures_test.rs
expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{output}\")"
input_file: crates/fixtures/tests/fixtures/error.assign-to-global.js
input_file: crates/forget_fixtures/tests/fixtures/error.assign-to-global.js
---
Input:
function foo() {
@@ -1,7 +1,7 @@
---
source: crates/fixtures/tests/fixtures_test.rs
source: crates/forget_fixtures/tests/fixtures_test.rs
expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{output}\")"
input_file: crates/fixtures/tests/fixtures/for-statement.js
input_file: crates/forget_fixtures/tests/fixtures/for-statement.js
---
Input:
function foo() {
@@ -52,4 +52,3 @@ bb2 (block)
predecessors: bb1
[20] #15 = LoadLocal unknown x$5
[21] Return unknown #15
@@ -1,7 +1,7 @@
---
source: crates/fixtures/tests/fixtures_test.rs
source: crates/forget_fixtures/tests/fixtures_test.rs
expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{output}\")"
input_file: crates/fixtures/tests/fixtures/function-expressions.js
input_file: crates/forget_fixtures/tests/fixtures/function-expressions.js
---
Input:
function Component(props) {
@@ -1,7 +1,7 @@
---
source: crates/fixtures/tests/fixtures_test.rs
source: crates/forget_fixtures/tests/fixtures_test.rs
expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{output}\")"
input_file: crates/fixtures/tests/fixtures/identifiers.js
input_file: crates/forget_fixtures/tests/fixtures/identifiers.js
---
Input:
// import React from "react";
@@ -39,4 +39,3 @@ bb0 (block)
[8] #8 = LoadLocal unknown z$7
[9] #9 = LoadLocal unknown x$4
[10] Return unknown #9
@@ -1,7 +1,7 @@
---
source: crates/fixtures/tests/fixtures_test.rs
source: crates/forget_fixtures/tests/fixtures_test.rs
expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{output}\")"
input_file: crates/fixtures/tests/fixtures/if-statement.js
input_file: crates/forget_fixtures/tests/fixtures/if-statement.js
---
Input:
function foo(a, b, c, d) {
@@ -38,4 +38,3 @@ bb1 (block)
[6] #3 = LoadLocal unknown d$7
[7] #4 = <undefined>
[8] Return unknown #4
@@ -1,7 +1,7 @@
---
source: crates/fixtures/tests/fixtures_test.rs
source: crates/forget_fixtures/tests/fixtures_test.rs
expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{output}\")"
input_file: crates/fixtures/tests/fixtures/simple.js
input_file: crates/forget_fixtures/tests/fixtures/simple.js
---
Input:
function test() {
@@ -25,4 +25,3 @@ bb0 (block)
[7] #7 = Array [unknown #0, unknown #1, unknown #2, unknown #3, unknown #4, ...unknown #6]
[8] #8 = 2
[9] Return unknown #8
@@ -1,7 +1,7 @@
---
source: crates/fixtures/tests/fixtures_test.rs
source: crates/forget_fixtures/tests/fixtures_test.rs
expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{output}\")"
input_file: crates/fixtures/tests/fixtures/ssa-reassign-if.js
input_file: crates/forget_fixtures/tests/fixtures/ssa-reassign-if.js
---
Input:
function Component(a, b) {
@@ -71,4 +71,3 @@ bb1 (block)
[24] #18 = LoadLocal unknown z$18
[25] #19 = Binary unknown #17 + unknown #18
[26] Return unknown #19
@@ -0,0 +1,20 @@
[package]
name = "forget_hir"
version = "0.1.0"
publish = false
authors.workspace = true
description.workspace = true
edition.workspace = true
homepage.workspace = true
keywords.workspace = true
license.workspace = true
repository.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bumpalo = { workspace = true }
forget_estree = { workspace = true }
indexmap = { workspace = true }
serde = { workspace = true }
forget_utils = { workspace = true }
@@ -5,7 +5,7 @@ use std::{
};
use bumpalo::{boxed::Box, collections::Vec, Bump};
use estree::BindingId;
use forget_estree::BindingId;
use crate::{
BlockId, Features, Identifier, IdentifierData, IdentifierId, Registry, Type, TypeVarId,
@@ -4,7 +4,7 @@ use bumpalo::{
boxed::Box,
collections::{String, Vec},
};
use estree::BinaryOperator;
use forget_estree::BinaryOperator;
use crate::{Function, IdentifierId, InstrIx, InstructionId, ScopeId, Type};
@@ -1,6 +1,6 @@
use std::fmt::{Result, Write};
use utils::ensure_sufficient_stack;
use forget_utils::ensure_sufficient_stack;
use crate::{
ArrayElement, BasicBlock, Function, Identifier, IdentifierOperand, Instruction,
@@ -0,0 +1,24 @@
[package]
name = "forget_optimization"
version = "0.1.0"
publish = false
authors.workspace = true
description.workspace = true
edition.workspace = true
homepage.workspace = true
keywords.workspace = true
license.workspace = true
repository.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
forget_estree = { workspace = true }
forget_hir = { workspace = true }
forget_ssa = { workspace = true }
forget_build_hir = { workspace = true }
forget_utils = { workspace = true }
bumpalo = { workspace = true }
indexmap = { workspace = true }
miette = { workspace = true }
thiserror = { workspace = true }
@@ -1,12 +1,12 @@
use std::collections::HashMap;
use build_hir::initialize_hir;
use estree::BinaryOperator;
use hir::{
BlockKind, Environment, Function, GotoKind, IdentifierId, Instruction, InstructionValue,
LoadGlobal, Operand, Primitive, PrimitiveValue, TerminalValue,
use forget_build_hir::initialize_hir;
use forget_estree::BinaryOperator;
use forget_hir::{
BlockKind, Environment, Function, GotoKind, GotoTerminal, IdentifierId, Instruction,
InstructionValue, LoadGlobal, Operand, Primitive, PrimitiveValue, TerminalValue,
};
use hir_ssa::eliminate_redundant_phis;
use forget_ssa::eliminate_redundant_phis;
pub fn constant_propagation<'a>(env: &Environment<'a>, fun: &mut Function<'a>) {
let mut constants = Constants::new();
@@ -107,7 +107,7 @@ fn apply_constant_propagation<'a>(
} else {
terminal.alternate
};
block.terminal.value = TerminalValue::Goto(hir::GotoTerminal {
block.terminal.value = TerminalValue::Goto(GotoTerminal {
block: target_block_id,
kind: GotoKind::Break,
});
@@ -0,0 +1,21 @@
[package]
name = "forget_ssa"
version = "0.1.0"
publish = false
authors.workspace = true
description.workspace = true
edition.workspace = true
homepage.workspace = true
keywords.workspace = true
license.workspace = true
repository.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
forget_hir = { workspace = true }
forget_utils = { workspace = true }
bumpalo = { workspace = true }
indexmap = { workspace = true }
miette = { workspace = true }
thiserror = { workspace = true }
@@ -1,3 +1,3 @@
# hir-ssa
# forget_ssa
This crate handles conversion to/from SSA and normal form. This includes a pass to eliminate redundant phi nodes in SSA form.
@@ -1,7 +1,7 @@
use std::collections::{HashMap, HashSet};
use hir::{BlockId, Environment, Function, Identifier, IdentifierId, InstructionValue};
use utils::RetainMut;
use forget_hir::{BlockId, Environment, Function, Identifier, IdentifierId, InstructionValue};
use forget_utils::RetainMut;
/// Pass to eliminate redundant phi nodes:
/// all operands are the same identifier, ie `x2 = phi(x1, x1, x1)`.
@@ -1,7 +1,7 @@
use std::{cell::RefCell, rc::Rc};
use bumpalo::collections::{CollectIn, Vec};
use hir::{
use forget_hir::{
BasicBlock, BlockId, Blocks, Environment, Function, Identifier, IdentifierData, IdentifierId,
IdentifierOperand, Instruction, InstructionValue, LValue, MutableRange, Phi,
};
@@ -1,4 +1,4 @@
use hir::{Environment, HIR};
use forget_hir::{Environment, HIR};
pub fn leave_ssa<'a>(_env: &'a Environment, _hir: &mut HIR<'a>) {
todo!("leave_ssa()");
@@ -0,0 +1,17 @@
[package]
name = "forget_utils"
version = "0.1.0"
publish = false
authors.workspace = true
description.workspace = true
edition.workspace = true
homepage.workspace = true
keywords.workspace = true
license.workspace = true
repository.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bumpalo = { workspace = true }
stacker = { workspace = true }
@@ -1,17 +0,0 @@
[package]
name = "hir-optimization"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
estree = { path = "../estree" }
hir = { path = "../hir" }
hir-ssa = { path = "../hir-ssa" }
build-hir = { path = "../build-hir" }
utils = { path = "../utils" }
bumpalo = "3.13.0"
indexmap = "2.0.0"
miette = { version = "5.9.0" }
thiserror = "1.0.41"
-14
View File
@@ -1,14 +0,0 @@
[package]
name = "hir-ssa"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
hir = { path = "../hir" }
utils = { path = "../utils" }
bumpalo = "3.13.0"
indexmap = "2.0.0"
miette = { version = "5.9.0" }
thiserror = "1.0.41"
-13
View File
@@ -1,13 +0,0 @@
[package]
name = "hir"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bumpalo = { version = "3.13.0", features = ["boxed", "collections"] }
estree = { path = "../estree" }
indexmap = "2.0.0"
serde = "1.0.164"
utils = { path = "../utils" }
@@ -1,14 +0,0 @@
[package]
name = "swc-demo"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
swc_common = { version = "0.31.12", features = ["tty-emitter", "sourcemap", "anyhow"] }
swc_core = "0.78.15"
swc_ecma_parser = "0.136.2"
swc_ecma_transforms_base = "0.129.8"
swc_ecma_visit = "0.92.1"
swc_visit = "0.5.6"
@@ -1,59 +0,0 @@
use swc_common::sync::Lrc;
use swc_common::Mark;
use swc_common::{
errors::{ColorConfig, Handler},
FileName, SourceMap,
};
use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax};
use swc_ecma_transforms_base::resolver;
use swc_ecma_visit::swc_ecma_ast::Program;
use swc_ecma_visit::VisitMutWith;
fn main() {
let cm: Lrc<SourceMap> = Default::default();
let handler = Handler::with_tty_emitter(ColorConfig::Auto, true, false, Some(cm.clone()));
// Real usage
// let fm = cm
// .load_file(Path::new("test.js"))
// .expect("failed to load test.js");
let fm = cm.new_source_file(
FileName::Custom("test.js".into()),
"function foo(x: number, y: number): number { return x + y}".into(),
);
let lexer = Lexer::new(
// We want to parse ecmascript
Syntax::Typescript(swc_ecma_parser::TsConfig {
tsx: true,
decorators: true,
..Default::default()
}),
// EsVersion defaults to es5
Default::default(),
StringInput::from(&*fm),
None,
);
let mut parser = Parser::new_from(lexer);
for e in parser.take_errors() {
e.into_diagnostic(&handler).emit();
}
let module = parser
.parse_module()
.map_err(|e| {
// Unrecoverable fatal error occurred
e.into_diagnostic(&handler).emit()
})
.expect("failed to parser module");
let mut module = Program::Module(module);
let _result = swc_common::GLOBALS.set(&swc_common::Globals::new(), || {
let mut resolved = resolver(Mark::new(), Mark::new(), true);
module.visit_mut_with(&mut resolved)
});
println!("{:#?}", module);
}
-10
View File
@@ -1,10 +0,0 @@
[package]
name = "utils"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bumpalo = { version = "3.13.0", features = ["boxed", "collections"] }
stacker = "0.1.15"