mirror of
https://github.com/MercuryWorkshop/scramjet.git
synced 2025-05-12 14:00:01 -04:00
rewrite
This commit is contained in:
parent
00de00e7ca
commit
cb2ab0cedd
36 changed files with 1825 additions and 1355 deletions
|
@ -11,7 +11,7 @@
|
|||
},
|
||||
"scripts": {
|
||||
"build": "rspack build --mode production",
|
||||
"rewriter:build": "cd rewriter && bash build.sh && cd ..",
|
||||
"rewriter:build": "cd rewriter/wasm/ && bash build.sh && cd ../../",
|
||||
"dev": "node server.js",
|
||||
"prepack": "RELEASE=1 npm run rewriter:build && npm run build",
|
||||
"pub": "npm publish --no-git-checks --access public",
|
||||
|
|
404
rewriter/Cargo.lock
generated
404
rewriter/Cargo.lock
generated
|
@ -1,6 +1,6 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
|
@ -16,9 +16,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.18"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
|
@ -46,13 +52,14 @@ checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
|||
|
||||
[[package]]
|
||||
name = "boa_ast"
|
||||
version = "0.19.1"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a69ee3a749ea36d4e56d92941e7b25076b493d4917c3d155b6cf369e23547d9"
|
||||
checksum = "2c340fe0f0b267787095cbe35240c6786ff19da63ec7b69367ba338eace8169b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"boa_interner",
|
||||
"boa_macros",
|
||||
"boa_string",
|
||||
"indexmap",
|
||||
"num-bigint",
|
||||
"rustc-hash",
|
||||
|
@ -60,9 +67,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "boa_engine"
|
||||
version = "0.19.1"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06e4559b35b80ceb2e6328481c0eca9a24506663ea33ee1e279be6b5b618b25c"
|
||||
checksum = "f620c3f06f51e65c0504ddf04978be1b814ac6586f0b45f6019801ab5efd37f9"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags",
|
||||
|
@ -75,9 +82,9 @@ dependencies = [
|
|||
"boa_string",
|
||||
"bytemuck",
|
||||
"cfg-if",
|
||||
"dashmap 5.5.3",
|
||||
"fast-float",
|
||||
"hashbrown 0.14.5",
|
||||
"dashmap",
|
||||
"fast-float2",
|
||||
"hashbrown 0.15.2",
|
||||
"icu_normalizer",
|
||||
"indexmap",
|
||||
"intrusive-collections",
|
||||
|
@ -99,32 +106,32 @@ dependencies = [
|
|||
"static_assertions",
|
||||
"tap",
|
||||
"thin-vec",
|
||||
"thiserror 1.0.64",
|
||||
"thiserror 2.0.6",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "boa_gc"
|
||||
version = "0.19.1"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "716406f57d67bc3ac7fd227d5513b42df401dff14a3be22cbd8ee29817225363"
|
||||
checksum = "2425c0b7720d42d73eaa6a883fbb77a5c920da8694964a3d79a67597ac55cce2"
|
||||
dependencies = [
|
||||
"boa_macros",
|
||||
"boa_profiler",
|
||||
"boa_string",
|
||||
"hashbrown 0.14.5",
|
||||
"hashbrown 0.15.2",
|
||||
"thin-vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "boa_interner"
|
||||
version = "0.19.1"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e18df2272616e1ba0322a69333d37dbb78797f1aa0595aad9dc41e8ecd06ad9"
|
||||
checksum = "42407a3b724cfaecde8f7d4af566df4b56af32a2f11f0956f5570bb974e7f749"
|
||||
dependencies = [
|
||||
"boa_gc",
|
||||
"boa_macros",
|
||||
"hashbrown 0.14.5",
|
||||
"hashbrown 0.15.2",
|
||||
"indexmap",
|
||||
"once_cell",
|
||||
"phf",
|
||||
|
@ -134,9 +141,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "boa_macros"
|
||||
version = "0.19.1"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240f4126219a83519bad05c9a40bfc0303921eeb571fc2d7e44c17ffac99d3f1"
|
||||
checksum = "9fd3f870829131332587f607a7ff909f1af5fc523fd1b192db55fbbdf52e8d3c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -146,16 +153,16 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "boa_parser"
|
||||
version = "0.19.1"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b59dc05bf1dc019b11478a92986f590cff43fced4d20e866eefb913493e91c"
|
||||
checksum = "9cc142dac798cdc6e2dbccfddeb50f36d2523bb977a976e19bdb3ae19b740804"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"boa_ast",
|
||||
"boa_interner",
|
||||
"boa_macros",
|
||||
"boa_profiler",
|
||||
"fast-float",
|
||||
"fast-float2",
|
||||
"icu_properties",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
|
@ -165,17 +172,17 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "boa_profiler"
|
||||
version = "0.19.1"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00ee0645509b3b91abd724f25072649d9e8e65653a78ff0b6e592788a58dd838"
|
||||
checksum = "4064908e7cdf9b6317179e9b04dcb27f1510c1c144aeab4d0394014f37a0f922"
|
||||
|
||||
[[package]]
|
||||
name = "boa_string"
|
||||
version = "0.19.1"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae85205289bab1f2c7c8a30ddf0541cf89ba2ff7dbd144feef50bbfa664288d4"
|
||||
checksum = "7debc13fbf7997bf38bf8e9b20f1ad5e2a7d27a900e1f6039fe244ce30f589b5"
|
||||
dependencies = [
|
||||
"fast-float",
|
||||
"fast-float2",
|
||||
"paste",
|
||||
"rustc-hash",
|
||||
"sptr",
|
||||
|
@ -193,9 +200,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.19.0"
|
||||
version = "1.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d"
|
||||
checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a"
|
||||
dependencies = [
|
||||
"bytemuck_derive",
|
||||
]
|
||||
|
@ -258,19 +265,6 @@ version = "0.8.20"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
version = "5.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"hashbrown 0.14.5",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
version = "6.1.0"
|
||||
|
@ -318,10 +312,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "fast-float"
|
||||
version = "0.2.0"
|
||||
name = "fast-float2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95765f67b4b18863968b4a1bd5bb576f732b29a4a28c7cd84c09fa3e2875f33c"
|
||||
checksum = "f8eb564c5c7423d25c886fb561d1e4ee69f72354d16918afa32c08811f6b6a55"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
|
@ -332,15 +332,6 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ftree"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "241f9dd9089e67c0b269989e9f884b12a61f68fc07ea8a4be6af8ee164e1abf7"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
|
@ -364,9 +355,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.0"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
|
@ -488,31 +484,33 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.5.0"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
|
||||
checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
"idna_adapter",
|
||||
"smallvec",
|
||||
"utf8_iter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna_adapter"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
|
||||
dependencies = [
|
||||
"icu_normalizer",
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.6.0"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
|
||||
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.15.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexset"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6279c421b4feea3fb8e07ea6bd0934b38b641f6601d5f6677062fad15272ed57"
|
||||
dependencies = [
|
||||
"ftree",
|
||||
"hashbrown 0.15.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -547,30 +545,31 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.72"
|
||||
version = "0.3.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
|
||||
checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.161"
|
||||
version = "0.2.168"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
|
||||
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.7.3"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704"
|
||||
checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
|
@ -603,6 +602,18 @@ dependencies = [
|
|||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"boa_engine",
|
||||
"oxc",
|
||||
"rewriter",
|
||||
"url",
|
||||
"urlencoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nonmax"
|
||||
version = "0.5.5"
|
||||
|
@ -688,14 +699,13 @@ checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56"
|
|||
|
||||
[[package]]
|
||||
name = "oxc"
|
||||
version = "0.37.0"
|
||||
version = "0.40.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fc2b8d593792ca631b252b043dcaa6e65195432922fde0c4d2397cfba25d02d"
|
||||
checksum = "49d6a3adf0b9cb2baa889d6e978d792c695a83886bb8e623d27ef0996be8f52b"
|
||||
dependencies = [
|
||||
"oxc_allocator",
|
||||
"oxc_ast",
|
||||
"oxc_diagnostics",
|
||||
"oxc_index",
|
||||
"oxc_parser",
|
||||
"oxc_regular_expression",
|
||||
"oxc_span",
|
||||
|
@ -712,7 +722,7 @@ dependencies = [
|
|||
"owo-colors",
|
||||
"oxc-miette-derive",
|
||||
"textwrap",
|
||||
"thiserror 1.0.64",
|
||||
"thiserror 1.0.69",
|
||||
"unicode-width 0.2.0",
|
||||
]
|
||||
|
||||
|
@ -729,9 +739,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "oxc_allocator"
|
||||
version = "0.37.0"
|
||||
version = "0.40.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3137194d5acbead2fd78db27c03b27faa2dfa53628514b1843ae31b2ba304d28"
|
||||
checksum = "6b1409be2036cb370daee57ec0942c3aa22949da7e269d583767327b9286493e"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"bumpalo",
|
||||
|
@ -739,9 +749,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "oxc_ast"
|
||||
version = "0.37.0"
|
||||
version = "0.40.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a142fb8a722e9eb9bf4c9ef37bc7f37e9cc7fc38acba1889d8fb9f2df8ca85a"
|
||||
checksum = "9353f8fc2507736ce314763ea7e365e25845f431800f8cba196b8365565a6139"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cow-utils",
|
||||
|
@ -757,9 +767,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "oxc_ast_macros"
|
||||
version = "0.37.0"
|
||||
version = "0.40.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ba3432be9886577f6ea36a3ecba6a9b7f2f0bc16aae8a759a0d43f91e0cb4d5"
|
||||
checksum = "ffc4de384e05599bb89541ebbff124947e326a02783d66d2c2c534ecf58a66b4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -768,31 +778,44 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "oxc_diagnostics"
|
||||
version = "0.37.0"
|
||||
version = "0.40.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2bc8acd3740a2b2b32e1826d966b65f11b9ef5bebb460ebc7cbadf898d7efec"
|
||||
checksum = "425d8c272e885c3318878acf0a1e985b7bdb8b8e57d695f4ab72e444ba781432"
|
||||
dependencies = [
|
||||
"oxc-miette",
|
||||
"rustc-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oxc_estree"
|
||||
version = "0.37.0"
|
||||
name = "oxc_ecmascript"
|
||||
version = "0.40.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b00fbdfd3e49faee78dada84e8cd79fdcf0bbb9a4c3f77a312b823d867e1894b"
|
||||
checksum = "754e92029d7deb771b5ae9fb628903d0a8da64ca9e61b2e05eb6e01e7613976c"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"oxc_ast",
|
||||
"oxc_span",
|
||||
"oxc_syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oxc_estree"
|
||||
version = "0.40.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89eb8a7cf00b2fd6a8e63f38548ae0f9b4f8e21734ef0ae71424104e8503d667"
|
||||
|
||||
[[package]]
|
||||
name = "oxc_index"
|
||||
version = "0.37.0"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4852baa0de945b11def319a618cb31546f71b0a782f22c2e80f32f0ed356804d"
|
||||
checksum = "5eca5d9726cd0a6e433debe003b7bc88b2ecad0bb6109f0cef7c55e692139a34"
|
||||
|
||||
[[package]]
|
||||
name = "oxc_parser"
|
||||
version = "0.37.0"
|
||||
version = "0.40.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bf3395e8cad18ec9cda1bd530720d8e2747c5e4767573ba9bb3f0c798d944a4"
|
||||
checksum = "56fff5de3a699593b5b7e8fd4b30ffedd6d0a9188580afd28745aaab83961e23"
|
||||
dependencies = [
|
||||
"assert-unchecked",
|
||||
"bitflags",
|
||||
|
@ -803,6 +826,7 @@ dependencies = [
|
|||
"oxc_allocator",
|
||||
"oxc_ast",
|
||||
"oxc_diagnostics",
|
||||
"oxc_ecmascript",
|
||||
"oxc_regular_expression",
|
||||
"oxc_span",
|
||||
"oxc_syntax",
|
||||
|
@ -812,9 +836,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "oxc_regular_expression"
|
||||
version = "0.37.0"
|
||||
version = "0.40.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab44622a5b4cb09e12eda5f70d9c033c3a820b26f92a1bc86e596e137dc42089"
|
||||
checksum = "e420360e2c8e83a3eef7c74bbf3a11b375370fa3656c8cee65e4dfe96e26bb93"
|
||||
dependencies = [
|
||||
"oxc_allocator",
|
||||
"oxc_ast_macros",
|
||||
|
@ -828,9 +852,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "oxc_span"
|
||||
version = "0.37.0"
|
||||
version = "0.40.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "363c6c4329272836347089768df8c87c298f3bd329668306e1f50fc1f8804b7c"
|
||||
checksum = "e940a7230413a1a138e04c1cf45fea6014d2e995b9c95857252259f5cec9f844"
|
||||
dependencies = [
|
||||
"compact_str",
|
||||
"oxc-miette",
|
||||
|
@ -841,13 +865,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "oxc_syntax"
|
||||
version = "0.37.0"
|
||||
version = "0.40.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8697e0c9c99df9f5863196f82cb705685d747b1732733b80f5203cf70d46335a"
|
||||
checksum = "ef274df7ce9d14a3d4f0b9d8453920b7a4f22bfcc1a4ec394233280cca07ed97"
|
||||
dependencies = [
|
||||
"assert-unchecked",
|
||||
"bitflags",
|
||||
"dashmap 6.1.0",
|
||||
"nonmax",
|
||||
"oxc_allocator",
|
||||
"oxc_ast_macros",
|
||||
|
@ -856,6 +879,7 @@ dependencies = [
|
|||
"oxc_span",
|
||||
"phf",
|
||||
"rustc-hash",
|
||||
"ryu-js",
|
||||
"unicode-id-start",
|
||||
]
|
||||
|
||||
|
@ -928,15 +952,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pollster"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2"
|
||||
checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.9.0"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2"
|
||||
checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
|
@ -1012,9 +1036,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.7"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
|
||||
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
@ -1033,22 +1057,17 @@ dependencies = [
|
|||
name = "rewriter"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"boa_engine",
|
||||
"indexset",
|
||||
"instant",
|
||||
"js-sys",
|
||||
"oxc",
|
||||
"thiserror 2.0.3",
|
||||
"smallvec",
|
||||
"thiserror 2.0.6",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "2.0.0"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
|
||||
checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497"
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
|
@ -1082,18 +1101,18 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.210"
|
||||
version = "1.0.216"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
||||
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.210"
|
||||
version = "1.0.216"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
||||
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1102,9 +1121,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.132"
|
||||
version = "1.0.133"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
|
||||
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
|
@ -1150,9 +1169,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.89"
|
||||
version = "2.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1195,27 +1214,27 @@ checksum = "a38c90d48152c236a3ab59271da4f4ae63d678c5d7ad6b7714d7cb9760be5e4b"
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.64"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl 1.0.64",
|
||||
"thiserror-impl 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.3"
|
||||
version = "2.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa"
|
||||
checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.3",
|
||||
"thiserror-impl 2.0.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.64"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1224,9 +1243,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.3"
|
||||
version = "2.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568"
|
||||
checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1235,9 +1254,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.36"
|
||||
version = "0.3.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
|
||||
checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
|
@ -1259,9 +1278,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
|||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.18"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
|
||||
checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
|
@ -1277,21 +1296,6 @@ dependencies = [
|
|||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.8"
|
||||
|
@ -1309,12 +1313,6 @@ dependencies = [
|
|||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-id-start"
|
||||
version = "1.3.1"
|
||||
|
@ -1323,9 +1321,9 @@ checksum = "2f322b60f6b9736017344fa0635d64be2f458fbc04eef65f6be22976dd1ffd5b"
|
|||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.13"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-linebreak"
|
||||
|
@ -1333,15 +1331,6 @@ version = "0.1.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.14"
|
||||
|
@ -1356,15 +1345,21 @@ checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
|||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.2"
|
||||
version = "2.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
|
||||
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urlencoding"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||
|
||||
[[package]]
|
||||
name = "utf16_iter"
|
||||
version = "1.0.5"
|
||||
|
@ -1389,11 +1384,25 @@ version = "0.11.0+wasi-snapshot-preview1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"js-sys",
|
||||
"oxc",
|
||||
"rewriter",
|
||||
"thiserror 2.0.6",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.95"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
|
||||
checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
|
@ -1402,13 +1411,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.95"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
|
||||
checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
|
@ -1417,9 +1425,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.95"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
|
||||
checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
|
@ -1427,9 +1435,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.95"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
|
||||
checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1440,15 +1448,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.95"
|
||||
version = "0.2.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
|
||||
checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.72"
|
||||
version = "0.3.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112"
|
||||
checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
|
@ -1541,9 +1549,9 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
|||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.7.4"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5"
|
||||
checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
|
@ -1553,9 +1561,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "yoke-derive"
|
||||
version = "0.7.4"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
|
||||
checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1586,18 +1594,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55"
|
||||
checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e"
|
||||
dependencies = [
|
||||
"zerofrom-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom-derive"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
|
||||
checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
|
@ -1,14 +1,6 @@
|
|||
[package]
|
||||
name = "rewriter"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[features]
|
||||
default = ["debug"]
|
||||
debug = []
|
||||
[workspace]
|
||||
members = ["native", "rewriter", "wasm"]
|
||||
resolver = "2"
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
|
@ -17,18 +9,6 @@ lto = true
|
|||
codegen-units = 1
|
||||
panic = "abort"
|
||||
|
||||
[dependencies]
|
||||
indexset = "0.5.0"
|
||||
instant = { version = "0.1.13", features = ["wasm-bindgen"] }
|
||||
js-sys = "0.3.69"
|
||||
oxc = "0.37.0"
|
||||
thiserror = "2.0.3"
|
||||
url = "2.5.2"
|
||||
wasm-bindgen = "0.2.95"
|
||||
web-sys = { version = "0.3.72", features = ["Url"] }
|
||||
[workspace.dependencies]
|
||||
oxc = "0.40.1"
|
||||
|
||||
[dev-dependencies]
|
||||
boa_engine = "0.19.0"
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(wasm_bindgen_unstable_test_coverage)'] }
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
set RUSTFLAGS="-C target-feature=+atomics,+bulk-memory,+simd128"
|
||||
cargo build --lib --target wasm32-unknown-unknown -Z build-std=panic_abort,std --release
|
||||
wasm-bindgen --weak-refs --target web --out-dir out/ target/wasm32-unknown-unknown/release/rewriter.wasm
|
||||
|
||||
cd ..
|
||||
|
||||
$WASM = "rewriter/out/rewriter_bg.wasm"
|
||||
|
||||
Measure-Command -Expression { wasm-opt -Oz --vacuum --dce --enable-threads --enable-bulk-memory --enable-simd "$WASM" -o rewriter/out/optimized.wasm }
|
||||
|
||||
Write-Output "self.WASM = '" | Out-File -NoNewline -FilePath static/wasm.js
|
||||
[System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes("rewriter/out/optimized.wasm")) | Out-File -Append -NoNewline -FilePath static/wasm.js
|
||||
Write-Output "';" | Out-File -Append -NoNewline -FilePath static/wasm.js
|
||||
Write-Output "Rewriter Build Complete!"
|
14
rewriter/native/Cargo.toml
Normal file
14
rewriter/native/Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "native"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.94"
|
||||
oxc = { workspace = true }
|
||||
rewriter = { version = "0.1.0", path = "../rewriter", features = ["debug"] }
|
||||
url = "2.5.4"
|
||||
urlencoding = "2.1.3"
|
||||
|
||||
[dev-dependencies]
|
||||
boa_engine = "0.20.0"
|
2
rewriter/native/discord.js
Normal file
2
rewriter/native/discord.js
Normal file
File diff suppressed because one or more lines are too long
|
@ -3394,4 +3394,4 @@ this._hd = this._hd || {}; (function (_) {
|
|||
_.z();
|
||||
} catch (e) { _._DumpException(e) }
|
||||
})(this._hd);
|
||||
// Google Inc.
|
||||
// Google Inc.
|
166
rewriter/native/src/main.rs
Normal file
166
rewriter/native/src/main.rs
Normal file
|
@ -0,0 +1,166 @@
|
|||
use std::{env, fs, str::FromStr, sync::Arc};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use oxc::diagnostics::NamedSource;
|
||||
use rewriter::{cfg::Config, rewrite, RewriteResult};
|
||||
use url::Url;
|
||||
use urlencoding::encode;
|
||||
|
||||
fn encode_string(str: String) -> String {
|
||||
encode(&str).to_string()
|
||||
}
|
||||
|
||||
fn dorewrite(data: &str) -> Result<RewriteResult> {
|
||||
rewrite(
|
||||
data,
|
||||
Config {
|
||||
prefix: "/scrammedjet/".to_string(),
|
||||
encoder: Box::new(encode_string),
|
||||
|
||||
base: Url::from_str("https://google.com/glorngle/si.js").context("invalid base")?,
|
||||
sourcetag: "glongle1".to_string(),
|
||||
|
||||
wrapfn: "$wrap".to_string(),
|
||||
wrapthisfn: "$gwrap".to_string(),
|
||||
importfn: "$import".to_string(),
|
||||
rewritefn: "$rewrite".to_string(),
|
||||
metafn: "$meta".to_string(),
|
||||
setrealmfn: "$setrealm".to_string(),
|
||||
pushsourcemapfn: "$pushsourcemap".to_string(),
|
||||
|
||||
capture_errors: true,
|
||||
do_sourcemaps: true,
|
||||
scramitize: false,
|
||||
strict_rewrites: true,
|
||||
},
|
||||
)
|
||||
.context("failed to rewrite file")
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let file = env::args().nth(1).unwrap_or_else(|| "test.js".to_string());
|
||||
let data = fs::read_to_string(file).context("failed to read file")?;
|
||||
|
||||
let res = dorewrite(&data)?;
|
||||
|
||||
let source = Arc::new(
|
||||
NamedSource::new(data, "https://google.com/glorngle/si.js").with_language("javascript"),
|
||||
);
|
||||
eprintln!("errors:");
|
||||
for err in res.errors {
|
||||
eprintln!("{}", err.with_source_code(source.clone()));
|
||||
}
|
||||
|
||||
println!("changes: {:#?}", res.changes);
|
||||
|
||||
println!(
|
||||
"rewritten:\n{}",
|
||||
String::from_utf8(res.js).context("failed to parse rewritten js")?
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::fs;
|
||||
|
||||
use boa_engine::{
|
||||
js_str, js_string,
|
||||
object::ObjectInitializer,
|
||||
property::{Attribute, PropertyDescriptorBuilder},
|
||||
Context, NativeFunction, Source,
|
||||
};
|
||||
|
||||
use crate::dorewrite;
|
||||
|
||||
#[test]
|
||||
fn google() {
|
||||
let source_text = include_str!("../sample/google.js");
|
||||
dorewrite(source_text).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let files = fs::read_dir("./tests").unwrap();
|
||||
|
||||
for file in files {
|
||||
if !file
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.file_name()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.ends_with(".js")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let content = fs::read_to_string(file.unwrap().path()).unwrap();
|
||||
|
||||
let mut context = Context::default();
|
||||
|
||||
let window = ObjectInitializer::new(&mut context).build();
|
||||
context
|
||||
.register_global_property(js_str!("window"), window, Attribute::READONLY)
|
||||
.unwrap();
|
||||
context
|
||||
.global_object()
|
||||
.define_property_or_throw(
|
||||
js_str!("location"),
|
||||
PropertyDescriptorBuilder::new()
|
||||
.get(
|
||||
NativeFunction::from_copy_closure(|_, _, _| {
|
||||
Ok(js_str!("location").into())
|
||||
})
|
||||
.to_js_function(context.realm()),
|
||||
)
|
||||
.set(
|
||||
NativeFunction::from_copy_closure(|_, _, _| {
|
||||
panic!("fail: window.location got set")
|
||||
})
|
||||
.to_js_function(context.realm()),
|
||||
)
|
||||
.build(),
|
||||
&mut context,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
context
|
||||
.register_global_callable(
|
||||
js_string!("fail"),
|
||||
0,
|
||||
NativeFunction::from_copy_closure(|_, _, _| {
|
||||
panic!("fail");
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let result = context
|
||||
.eval(Source::from_bytes(
|
||||
br#"
|
||||
function $wrap(val) {
|
||||
if (val === window || val === "location" || val === globalThis) return "";
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
function assert(val) {
|
||||
if (!val) fail();
|
||||
}
|
||||
|
||||
function check(val) {
|
||||
if (val === window || val === "location") fail();
|
||||
}
|
||||
"#,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
let rewritten = dorewrite(&content).unwrap();
|
||||
println!("{:?}", rewritten);
|
||||
|
||||
context.eval(Source::from_bytes(&rewritten.js)).unwrap();
|
||||
println!("PASS");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,5 +25,3 @@ function f() { return import("x") }
|
|||
|
||||
|
||||
let window = (1, window);
|
||||
|
||||
|
13
rewriter/rewriter/Cargo.toml
Normal file
13
rewriter/rewriter/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "rewriter"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
oxc = { workspace = true }
|
||||
smallvec = "1.13.2"
|
||||
thiserror = "2.0.6"
|
||||
url = "2.5.4"
|
||||
|
||||
[features]
|
||||
debug = []
|
24
rewriter/rewriter/src/cfg.rs
Normal file
24
rewriter/rewriter/src/cfg.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
use url::Url;
|
||||
|
||||
pub struct Config {
|
||||
pub prefix: String,
|
||||
pub sourcetag: String,
|
||||
pub base: Url,
|
||||
|
||||
pub wrapfn: String,
|
||||
pub wrapthisfn: String,
|
||||
pub importfn: String,
|
||||
pub rewritefn: String,
|
||||
pub setrealmfn: String,
|
||||
pub metafn: String,
|
||||
pub pushsourcemapfn: String,
|
||||
|
||||
pub encoder: EncodeFn,
|
||||
|
||||
pub capture_errors: bool,
|
||||
pub scramitize: bool,
|
||||
pub do_sourcemaps: bool,
|
||||
pub strict_rewrites: bool,
|
||||
}
|
||||
|
||||
pub type EncodeFn = Box<dyn Fn(String) -> String>;
|
277
rewriter/rewriter/src/changes.rs
Normal file
277
rewriter/rewriter/src/changes.rs
Normal file
|
@ -0,0 +1,277 @@
|
|||
use oxc::{
|
||||
ast::ast::AssignmentOperator,
|
||||
span::{CompactStr, Span},
|
||||
};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use crate::cfg::Config;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum JsChange {
|
||||
/// `(cfg.wrapfn(ident))` | `cfg.wrapfn(ident)`
|
||||
WrapFn {
|
||||
span: Span,
|
||||
ident: CompactStr,
|
||||
wrapped: bool,
|
||||
},
|
||||
/// `cfg.setrealmfn({}).ident`
|
||||
SetRealmFn {
|
||||
span: Span,
|
||||
ident: CompactStr,
|
||||
},
|
||||
/// `cfg.wrapthis(this)`
|
||||
WrapThisFn {
|
||||
span: Span,
|
||||
},
|
||||
/// `(cfg.importfn("cfg.base"))`
|
||||
ImportFn {
|
||||
span: Span,
|
||||
},
|
||||
/// `cfg.metafn("cfg.base")`
|
||||
MetaFn {
|
||||
span: Span,
|
||||
},
|
||||
|
||||
/// `$scramerr(name)`
|
||||
ScramErr {
|
||||
span: Span,
|
||||
name: CompactStr,
|
||||
},
|
||||
/// `$scramitize(span)`
|
||||
Scramitize {
|
||||
span: Span,
|
||||
},
|
||||
|
||||
/// `eval(cfg.rewritefn(inner))`
|
||||
Eval {
|
||||
span: Span,
|
||||
inner: Span,
|
||||
},
|
||||
/// `((t)=>$scramjet$tryset(name,"op",t)||(name op t))(rhsspan)`
|
||||
Assignment {
|
||||
name: CompactStr,
|
||||
entirespan: Span,
|
||||
rhsspan: Span,
|
||||
op: AssignmentOperator,
|
||||
},
|
||||
/// `ident,` -> `ident: cfg.wrapfn(ident)`
|
||||
ShorthandObj {
|
||||
span: Span,
|
||||
name: CompactStr,
|
||||
},
|
||||
SourceTag {
|
||||
span: Span,
|
||||
tagname: String,
|
||||
},
|
||||
|
||||
// don't use for anything static, only use for stuff like rewriteurl
|
||||
Replace {
|
||||
span: Span,
|
||||
text: String,
|
||||
},
|
||||
Delete {
|
||||
span: Span,
|
||||
},
|
||||
}
|
||||
|
||||
type Changes<'a> = SmallVec<[&'a str; 8]>;
|
||||
|
||||
enum JsChangeInner<'a> {
|
||||
Wrap {
|
||||
/// Changes to add before span
|
||||
before: Changes<'a>,
|
||||
/// Span to add in between
|
||||
span: &'a Span,
|
||||
/// Changes to add after span
|
||||
after: Changes<'a>,
|
||||
},
|
||||
Replace(Changes<'a>),
|
||||
}
|
||||
|
||||
impl JsChange {
|
||||
pub fn get_span(&self) -> &Span {
|
||||
match self {
|
||||
Self::WrapFn { span, .. } => span,
|
||||
Self::SetRealmFn { span, .. } => span,
|
||||
Self::WrapThisFn { span } => span,
|
||||
Self::ImportFn { span } => span,
|
||||
Self::MetaFn { span } => span,
|
||||
|
||||
Self::ScramErr { span, .. } => span,
|
||||
Self::Scramitize { span } => span,
|
||||
|
||||
Self::Eval { span, .. } => span,
|
||||
Self::Assignment { entirespan, .. } => entirespan,
|
||||
Self::ShorthandObj { span, .. } => span,
|
||||
Self::SourceTag { span, .. } => span,
|
||||
|
||||
Self::Replace { span, .. } => span,
|
||||
Self::Delete { span } => span,
|
||||
}
|
||||
}
|
||||
|
||||
// returns (bunch of stuff to add before, option<bunch of stuff to add after>)
|
||||
// bunch of stuff to add after should only be some if it's not a replace op
|
||||
fn to_inner<'a>(&'a self, cfg: &'a Config) -> JsChangeInner<'a> {
|
||||
match self {
|
||||
Self::WrapFn { ident, wrapped, .. } => JsChangeInner::Replace(if *wrapped {
|
||||
smallvec!["(", cfg.wrapfn.as_str(), "(", ident.as_str(), ")", ")"]
|
||||
} else {
|
||||
smallvec![cfg.wrapfn.as_str(), "(", ident.as_str(), ")"]
|
||||
}),
|
||||
Self::SetRealmFn { ident, .. } => {
|
||||
JsChangeInner::Replace(smallvec![cfg.setrealmfn.as_str(), "({}).", ident.as_str()])
|
||||
}
|
||||
Self::WrapThisFn { .. } => {
|
||||
JsChangeInner::Replace(smallvec![cfg.wrapthisfn.as_str(), "(this)"])
|
||||
}
|
||||
Self::ImportFn { .. } => JsChangeInner::Replace(smallvec![
|
||||
"(",
|
||||
cfg.importfn.as_str(),
|
||||
"(\"",
|
||||
cfg.base.as_str(),
|
||||
"\"))"
|
||||
]),
|
||||
Self::MetaFn { .. } => JsChangeInner::Replace(smallvec![
|
||||
cfg.metafn.as_str(),
|
||||
"(\"",
|
||||
cfg.base.as_str(),
|
||||
"\")"
|
||||
]),
|
||||
|
||||
Self::ScramErr { name, .. } => {
|
||||
JsChangeInner::Replace(smallvec!["$scramerr(", name.as_str(), ");"])
|
||||
}
|
||||
Self::Scramitize { span } => JsChangeInner::Wrap {
|
||||
before: smallvec!["$scramitize("],
|
||||
span,
|
||||
after: smallvec![")"],
|
||||
},
|
||||
|
||||
Self::Eval { inner, .. } => JsChangeInner::Wrap {
|
||||
before: smallvec!["eval(", cfg.rewritefn.as_str(), "("],
|
||||
span: inner,
|
||||
after: smallvec![")"],
|
||||
},
|
||||
Self::Assignment {
|
||||
name, rhsspan, op, ..
|
||||
} => JsChangeInner::Wrap {
|
||||
before: smallvec![
|
||||
"((t)=>$scramjet$tryset(",
|
||||
name.as_str(),
|
||||
",\"",
|
||||
op.as_str(),
|
||||
"\",t)||(",
|
||||
name.as_str(),
|
||||
op.as_str(),
|
||||
"t))("
|
||||
],
|
||||
span: rhsspan,
|
||||
after: smallvec![")"],
|
||||
},
|
||||
Self::ShorthandObj { name, .. } => JsChangeInner::Replace(smallvec![
|
||||
name.as_str(),
|
||||
":",
|
||||
cfg.wrapfn.as_str(),
|
||||
"(",
|
||||
name.as_str(),
|
||||
")"
|
||||
]),
|
||||
Self::SourceTag { tagname, .. } => JsChangeInner::Replace(smallvec![
|
||||
"/*scramtag ",
|
||||
tagname.as_str(),
|
||||
" ",
|
||||
cfg.sourcetag.as_str(),
|
||||
"*/"
|
||||
]),
|
||||
Self::Replace { text, .. } => JsChangeInner::Replace(smallvec![text.as_str()]),
|
||||
Self::Delete { .. } => JsChangeInner::Replace(smallvec![]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for JsChange {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for JsChange {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.get_span().start.cmp(&other.get_span().start)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct JsChangeResult {
|
||||
pub js: Vec<u8>,
|
||||
pub sourcemap: Vec<u8>,
|
||||
}
|
||||
|
||||
pub(crate) struct JsChanges {
|
||||
pub inner: Vec<JsChange>,
|
||||
}
|
||||
|
||||
impl JsChanges {
|
||||
pub fn new() -> Self {
|
||||
Self { inner: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn add(&mut self, change: JsChange) {
|
||||
self.inner.push(change);
|
||||
}
|
||||
|
||||
pub fn perform(&mut self, js: &str, cfg: &Config) -> JsChangeResult {
|
||||
let mut offset = 0;
|
||||
let mut buffer = Vec::with_capacity(((js.len() as u64 * 120) / 100) as usize);
|
||||
|
||||
// TODO: add sourcemaps
|
||||
let map = Vec::with_capacity(js.len() * 2);
|
||||
|
||||
self.inner.sort();
|
||||
|
||||
for change in &self.inner {
|
||||
println!("{:?}", change);
|
||||
let span = change.get_span();
|
||||
let start = span.start as usize;
|
||||
let end = span.end as usize;
|
||||
|
||||
buffer.extend_from_slice(js[offset..start].as_bytes());
|
||||
|
||||
let change = change.to_inner(cfg);
|
||||
match change {
|
||||
JsChangeInner::Wrap {
|
||||
before,
|
||||
span: wrapspan,
|
||||
after,
|
||||
} => {
|
||||
// wrap op
|
||||
for str in before {
|
||||
buffer.extend_from_slice(str.as_bytes());
|
||||
}
|
||||
|
||||
let wrapstart = wrapspan.start as usize;
|
||||
let wrapend = wrapspan.end as usize;
|
||||
buffer.extend_from_slice(js[wrapstart..wrapend].as_bytes());
|
||||
|
||||
for str in after {
|
||||
buffer.extend_from_slice(str.as_bytes());
|
||||
}
|
||||
}
|
||||
JsChangeInner::Replace(list) => {
|
||||
for str in list {
|
||||
buffer.extend_from_slice(str.as_bytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
offset = end;
|
||||
}
|
||||
|
||||
buffer.extend_from_slice(js[offset..].as_bytes());
|
||||
|
||||
JsChangeResult {
|
||||
js: buffer,
|
||||
sourcemap: map,
|
||||
}
|
||||
}
|
||||
}
|
65
rewriter/rewriter/src/lib.rs
Normal file
65
rewriter/rewriter/src/lib.rs
Normal file
|
@ -0,0 +1,65 @@
|
|||
use cfg::Config;
|
||||
use changes::{JsChange, JsChangeResult, JsChanges};
|
||||
use oxc::{
|
||||
allocator::Allocator,
|
||||
ast::Visit,
|
||||
diagnostics::OxcDiagnostic,
|
||||
parser::{ParseOptions, Parser},
|
||||
span::SourceType,
|
||||
};
|
||||
use thiserror::Error;
|
||||
use visitor::Visitor;
|
||||
|
||||
pub mod cfg;
|
||||
pub mod changes;
|
||||
mod visitor;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum RewriterError {
|
||||
#[error("oxc panicked in parser")]
|
||||
OxcPanicked,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RewriteResult {
|
||||
pub js: Vec<u8>,
|
||||
pub sourcemap: Vec<u8>,
|
||||
pub errors: Vec<OxcDiagnostic>,
|
||||
pub changes: Vec<JsChange>,
|
||||
}
|
||||
|
||||
pub fn rewrite(js: &str, config: Config) -> Result<RewriteResult, RewriterError> {
|
||||
let allocator = Allocator::default();
|
||||
let source_type = SourceType::default();
|
||||
let ret = Parser::new(&allocator, js, source_type)
|
||||
.with_options(ParseOptions {
|
||||
parse_regular_expression: false, // default
|
||||
allow_return_outside_function: true,
|
||||
preserve_parens: true, // default
|
||||
})
|
||||
.parse();
|
||||
|
||||
if ret.panicked {
|
||||
return Err(RewriterError::OxcPanicked);
|
||||
}
|
||||
|
||||
let mut visitor = Visitor {
|
||||
jschanges: JsChanges::new(),
|
||||
config,
|
||||
};
|
||||
visitor.visit_program(&ret.program);
|
||||
let Visitor {
|
||||
mut jschanges,
|
||||
config,
|
||||
} = visitor;
|
||||
|
||||
let JsChangeResult { js, sourcemap } = jschanges.perform(js, &config);
|
||||
let JsChanges { inner: changes } = jschanges;
|
||||
|
||||
Ok(RewriteResult {
|
||||
js,
|
||||
sourcemap,
|
||||
errors: ret.errors,
|
||||
changes,
|
||||
})
|
||||
}
|
340
rewriter/rewriter/src/visitor.rs
Normal file
340
rewriter/rewriter/src/visitor.rs
Normal file
|
@ -0,0 +1,340 @@
|
|||
use oxc::{
|
||||
ast::{
|
||||
ast::{
|
||||
AssignmentExpression, AssignmentTarget, CallExpression, DebuggerStatement,
|
||||
ExportAllDeclaration, ExportNamedDeclaration, Expression, ForInStatement,
|
||||
ForOfStatement, FunctionBody, IdentifierReference, ImportDeclaration, ImportExpression,
|
||||
MemberExpression, MetaProperty, NewExpression, ObjectExpression, ObjectPropertyKind,
|
||||
ReturnStatement, ThisExpression, UnaryExpression, UnaryOperator, UpdateExpression,
|
||||
},
|
||||
visit::walk,
|
||||
Visit,
|
||||
},
|
||||
span::{Atom, GetSpan, Span},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
cfg::Config,
|
||||
changes::{JsChange, JsChanges},
|
||||
};
|
||||
|
||||
// js MUST not be able to get a reference to any of these because sbx
|
||||
//
|
||||
// maybe move this out of this lib?
|
||||
const UNSAFE_GLOBALS: &[&str] = &[
|
||||
"window",
|
||||
"self",
|
||||
"globalThis",
|
||||
"this",
|
||||
"parent",
|
||||
"top",
|
||||
"location",
|
||||
"document",
|
||||
"eval",
|
||||
"frames",
|
||||
];
|
||||
|
||||
pub struct Visitor {
|
||||
pub jschanges: JsChanges,
|
||||
pub config: Config,
|
||||
}
|
||||
|
||||
impl Visitor {
|
||||
fn rewrite_url(&mut self, url: String) -> String {
|
||||
let url = self.config.base.join(&url).unwrap();
|
||||
|
||||
let urlencoded = (self.config.encoder)(url.to_string());
|
||||
|
||||
format!("\"{}{}\"", self.config.prefix, urlencoded)
|
||||
}
|
||||
|
||||
fn rewrite_ident(&mut self, name: &Atom, span: Span) {
|
||||
if UNSAFE_GLOBALS.contains(&name.as_str()) {
|
||||
self.jschanges.add(JsChange::WrapFn {
|
||||
span,
|
||||
ident: name.to_compact_str(),
|
||||
wrapped: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_member_expression(&mut self, it: &Expression) -> bool {
|
||||
if match it {
|
||||
Expression::Identifier(s) => {
|
||||
self.rewrite_ident(&s.name, s.span);
|
||||
true
|
||||
}
|
||||
Expression::StaticMemberExpression(s) => self.walk_member_expression(&s.object),
|
||||
Expression::ComputedMemberExpression(s) => self.walk_member_expression(&s.object),
|
||||
_ => false,
|
||||
} {
|
||||
return true;
|
||||
}
|
||||
// TODO: WE SHOULD PROBABLY WALK THE REST OF THE TREE
|
||||
// walk::walk_expression(self, it);
|
||||
false
|
||||
}
|
||||
|
||||
fn scramitize(&mut self, span: Span) {
|
||||
self.jschanges.add(JsChange::Scramitize { span });
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visit<'a> for Visitor {
|
||||
fn visit_identifier_reference(&mut self, it: &IdentifierReference) {
|
||||
// if self.config.capture_errors {
|
||||
// self.jschanges.insert(JsChange::GenericChange {
|
||||
// span: it.span,
|
||||
// text: format!(
|
||||
// "{}({}, typeof arguments != 'undefined' && arguments)",
|
||||
// self.config.wrapfn, it.name
|
||||
// ),
|
||||
// });
|
||||
// } else {
|
||||
//
|
||||
if UNSAFE_GLOBALS.contains(&it.name.as_str()) {
|
||||
self.jschanges.add(JsChange::WrapFn {
|
||||
span: it.span,
|
||||
ident: it.name.to_compact_str(),
|
||||
wrapped: false,
|
||||
});
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
// we need to rewrite `new Something` to `new (wrapfn(Something))` instead of `new wrapfn(Something)`, that's why there's weird extra code here
|
||||
fn visit_new_expression(&mut self, it: &NewExpression) {
|
||||
self.walk_member_expression(&it.callee);
|
||||
walk::walk_arguments(self, &it.arguments);
|
||||
}
|
||||
|
||||
fn visit_member_expression(&mut self, it: &MemberExpression) {
|
||||
match it {
|
||||
MemberExpression::StaticMemberExpression(s) => {
|
||||
if s.property.name == "postMessage" {
|
||||
self.jschanges.add(JsChange::SetRealmFn {
|
||||
span: s.property.span,
|
||||
ident: s.property.name.to_compact_str(),
|
||||
});
|
||||
|
||||
walk::walk_expression(self, &s.object);
|
||||
return; // unwise to walk the rest of the tree
|
||||
}
|
||||
|
||||
if !self.config.strict_rewrites
|
||||
&& !UNSAFE_GLOBALS.contains(&s.property.name.as_str())
|
||||
{
|
||||
if let Expression::Identifier(_) = &s.object {
|
||||
// cull tree - this should be safe
|
||||
return;
|
||||
}
|
||||
if let Expression::ThisExpression(_) = &s.object {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if self.config.scramitize
|
||||
&& !matches!(s.object, Expression::MetaProperty(_) | Expression::Super(_))
|
||||
{
|
||||
self.scramitize(s.object.span());
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// TODO
|
||||
// you could break this with ["postMessage"] etc
|
||||
// however this code only exists because of recaptcha whatever
|
||||
// and it would slow down js execution a lot
|
||||
}
|
||||
}
|
||||
|
||||
walk::walk_member_expression(self, it);
|
||||
}
|
||||
fn visit_this_expression(&mut self, it: &ThisExpression) {
|
||||
self.jschanges.add(JsChange::WrapThisFn { span: it.span });
|
||||
}
|
||||
|
||||
fn visit_debugger_statement(&mut self, it: &DebuggerStatement) {
|
||||
// delete debugger statements entirely. some sites will spam debugger as an anti-debugging measure, and we don't want that!
|
||||
self.jschanges.add(JsChange::Delete { span: it.span });
|
||||
}
|
||||
|
||||
// we can't overwrite window.eval in the normal way because that would make everything an
|
||||
// indirect eval, which could break things. we handle that edge case here
|
||||
fn visit_call_expression(&mut self, it: &CallExpression<'a>) {
|
||||
if let Expression::Identifier(s) = &it.callee {
|
||||
// if it's optional that actually makes it an indirect eval which is handled separately
|
||||
if s.name == "eval" && !it.optional {
|
||||
self.jschanges.add(JsChange::Eval {
|
||||
span: Span::new(it.span.start, it.span.end),
|
||||
inner: Span::new(s.span.end + 1, it.span.end),
|
||||
});
|
||||
|
||||
// then we walk the arguments, but not the callee, since we want it to resolve to
|
||||
// the real eval
|
||||
walk::walk_arguments(self, &it.arguments);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if self.config.scramitize {
|
||||
self.scramitize(it.span);
|
||||
}
|
||||
walk::walk_call_expression(self, it);
|
||||
}
|
||||
|
||||
fn visit_import_declaration(&mut self, it: &ImportDeclaration<'a>) {
|
||||
let name = it.source.value.to_string();
|
||||
let text = self.rewrite_url(name);
|
||||
self.jschanges.add(JsChange::Replace {
|
||||
span: it.source.span,
|
||||
text,
|
||||
});
|
||||
walk::walk_import_declaration(self, it);
|
||||
}
|
||||
fn visit_import_expression(&mut self, it: &ImportExpression<'a>) {
|
||||
self.jschanges.add(JsChange::ImportFn {
|
||||
span: Span::new(it.span.start, it.span.start + 6),
|
||||
});
|
||||
walk::walk_import_expression(self, it);
|
||||
}
|
||||
|
||||
fn visit_export_all_declaration(&mut self, it: &ExportAllDeclaration<'a>) {
|
||||
let name = it.source.value.to_string();
|
||||
let text = self.rewrite_url(name);
|
||||
self.jschanges.add(JsChange::Replace {
|
||||
span: it.source.span,
|
||||
text,
|
||||
});
|
||||
}
|
||||
fn visit_export_named_declaration(&mut self, it: &ExportNamedDeclaration<'a>) {
|
||||
if let Some(source) = &it.source {
|
||||
let name = source.value.to_string();
|
||||
let text = self.rewrite_url(name);
|
||||
self.jschanges.add(JsChange::Replace {
|
||||
span: source.span,
|
||||
text,
|
||||
});
|
||||
}
|
||||
// do not walk further, we don't want to rewrite the identifiers
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
fn visit_try_statement(&mut self, it: &oxc::ast::ast::TryStatement<'a>) {
|
||||
// for debugging we need to know what the error was
|
||||
|
||||
if self.config.capture_errors {
|
||||
if let Some(h) = &it.handler {
|
||||
if let Some(name) = &h.param {
|
||||
if let Some(name) = name.pattern.get_identifier() {
|
||||
self.jschanges.add(JsChange::ScramErr {
|
||||
span: Span::new(h.body.span.start + 1, h.body.span.start + 1),
|
||||
name: name.to_compact_str(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
walk::walk_try_statement(self, it);
|
||||
}
|
||||
|
||||
fn visit_object_expression(&mut self, it: &ObjectExpression<'a>) {
|
||||
for prop in &it.properties {
|
||||
if let ObjectPropertyKind::ObjectProperty(p) = prop {
|
||||
match &p.value {
|
||||
Expression::Identifier(s) => {
|
||||
if UNSAFE_GLOBALS.contains(&s.name.to_string().as_str()) && p.shorthand {
|
||||
self.jschanges.add(JsChange::ShorthandObj {
|
||||
span: s.span,
|
||||
name: s.name.to_compact_str(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
walk::walk_object_expression(self, it);
|
||||
}
|
||||
|
||||
fn visit_function_body(&mut self, it: &FunctionBody<'a>) {
|
||||
// tag function for use in sourcemaps
|
||||
if self.config.do_sourcemaps {
|
||||
self.jschanges.add(JsChange::SourceTag {
|
||||
span: Span::new(it.span.start, it.span.start),
|
||||
tagname: it.span.start.to_string(),
|
||||
});
|
||||
}
|
||||
walk::walk_function_body(self, it);
|
||||
}
|
||||
|
||||
fn visit_return_statement(&mut self, it: &ReturnStatement<'a>) {
|
||||
// if let Some(arg) = &it.argument {
|
||||
// self.jschanges.insert(JsChange::GenericChange {
|
||||
// span: Span::new(it.span.start + 6, it.span.start + 6),
|
||||
// text: format!(" $scramdbg((()=>{{ try {{return arguments}} catch(_){{}} }})(),("),
|
||||
// });
|
||||
// self.jschanges.insert(JsChange::GenericChange {
|
||||
// span: Span::new(expression_span(arg).end, expression_span(arg).end),
|
||||
// text: format!("))"),
|
||||
// });
|
||||
// }
|
||||
walk::walk_return_statement(self, it);
|
||||
}
|
||||
|
||||
fn visit_unary_expression(&mut self, it: &UnaryExpression<'a>) {
|
||||
if matches!(it.operator, UnaryOperator::Typeof) {
|
||||
// don't walk to identifier rewrites since it won't matter
|
||||
return;
|
||||
}
|
||||
walk::walk_unary_expression(self, it);
|
||||
}
|
||||
|
||||
// we don't want to rewrite the identifiers here because of a very specific edge case
|
||||
fn visit_for_in_statement(&mut self, it: &ForInStatement<'a>) {
|
||||
walk::walk_statement(self, &it.body);
|
||||
}
|
||||
fn visit_for_of_statement(&mut self, it: &ForOfStatement<'a>) {
|
||||
walk::walk_statement(self, &it.body);
|
||||
}
|
||||
|
||||
fn visit_update_expression(&mut self, _it: &UpdateExpression<'a>) {
|
||||
// then no, don't walk it, we don't care
|
||||
}
|
||||
|
||||
fn visit_meta_property(&mut self, it: &MetaProperty<'a>) {
|
||||
if it.meta.name == "import" {
|
||||
self.jschanges.add(JsChange::MetaFn { span: it.span });
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_assignment_expression(&mut self, it: &AssignmentExpression<'a>) {
|
||||
match &it.left {
|
||||
AssignmentTarget::AssignmentTargetIdentifier(s) => {
|
||||
if ["location"].contains(&s.name.to_string().as_str()) {
|
||||
self.jschanges.add(JsChange::Assignment {
|
||||
name: s.name.to_compact_str(),
|
||||
entirespan: it.span,
|
||||
rhsspan: it.right.span(),
|
||||
op: it.operator,
|
||||
});
|
||||
|
||||
// avoid walking rest of tree, i would need to figure out nested rewrites
|
||||
// somehow
|
||||
return;
|
||||
}
|
||||
}
|
||||
AssignmentTarget::ArrayAssignmentTarget(_) => {
|
||||
// [location] = ["https://example.com"]
|
||||
// this is such a ridiculously specific edge case. just ignore it
|
||||
return;
|
||||
}
|
||||
_ => {
|
||||
// only walk the left side if it isn't an identifier, we can't replace the
|
||||
// identifier with a function obviously
|
||||
walk::walk_assignment_target(self, &it.left);
|
||||
}
|
||||
}
|
||||
walk::walk_expression(self, &it.right);
|
||||
}
|
||||
}
|
|
@ -2,3 +2,4 @@
|
|||
channel = "nightly"
|
||||
targets = [ "wasm32-unknown-unknown" ]
|
||||
components = [ "rust-src" ]
|
||||
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
use js_sys::Error;
|
||||
use thiserror::Error;
|
||||
use wasm_bindgen::{JsError, JsValue};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum RewriterError {
|
||||
#[error("JS: {0}")]
|
||||
Js(String),
|
||||
#[error("URL parse error: {0}")]
|
||||
Url(#[from] url::ParseError),
|
||||
#[error("str fromutf8 error: {0}")]
|
||||
Str(#[from] std::str::Utf8Error),
|
||||
#[error("reflect set failed: {0}")]
|
||||
ReflectSetFail(String),
|
||||
|
||||
#[error("{0} was not {1}")]
|
||||
Not(String, &'static str),
|
||||
#[error("❗❗❗ ❗❗❗ REWRITER OFFSET OOB FAIL ❗❗❗ ❗❗❗")]
|
||||
Oob,
|
||||
}
|
||||
|
||||
impl From<JsValue> for RewriterError {
|
||||
fn from(value: JsValue) -> Self {
|
||||
Self::Js(Error::from(value).to_string().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RewriterError> for JsValue {
|
||||
fn from(value: RewriterError) -> Self {
|
||||
JsError::from(value).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl RewriterError {
|
||||
pub fn not_str(x: &str) -> Self {
|
||||
Self::Not(x.to_string(), "string")
|
||||
}
|
||||
|
||||
pub fn not_fn(x: &str) -> Self {
|
||||
Self::Not(x.to_string(), "function")
|
||||
}
|
||||
|
||||
pub fn not_bool(x: &str) -> Self {
|
||||
Self::Not(x.to_string(), "bool")
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, RewriterError>;
|
|
@ -1,179 +0,0 @@
|
|||
pub mod error;
|
||||
pub mod rewrite;
|
||||
|
||||
use std::{str::FromStr, sync::Arc, time::Duration};
|
||||
|
||||
use error::{Result, RewriterError};
|
||||
use instant::Instant;
|
||||
use js_sys::{Function, Object, Reflect};
|
||||
use oxc::diagnostics::{NamedSource, OxcDiagnostic};
|
||||
use rewrite::{rewrite, Config, EncodeFn};
|
||||
use url::Url;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen(typescript_custom_section)]
|
||||
const REWRITER_OUTPUT: &'static str = r#"
|
||||
type RewriterOutput = { js: Uint8Array, errors: string[], duration: bigint };
|
||||
"#;
|
||||
|
||||
#[wasm_bindgen(inline_js = r#"
|
||||
// slightly modified https://github.com/ungap/random-uuid/blob/main/index.js
|
||||
export function scramtag() {
|
||||
return (""+1e10).replace(/[018]/g,
|
||||
c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
|
||||
);
|
||||
}
|
||||
"#)]
|
||||
extern "C" {
|
||||
pub fn scramtag() -> String;
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(typescript_type = "RewriterOutput")]
|
||||
pub type RewriterOutput;
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
fn error(s: &str);
|
||||
}
|
||||
|
||||
fn create_encode_function(encode: JsValue) -> Result<EncodeFn> {
|
||||
let encode = encode.dyn_into::<Function>()?;
|
||||
|
||||
Ok(Box::new(move |str| {
|
||||
encode
|
||||
.call1(&JsValue::NULL, &str.into())
|
||||
.unwrap()
|
||||
.as_string()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
}))
|
||||
}
|
||||
|
||||
fn get_obj(obj: &JsValue, k: &str) -> Result<JsValue> {
|
||||
Ok(Reflect::get(obj, &k.into())?)
|
||||
}
|
||||
|
||||
fn get_str(obj: &JsValue, k: &str) -> Result<String> {
|
||||
Reflect::get(obj, &k.into())?
|
||||
.as_string()
|
||||
.ok_or_else(|| RewriterError::not_str(k))
|
||||
}
|
||||
|
||||
fn set_obj(obj: &Object, k: &str, v: &JsValue) -> Result<()> {
|
||||
if !Reflect::set(&obj.into(), &k.into(), v)? {
|
||||
Err(RewriterError::ReflectSetFail(k.to_string()))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn get_flag(scramjet: &Object, url: &str, flag: &str) -> Result<bool> {
|
||||
let fenabled = get_obj(scramjet, "flagEnabled")?
|
||||
.dyn_into::<Function>()
|
||||
.map_err(|_| RewriterError::not_fn("scramjet.flagEnabled"))?;
|
||||
let ret = fenabled.call2(
|
||||
&JsValue::NULL,
|
||||
&flag.into(),
|
||||
&web_sys::Url::new(url)?.into(),
|
||||
)?;
|
||||
|
||||
ret.as_bool().ok_or_else(|| RewriterError::not_bool("scramjet.flagEnabled return value"))
|
||||
}
|
||||
|
||||
fn get_config(scramjet: &Object, url: &str) -> Result<Config> {
|
||||
let codec = &get_obj(scramjet, "codec")?;
|
||||
let config = &get_obj(scramjet, "config")?;
|
||||
let globals = &get_obj(config, "globals")?;
|
||||
|
||||
Ok(Config {
|
||||
prefix: get_str(config, "prefix")?,
|
||||
encode: create_encode_function(get_obj(codec, "encode")?)?,
|
||||
|
||||
wrapfn: get_str(globals, "wrapfn")?,
|
||||
wrapthisfn: get_str(globals, "wrapthisfn")?,
|
||||
importfn: get_str(globals, "importfn")?,
|
||||
rewritefn: get_str(globals, "rewritefn")?,
|
||||
metafn: get_str(globals, "metafn")?,
|
||||
setrealmfn: get_str(globals, "setrealmfn")?,
|
||||
pushsourcemapfn: get_str(globals, "pushsourcemapfn")?,
|
||||
|
||||
do_sourcemaps: get_flag(scramjet, url, "sourcemaps")?,
|
||||
capture_errors: get_flag(scramjet, url, "captureErrors")?,
|
||||
scramitize: get_flag(scramjet, url, "scramitize")?,
|
||||
strict_rewrites: get_flag(scramjet, url, "strictRewrites")?,
|
||||
})
|
||||
}
|
||||
|
||||
fn duration_to_millis_f64(duration: Duration) -> f64 {
|
||||
(duration.as_secs() as f64) * 1_000f64 + (duration.subsec_nanos() as f64) / 1_000_000f64
|
||||
}
|
||||
|
||||
fn create_rewriter_output(
|
||||
out: (Vec<u8>, Vec<OxcDiagnostic>),
|
||||
url: String,
|
||||
src: String,
|
||||
duration: Duration,
|
||||
) -> Result<RewriterOutput> {
|
||||
let src = Arc::new(NamedSource::new(url, src).with_language("javascript"));
|
||||
#[cfg(feature = "debug")]
|
||||
let errs: Vec<_> = out
|
||||
.1
|
||||
.into_iter()
|
||||
.map(|x| format!("{}", x.with_source_code(src.clone())))
|
||||
.collect();
|
||||
|
||||
let obj = Object::new();
|
||||
set_obj(&obj, "js", &out.0.into())?;
|
||||
#[cfg(feature = "debug")]
|
||||
set_obj(&obj, "errors", &errs.into())?;
|
||||
#[cfg(not(feature = "debug"))]
|
||||
set_obj(&obj, "errors", &js_sys::Array::new())?;
|
||||
set_obj(&obj, "duration", &duration_to_millis_f64(duration).into())?;
|
||||
|
||||
Ok(RewriterOutput::from(JsValue::from(obj)))
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn rewrite_js(
|
||||
js: String,
|
||||
url: &str,
|
||||
script_url: String,
|
||||
scramjet: &Object,
|
||||
) -> Result<RewriterOutput> {
|
||||
let before = Instant::now();
|
||||
let out = rewrite(
|
||||
&js,
|
||||
Url::from_str(url)?,
|
||||
scramtag(),
|
||||
get_config(scramjet, url)?,
|
||||
)?;
|
||||
let after = Instant::now();
|
||||
|
||||
create_rewriter_output(out, script_url, js, after - before)
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn rewrite_js_from_arraybuffer(
|
||||
js: Vec<u8>,
|
||||
url: &str,
|
||||
script_url: String,
|
||||
scramjet: &Object,
|
||||
) -> Result<RewriterOutput> {
|
||||
// we know that this is a valid utf-8 string
|
||||
let js = unsafe { String::from_utf8_unchecked(js) };
|
||||
|
||||
let before = Instant::now();
|
||||
let out = rewrite(
|
||||
&js,
|
||||
Url::from_str(url)?,
|
||||
scramtag(),
|
||||
get_config(scramjet, url)?,
|
||||
)?;
|
||||
let after = Instant::now();
|
||||
|
||||
create_rewriter_output(out, script_url, js, after - before)
|
||||
}
|
|
@ -1,255 +0,0 @@
|
|||
#![allow(clippy::print_stdout)]
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
env,
|
||||
path::Path,
|
||||
str::{from_utf8, FromStr},
|
||||
};
|
||||
|
||||
pub mod error;
|
||||
pub mod rewrite;
|
||||
|
||||
use error::Result;
|
||||
use rewrite::rewrite;
|
||||
use url::Url;
|
||||
|
||||
use crate::rewrite::Config;
|
||||
|
||||
// Instruction:
|
||||
// create a `test.js`,
|
||||
// run `cargo run -p oxc_parser --example visitor`
|
||||
// or `cargo watch -x "run -p oxc_parser --example visitor"`
|
||||
|
||||
/// Percent-encodes every byte except alphanumerics and `-`, `_`, `.`, `~`. Assumes UTF-8 encoding.
|
||||
///
|
||||
/// Call `.into_owned()` if you need a `String`
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn encode(data: &str) -> Cow<'_, str> {
|
||||
encode_binary(data.as_bytes())
|
||||
}
|
||||
|
||||
/// Percent-encodes every byte except alphanumerics and `-`, `_`, `.`, `~`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn encode_binary(data: &[u8]) -> Cow<'_, str> {
|
||||
// add maybe extra capacity, but try not to exceed allocator's bucket size
|
||||
let mut escaped = String::new();
|
||||
let _ = escaped.try_reserve(data.len() | 15);
|
||||
let unmodified = append_string(data, &mut escaped, true);
|
||||
if unmodified {
|
||||
return Cow::Borrowed(unsafe {
|
||||
// encode_into has checked it's ASCII
|
||||
std::str::from_utf8_unchecked(data)
|
||||
});
|
||||
}
|
||||
Cow::Owned(escaped)
|
||||
}
|
||||
|
||||
fn append_string(data: &[u8], escaped: &mut String, may_skip: bool) -> bool {
|
||||
encode_into(data, may_skip, |s| {
|
||||
escaped.push_str(s);
|
||||
Ok::<_, std::convert::Infallible>(())
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn encode_into<E>(
|
||||
mut data: &[u8],
|
||||
may_skip_write: bool,
|
||||
mut push_str: impl FnMut(&str) -> std::result::Result<(), E>,
|
||||
) -> std::result::Result<bool, E> {
|
||||
let mut pushed = false;
|
||||
loop {
|
||||
// Fast path to skip over safe chars at the beginning of the remaining string
|
||||
let ascii_len = data
|
||||
.iter()
|
||||
.take_while(
|
||||
|&&c| matches!(c, b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z' | b'-' | b'.' | b'_' | b'~'),
|
||||
)
|
||||
.count();
|
||||
|
||||
let (safe, rest) = if ascii_len >= data.len() {
|
||||
if !pushed && may_skip_write {
|
||||
return Ok(true);
|
||||
}
|
||||
(data, &[][..]) // redundatnt to optimize out a panic in split_at
|
||||
} else {
|
||||
data.split_at(ascii_len)
|
||||
};
|
||||
pushed = true;
|
||||
if !safe.is_empty() {
|
||||
push_str(unsafe { std::str::from_utf8_unchecked(safe) })?;
|
||||
}
|
||||
if rest.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
match rest.split_first() {
|
||||
Some((byte, rest)) => {
|
||||
let enc = &[b'%', to_hex_digit(byte >> 4), to_hex_digit(byte & 15)];
|
||||
push_str(unsafe { std::str::from_utf8_unchecked(enc) })?;
|
||||
data = rest;
|
||||
}
|
||||
None => break,
|
||||
};
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_hex_digit(digit: u8) -> u8 {
|
||||
match digit {
|
||||
0..=9 => b'0' + digit,
|
||||
10..=255 => b'A' - 10 + digit,
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_string(s: String) -> String {
|
||||
encode(&s).to_string()
|
||||
}
|
||||
|
||||
fn dorewrite(source_text: &str) -> Result<String> {
|
||||
Ok(from_utf8(
|
||||
rewrite(
|
||||
source_text,
|
||||
Url::from_str("https://google.com/glorngle/si.js").unwrap(),
|
||||
"glongle1".to_string(),
|
||||
Config {
|
||||
prefix: "/scrammedjet/".to_string(),
|
||||
encode: Box::new(encode_string),
|
||||
wrapfn: "$wrap".to_string(),
|
||||
wrapthisfn: "$gwrap".to_string(),
|
||||
importfn: "$import".to_string(),
|
||||
rewritefn: "$rewrite".to_string(),
|
||||
metafn: "$meta".to_string(),
|
||||
setrealmfn: "$setrealm".to_string(),
|
||||
pushsourcemapfn: "$pushsourcemap".to_string(),
|
||||
capture_errors: true,
|
||||
do_sourcemaps: true,
|
||||
scramitize: false,
|
||||
strict_rewrites: true,
|
||||
},
|
||||
)?
|
||||
.0
|
||||
.as_slice(),
|
||||
)?
|
||||
.to_string())
|
||||
}
|
||||
|
||||
fn main() -> std::io::Result<()> {
|
||||
let name = env::args().nth(1).unwrap_or_else(|| "test.js".to_string());
|
||||
let path = Path::new(&name);
|
||||
let source_text = std::fs::read_to_string(path)?;
|
||||
|
||||
println!("{}", dorewrite(&source_text).unwrap());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::fs;
|
||||
|
||||
use boa_engine::{
|
||||
js_str, js_string,
|
||||
object::ObjectInitializer,
|
||||
property::{Attribute, PropertyDescriptorBuilder},
|
||||
Context, NativeFunction, Source,
|
||||
};
|
||||
|
||||
use crate::dorewrite;
|
||||
|
||||
#[test]
|
||||
fn google() {
|
||||
// sanity check- just making sure it won't crash
|
||||
let source_text = include_str!("../sample/google.js");
|
||||
dorewrite(source_text).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let files = fs::read_dir("./tests").unwrap();
|
||||
|
||||
for file in files {
|
||||
if !file
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.file_name()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.ends_with(".js")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let content = fs::read_to_string(file.unwrap().path()).unwrap();
|
||||
|
||||
let mut context = Context::default();
|
||||
|
||||
let window = ObjectInitializer::new(&mut context).build();
|
||||
context
|
||||
.register_global_property(js_str!("window"), window, Attribute::READONLY)
|
||||
.unwrap();
|
||||
context
|
||||
.global_object()
|
||||
.define_property_or_throw(
|
||||
js_str!("location"),
|
||||
PropertyDescriptorBuilder::new()
|
||||
.get(
|
||||
NativeFunction::from_copy_closure(|_, _, _| {
|
||||
Ok(js_str!("location").into())
|
||||
})
|
||||
.to_js_function(context.realm()),
|
||||
)
|
||||
.set(
|
||||
NativeFunction::from_copy_closure(|_, _, _| {
|
||||
panic!("fail: window.location got set")
|
||||
})
|
||||
.to_js_function(context.realm()),
|
||||
)
|
||||
.build(),
|
||||
&mut context,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
context
|
||||
.register_global_callable(
|
||||
js_string!("fail"),
|
||||
0,
|
||||
NativeFunction::from_copy_closure(|_, _, _| {
|
||||
panic!("fail");
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let result = context
|
||||
.eval(Source::from_bytes(
|
||||
br#"
|
||||
function $wrap(val) {
|
||||
if (val === window || val === "location" || val === globalThis) return "";
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
function assert(val) {
|
||||
if (!val) fail();
|
||||
}
|
||||
|
||||
function check(val) {
|
||||
if (val === window || val === "location") fail();
|
||||
}
|
||||
"#,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
let rewritten = dorewrite(&content).unwrap();
|
||||
println!("{}", rewritten);
|
||||
|
||||
context
|
||||
.eval(Source::from_bytes(rewritten.as_bytes()))
|
||||
.unwrap();
|
||||
println!("PASS");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,591 +0,0 @@
|
|||
use std::str;
|
||||
|
||||
use indexset::BTreeSet;
|
||||
use oxc::{
|
||||
allocator::Allocator,
|
||||
ast::{
|
||||
ast::{
|
||||
AssignmentExpression, AssignmentTarget, CallExpression, DebuggerStatement,
|
||||
ExportAllDeclaration, ExportNamedDeclaration, Expression, ForInStatement,
|
||||
ForOfStatement, FunctionBody, IdentifierReference, ImportDeclaration, ImportExpression,
|
||||
MemberExpression, MetaProperty, NewExpression, ObjectExpression, ObjectPropertyKind,
|
||||
ReturnStatement, ThisExpression, UnaryExpression, UpdateExpression,
|
||||
},
|
||||
visit::walk,
|
||||
Visit,
|
||||
},
|
||||
diagnostics::OxcDiagnostic,
|
||||
parser::{ParseOptions, Parser},
|
||||
span::{Atom, GetSpan, SourceType, Span},
|
||||
syntax::operator::{AssignmentOperator, UnaryOperator},
|
||||
};
|
||||
use url::Url;
|
||||
|
||||
use crate::error::{Result, RewriterError};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum JsChange {
|
||||
GenericChange {
|
||||
span: Span,
|
||||
text: String,
|
||||
},
|
||||
SourceTag {
|
||||
tagstart: u32,
|
||||
},
|
||||
Assignment {
|
||||
name: String,
|
||||
entirespan: Span,
|
||||
rhsspan: Span,
|
||||
op: AssignmentOperator,
|
||||
},
|
||||
}
|
||||
|
||||
impl JsChange {
|
||||
fn inner_cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
let a = match self {
|
||||
JsChange::GenericChange { span, text: _ } => span.start,
|
||||
JsChange::Assignment {
|
||||
name: _,
|
||||
entirespan,
|
||||
rhsspan: _,
|
||||
op: _,
|
||||
} => entirespan.start,
|
||||
JsChange::SourceTag { tagstart } => *tagstart,
|
||||
};
|
||||
let b = match other {
|
||||
JsChange::GenericChange { span, text: _ } => span.start,
|
||||
JsChange::Assignment {
|
||||
name: _,
|
||||
entirespan,
|
||||
rhsspan: _,
|
||||
op: _,
|
||||
} => entirespan.start,
|
||||
JsChange::SourceTag { tagstart } => *tagstart,
|
||||
};
|
||||
a.cmp(&b)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for JsChange {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.inner_cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for JsChange {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.inner_cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
pub type EncodeFn = Box<dyn Fn(String) -> String>;
|
||||
struct Rewriter {
|
||||
jschanges: BTreeSet<JsChange>,
|
||||
base: Url,
|
||||
config: Config,
|
||||
}
|
||||
|
||||
pub struct Config {
|
||||
pub prefix: String,
|
||||
|
||||
pub wrapfn: String,
|
||||
pub wrapthisfn: String,
|
||||
pub importfn: String,
|
||||
pub rewritefn: String,
|
||||
pub setrealmfn: String,
|
||||
pub metafn: String,
|
||||
pub pushsourcemapfn: String,
|
||||
|
||||
pub encode: EncodeFn,
|
||||
pub capture_errors: bool,
|
||||
pub scramitize: bool,
|
||||
pub do_sourcemaps: bool,
|
||||
pub strict_rewrites: bool,
|
||||
}
|
||||
|
||||
impl Rewriter {
|
||||
fn rewrite_url(&mut self, url: String) -> String {
|
||||
let url = self.base.join(&url).unwrap();
|
||||
|
||||
let urlencoded = (self.config.encode)(url.to_string());
|
||||
|
||||
format!("\"{}{}\"", self.config.prefix, urlencoded)
|
||||
}
|
||||
|
||||
fn rewrite_ident(&mut self, name: &Atom, span: Span) {
|
||||
if UNSAFE_GLOBALS.contains(&name.to_string().as_str()) {
|
||||
self.jschanges.insert(JsChange::GenericChange {
|
||||
span,
|
||||
text: format!("({}({}))", self.config.wrapfn, name),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_member_expression(&mut self, it: &Expression) -> bool {
|
||||
if match it {
|
||||
Expression::Identifier(s) => {
|
||||
self.rewrite_ident(&s.name, s.span);
|
||||
true
|
||||
}
|
||||
Expression::StaticMemberExpression(s) => self.walk_member_expression(&s.object),
|
||||
Expression::ComputedMemberExpression(s) => self.walk_member_expression(&s.object),
|
||||
_ => false,
|
||||
} {
|
||||
return true;
|
||||
}
|
||||
// TODO: WE SHOULD PROBABLY WALK THE REST OF THE TREE
|
||||
// walk::walk_expression(self, it);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visit<'a> for Rewriter {
|
||||
fn visit_identifier_reference(&mut self, it: &IdentifierReference<'a>) {
|
||||
// if self.config.capture_errors {
|
||||
// self.jschanges.insert(JsChange::GenericChange {
|
||||
// span: it.span,
|
||||
// text: format!(
|
||||
// "{}({}, typeof arguments != 'undefined' && arguments)",
|
||||
// self.config.wrapfn, it.name
|
||||
// ),
|
||||
// });
|
||||
// } else {
|
||||
if UNSAFE_GLOBALS.contains(&it.name.to_string().as_str()) {
|
||||
self.jschanges.insert(JsChange::GenericChange {
|
||||
span: it.span,
|
||||
text: format!("{}({})", self.config.wrapfn, it.name),
|
||||
});
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
// we need to rewrite `new Something` to `new (wrapfn(Something))` instead of `new wrapfn(Something)`, that's why there's weird extra code here
|
||||
fn visit_new_expression(&mut self, it: &NewExpression<'a>) {
|
||||
self.walk_member_expression(&it.callee);
|
||||
walk::walk_arguments(self, &it.arguments);
|
||||
}
|
||||
fn visit_member_expression(&mut self, it: &MemberExpression<'a>) {
|
||||
match it {
|
||||
MemberExpression::StaticMemberExpression(s) => {
|
||||
if s.property.name == "postMessage" {
|
||||
self.jschanges.insert(JsChange::GenericChange {
|
||||
span: s.property.span,
|
||||
// an empty object will let us safely reconstruct the realm later
|
||||
text: format!("{}({{}}).{}", self.config.setrealmfn, s.property.name),
|
||||
});
|
||||
|
||||
walk::walk_expression(self, &s.object);
|
||||
return; // unwise to walk the rest of the tree
|
||||
}
|
||||
|
||||
if !self.config.strict_rewrites
|
||||
&& !UNSAFE_GLOBALS.contains(&s.property.name.as_str())
|
||||
{
|
||||
if let Expression::Identifier(_) = &s.object {
|
||||
// cull tree - this should be safe
|
||||
return;
|
||||
}
|
||||
if let Expression::ThisExpression(_) = &s.object {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if self.config.scramitize
|
||||
&& !matches!(s.object, Expression::MetaProperty(_))
|
||||
&& !matches!(s.object, Expression::Super(_))
|
||||
{
|
||||
let span = s.object.span();
|
||||
self.jschanges.insert(JsChange::GenericChange {
|
||||
span: Span::new(span.start, span.start),
|
||||
text: " $scramitize(".to_string(),
|
||||
});
|
||||
self.jschanges.insert(JsChange::GenericChange {
|
||||
span: Span::new(span.end, span.end),
|
||||
text: ")".to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// TODO
|
||||
// you could break this with ["postMessage"] etc
|
||||
// however this code only exists because of recaptcha whatever
|
||||
// and it would slow down js execution a lot
|
||||
}
|
||||
}
|
||||
|
||||
walk::walk_member_expression(self, it);
|
||||
}
|
||||
fn visit_this_expression(&mut self, it: &ThisExpression) {
|
||||
self.jschanges.insert(JsChange::GenericChange {
|
||||
span: it.span,
|
||||
text: format!("{}(this)", self.config.wrapthisfn),
|
||||
});
|
||||
}
|
||||
|
||||
fn visit_debugger_statement(&mut self, it: &DebuggerStatement) {
|
||||
// delete debugger statements entirely. some sites will spam debugger as an anti-debugging measure, and we don't want that!
|
||||
self.jschanges.insert(JsChange::GenericChange {
|
||||
span: it.span,
|
||||
text: "".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
// we can't overwrite window.eval in the normal way because that would make everything an
|
||||
// indirect eval, which could break things. we handle that edge case here
|
||||
fn visit_call_expression(&mut self, it: &CallExpression<'a>) {
|
||||
if let Expression::Identifier(s) = &it.callee {
|
||||
// if it's optional that actually makes it an indirect eval which is handled separately
|
||||
if s.name == "eval" && !it.optional {
|
||||
self.jschanges.insert(JsChange::GenericChange {
|
||||
span: Span::new(s.span.start, s.span.end + 1),
|
||||
text: format!("eval({}(", self.config.rewritefn),
|
||||
});
|
||||
self.jschanges.insert(JsChange::GenericChange {
|
||||
span: Span::new(it.span.end, it.span.end),
|
||||
text: ")".to_string(),
|
||||
});
|
||||
|
||||
// then we walk the arguments, but not the callee, since we want it to resolve to
|
||||
// the real eval
|
||||
walk::walk_arguments(self, &it.arguments);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if self.config.scramitize {
|
||||
self.jschanges.insert(JsChange::GenericChange {
|
||||
span: Span::new(it.span.start, it.span.start),
|
||||
text: " $scramitize(".to_string(),
|
||||
});
|
||||
self.jschanges.insert(JsChange::GenericChange {
|
||||
span: Span::new(it.span.end, it.span.end),
|
||||
text: ")".to_string(),
|
||||
});
|
||||
}
|
||||
walk::walk_call_expression(self, it);
|
||||
}
|
||||
|
||||
fn visit_import_declaration(&mut self, it: &ImportDeclaration<'a>) {
|
||||
let name = it.source.value.to_string();
|
||||
let text = self.rewrite_url(name);
|
||||
self.jschanges.insert(JsChange::GenericChange {
|
||||
span: it.source.span,
|
||||
text,
|
||||
});
|
||||
walk::walk_import_declaration(self, it);
|
||||
}
|
||||
fn visit_import_expression(&mut self, it: &ImportExpression<'a>) {
|
||||
self.jschanges.insert(JsChange::GenericChange {
|
||||
span: Span::new(it.span.start, it.span.start + 6),
|
||||
text: format!("({}(\"{}\"))", self.config.importfn, self.base),
|
||||
});
|
||||
walk::walk_import_expression(self, it);
|
||||
}
|
||||
|
||||
fn visit_export_all_declaration(&mut self, it: &ExportAllDeclaration<'a>) {
|
||||
let name = it.source.value.to_string();
|
||||
let text = self.rewrite_url(name);
|
||||
self.jschanges.insert(JsChange::GenericChange {
|
||||
span: it.source.span,
|
||||
text,
|
||||
});
|
||||
}
|
||||
|
||||
fn visit_export_named_declaration(&mut self, it: &ExportNamedDeclaration<'a>) {
|
||||
if let Some(source) = &it.source {
|
||||
let name = source.value.to_string();
|
||||
let text = self.rewrite_url(name);
|
||||
self.jschanges.insert(JsChange::GenericChange {
|
||||
span: source.span,
|
||||
text,
|
||||
});
|
||||
}
|
||||
// do not walk further, we don't want to rewrite the identifiers
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
fn visit_try_statement(&mut self, it: &oxc::ast::ast::TryStatement<'a>) {
|
||||
// for debugging we need to know what the error was
|
||||
|
||||
if self.config.capture_errors {
|
||||
if let Some(h) = &it.handler {
|
||||
if let Some(name) = &h.param {
|
||||
if let Some(name) = name.pattern.get_identifier() {
|
||||
self.jschanges.insert(JsChange::GenericChange {
|
||||
span: Span::new(h.body.span.start + 1, h.body.span.start + 1),
|
||||
text: format!("$scramerr({});", name),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
walk::walk_try_statement(self, it);
|
||||
}
|
||||
|
||||
fn visit_object_expression(&mut self, it: &ObjectExpression<'a>) {
|
||||
for prop in &it.properties {
|
||||
#[allow(clippy::single_match)]
|
||||
match prop {
|
||||
ObjectPropertyKind::ObjectProperty(p) => match &p.value {
|
||||
Expression::Identifier(s) => {
|
||||
if UNSAFE_GLOBALS.contains(&s.name.to_string().as_str()) && p.shorthand {
|
||||
self.jschanges.insert(JsChange::GenericChange {
|
||||
span: s.span,
|
||||
text: format!("{}: ({}({}))", s.name, self.config.wrapfn, s.name),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
walk::walk_object_expression(self, it);
|
||||
}
|
||||
|
||||
fn visit_function_body(&mut self, it: &FunctionBody<'a>) {
|
||||
// tag function for use in sourcemaps
|
||||
if self.config.do_sourcemaps {
|
||||
self.jschanges.insert(JsChange::SourceTag {
|
||||
tagstart: it.span.start,
|
||||
});
|
||||
}
|
||||
walk::walk_function_body(self, it);
|
||||
}
|
||||
|
||||
fn visit_return_statement(&mut self, it: &ReturnStatement<'a>) {
|
||||
// if let Some(arg) = &it.argument {
|
||||
// self.jschanges.insert(JsChange::GenericChange {
|
||||
// span: Span::new(it.span.start + 6, it.span.start + 6),
|
||||
// text: format!(" $scramdbg((()=>{{ try {{return arguments}} catch(_){{}} }})(),("),
|
||||
// });
|
||||
// self.jschanges.insert(JsChange::GenericChange {
|
||||
// span: Span::new(expression_span(arg).end, expression_span(arg).end),
|
||||
// text: format!("))"),
|
||||
// });
|
||||
// }
|
||||
walk::walk_return_statement(self, it);
|
||||
}
|
||||
|
||||
fn visit_unary_expression(&mut self, it: &UnaryExpression<'a>) {
|
||||
if matches!(it.operator, UnaryOperator::Typeof) {
|
||||
// don't walk to identifier rewrites since it won't matter
|
||||
return;
|
||||
}
|
||||
walk::walk_unary_expression(self, it);
|
||||
}
|
||||
|
||||
// we don't want to rewrite the identifiers here because of a very specific edge case
|
||||
fn visit_for_in_statement(&mut self, it: &ForInStatement<'a>) {
|
||||
walk::walk_statement(self, &it.body);
|
||||
}
|
||||
fn visit_for_of_statement(&mut self, it: &ForOfStatement<'a>) {
|
||||
walk::walk_statement(self, &it.body);
|
||||
}
|
||||
|
||||
fn visit_update_expression(&mut self, _it: &UpdateExpression<'a>) {
|
||||
// then no, don't walk it, we don't care
|
||||
}
|
||||
|
||||
fn visit_meta_property(&mut self, it: &MetaProperty<'a>) {
|
||||
if it.meta.name == "import" {
|
||||
self.jschanges.insert(JsChange::GenericChange {
|
||||
span: it.span,
|
||||
text: format!("{}(\"{}\")", self.config.metafn, self.base),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_assignment_expression(&mut self, it: &AssignmentExpression<'a>) {
|
||||
#[allow(clippy::single_match)]
|
||||
match &it.left {
|
||||
AssignmentTarget::AssignmentTargetIdentifier(s) => {
|
||||
if ["location"].contains(&s.name.to_string().as_str()) {
|
||||
self.jschanges.insert(JsChange::Assignment {
|
||||
name: s.name.to_string(),
|
||||
entirespan: it.span,
|
||||
rhsspan: it.right.span(),
|
||||
op: it.operator,
|
||||
});
|
||||
|
||||
// avoid walking rest of tree, i would need to figure out nested rewrites
|
||||
// somehow
|
||||
return;
|
||||
}
|
||||
}
|
||||
AssignmentTarget::ArrayAssignmentTarget(_) => {
|
||||
// [location] = ["https://example.com"]
|
||||
// this is such a ridiculously specific edge case. just ignore it
|
||||
return;
|
||||
}
|
||||
_ => {
|
||||
// only walk the left side if it isn't an identifier, we can't replace the
|
||||
// identifier with a function obviously
|
||||
walk::walk_assignment_target(self, &it.left);
|
||||
}
|
||||
}
|
||||
walk::walk_expression(self, &it.right);
|
||||
}
|
||||
}
|
||||
|
||||
// js MUST not be able to get a reference to any of these because sbx
|
||||
const UNSAFE_GLOBALS: &[&str] = &[
|
||||
"window",
|
||||
"self",
|
||||
"globalThis",
|
||||
"this",
|
||||
"parent",
|
||||
"top",
|
||||
"location",
|
||||
"document",
|
||||
"eval",
|
||||
"frames",
|
||||
];
|
||||
|
||||
pub fn rewrite(
|
||||
js: &str,
|
||||
url: Url,
|
||||
sourcetag: String,
|
||||
config: Config,
|
||||
) -> Result<(Vec<u8>, Vec<OxcDiagnostic>)> {
|
||||
let allocator = Allocator::default();
|
||||
let source_type = SourceType::default();
|
||||
let ret = Parser::new(&allocator, js, source_type)
|
||||
.with_options(ParseOptions {
|
||||
parse_regular_expression: false, // default
|
||||
allow_return_outside_function: true,
|
||||
preserve_parens: true, // default
|
||||
})
|
||||
.parse();
|
||||
|
||||
let program = ret.program;
|
||||
|
||||
let mut ast_pass = Rewriter {
|
||||
jschanges: BTreeSet::new(),
|
||||
base: url,
|
||||
config,
|
||||
};
|
||||
|
||||
ast_pass.visit_program(&program);
|
||||
|
||||
let original_len = js.len();
|
||||
let mut difference = 0i32;
|
||||
|
||||
for change in &ast_pass.jschanges {
|
||||
match &change {
|
||||
JsChange::GenericChange { span, text } => {
|
||||
difference += text.len() as i32 - (span.end - span.start) as i32;
|
||||
}
|
||||
JsChange::Assignment {
|
||||
name,
|
||||
entirespan,
|
||||
rhsspan: _,
|
||||
op: _,
|
||||
} => difference += entirespan.size() as i32 + name.len() as i32 + 10,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let size_estimate = (original_len as i32 + difference) as usize;
|
||||
let mut buffer: Vec<u8> = Vec::with_capacity(size_estimate);
|
||||
|
||||
let mut sourcemap: Vec<u8> = Vec::new();
|
||||
if ast_pass.config.do_sourcemaps {
|
||||
sourcemap.reserve(size_estimate * 2);
|
||||
sourcemap.extend_from_slice(format!("{}([", ast_pass.config.pushsourcemapfn).as_bytes());
|
||||
}
|
||||
|
||||
let mut offset = 0;
|
||||
for change in ast_pass.jschanges {
|
||||
match &change {
|
||||
JsChange::GenericChange { span, text } => {
|
||||
let start = span.start as usize;
|
||||
let end = span.end as usize;
|
||||
|
||||
if ast_pass.config.do_sourcemaps {
|
||||
let spliced = &js[start..end];
|
||||
sourcemap.extend_from_slice(
|
||||
format!(
|
||||
"[\"{}\",{},{}],",
|
||||
json_escape_string(spliced),
|
||||
start,
|
||||
start + text.len()
|
||||
)
|
||||
.as_bytes(),
|
||||
);
|
||||
}
|
||||
|
||||
buffer
|
||||
.extend_from_slice(js.get(offset..start).ok_or(RewriterError::Oob)?.as_bytes());
|
||||
|
||||
buffer.extend_from_slice(text.as_bytes());
|
||||
offset = end;
|
||||
}
|
||||
JsChange::Assignment {
|
||||
name,
|
||||
entirespan,
|
||||
rhsspan,
|
||||
op,
|
||||
} => {
|
||||
let start = entirespan.start as usize;
|
||||
buffer.extend_from_slice(js[offset..start].as_bytes());
|
||||
|
||||
buffer.extend_from_slice(
|
||||
format!(
|
||||
"((t)=>$scramjet$tryset({},\"{}\",t)||({}{}t))({})",
|
||||
name,
|
||||
op.as_str(),
|
||||
name,
|
||||
op.as_str(),
|
||||
&js[rhsspan.start as usize..rhsspan.end as usize]
|
||||
)
|
||||
.as_bytes(),
|
||||
);
|
||||
|
||||
offset = entirespan.end as usize;
|
||||
}
|
||||
JsChange::SourceTag { tagstart } => {
|
||||
let start = *tagstart as usize;
|
||||
buffer
|
||||
.extend_from_slice(js.get(offset..start).ok_or(RewriterError::Oob)?.as_bytes());
|
||||
|
||||
let inject = format!("/*scramtag {} {}*/", start, sourcetag);
|
||||
buffer.extend_from_slice(inject.as_bytes());
|
||||
|
||||
offset = start;
|
||||
}
|
||||
}
|
||||
}
|
||||
buffer.extend_from_slice(js[offset..].as_bytes());
|
||||
|
||||
if ast_pass.config.do_sourcemaps {
|
||||
sourcemap.extend_from_slice(b"],");
|
||||
sourcemap.extend_from_slice(b"\"");
|
||||
sourcemap.extend_from_slice(sourcetag.as_bytes());
|
||||
sourcemap.extend_from_slice(b"\");\n");
|
||||
|
||||
sourcemap.extend_from_slice(&buffer);
|
||||
|
||||
return Ok((sourcemap, ret.errors));
|
||||
}
|
||||
|
||||
Ok((buffer, ret.errors))
|
||||
}
|
||||
|
||||
fn json_escape_string(s: &str) -> String {
|
||||
let mut out = String::with_capacity(s.len());
|
||||
for c in s.chars() {
|
||||
match c {
|
||||
'"' => out.push_str("\\\""),
|
||||
'\\' => out.push_str("\\\\"),
|
||||
'\x08' => out.push_str("\\b"),
|
||||
'\x0C' => out.push_str("\\f"),
|
||||
'\n' => out.push_str("\\n"),
|
||||
'\r' => out.push_str("\\r"),
|
||||
'\t' => out.push_str("\\t"),
|
||||
_ => out.push(c),
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
check(window);
|
||||
check(this);
|
||||
check(globalThis);
|
||||
check(location);
|
||||
check(globalThis["win" + "dow"])
|
|
@ -1,3 +0,0 @@
|
|||
const { location: x } = globalThis;
|
||||
|
||||
check(x);
|
|
@ -1,6 +0,0 @@
|
|||
function f(g = globalThis, l = location) {
|
||||
check(g);
|
||||
check(l);
|
||||
}
|
||||
|
||||
f();
|
|
@ -1,7 +0,0 @@
|
|||
|
||||
function f(location, globalThis) {
|
||||
assert(location === 1)
|
||||
assert(globalThis === 2)
|
||||
}
|
||||
|
||||
f(1, 2);
|
|
@ -1,8 +0,0 @@
|
|||
|
||||
let reached = false
|
||||
let eval = (t) => t === "location = 1" && (reached = true)
|
||||
|
||||
// testing to make sure this doesn't get rewritten
|
||||
eval("location = 1")
|
||||
|
||||
if (!reached) fail();
|
|
@ -1,3 +0,0 @@
|
|||
for (location of ["https://google.com"]) {
|
||||
//
|
||||
}
|
21
rewriter/wasm/Cargo.toml
Normal file
21
rewriter/wasm/Cargo.toml
Normal file
|
@ -0,0 +1,21 @@
|
|||
[package]
|
||||
name = "wasm"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
instant = { version = "0.1.13", features = ["wasm-bindgen"] }
|
||||
js-sys = "0.3.76"
|
||||
oxc = { workspace = true }
|
||||
rewriter = { version = "0.1.0", path = "../rewriter" }
|
||||
thiserror = "2.0.6"
|
||||
url = "2.5.4"
|
||||
wasm-bindgen = "0.2.99"
|
||||
web-sys = { version = "0.3.76", features = ["Url"] }
|
||||
|
||||
[features]
|
||||
default = ["debug"]
|
||||
debug = ["rewriter/debug"]
|
14
rewriter/build.sh → rewriter/wasm/build.sh
Executable file → Normal file
14
rewriter/build.sh → rewriter/wasm/build.sh
Executable file → Normal file
|
@ -8,7 +8,7 @@ which cargo wasm-bindgen wasm-opt &> /dev/null || {
|
|||
exit 1
|
||||
}
|
||||
|
||||
WBG="wasm-bindgen 0.2.95"
|
||||
WBG="wasm-bindgen 0.2.99"
|
||||
if ! [[ "$(wasm-bindgen -V)" =~ ^"$WBG" ]]; then
|
||||
echo "Incorrect wasm-bindgen-cli version: '$(wasm-bindgen -V)' != '$WBG'"
|
||||
exit 1
|
||||
|
@ -23,16 +23,14 @@ else
|
|||
fi
|
||||
|
||||
RUSTFLAGS='-C target-feature=+atomics,+bulk-memory,+simd128 -Zlocation-detail=none -Zfmt-debug=none' cargo build --lib --target wasm32-unknown-unknown -Z build-std=panic_abort,std -Z build-std-features=panic_immediate_abort --no-default-features --features "$FEATURES" --release
|
||||
wasm-bindgen --target web --out-dir out/ target/wasm32-unknown-unknown/release/rewriter.wasm
|
||||
wasm-bindgen --target web --out-dir out/ ../target/wasm32-unknown-unknown/release/wasm.wasm
|
||||
|
||||
sed -i 's/import.meta.url/""/g' out/rewriter.js
|
||||
sed -i 's/import.meta.url/""/g' out/wasm.js
|
||||
|
||||
cd ..
|
||||
|
||||
WASM=rewriter/out/rewriter_bg.wasm
|
||||
cd ../../
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
time wasm-opt $WASMOPTFLAGS --converge -tnh -O4 --vacuum --dce --enable-threads --enable-bulk-memory --enable-simd "$WASM" -o rewriter/out/optimized.wasm
|
||||
time wasm-opt $WASMOPTFLAGS --converge -tnh -O4 --vacuum --dce --enable-threads --enable-bulk-memory --enable-simd rewriter/wasm/out/wasm_bg.wasm -o rewriter/wasm/out/optimized.wasm
|
||||
|
||||
mkdir dist/ || true
|
||||
|
||||
|
@ -44,7 +42,7 @@ if ("document" in self && document?.currentScript) {
|
|||
}
|
||||
EOF
|
||||
echo -n "self.WASM = '"
|
||||
base64 -w0 < "rewriter/out/optimized.wasm"
|
||||
base64 -w0 < "rewriter/wasm/out/optimized.wasm"
|
||||
echo -n "';"
|
||||
|
||||
} > dist/scramjet.wasm.js
|
BIN
rewriter/wasm/out/optimized.wasm
Normal file
BIN
rewriter/wasm/out/optimized.wasm
Normal file
Binary file not shown.
|
@ -0,0 +1,7 @@
|
|||
|
||||
// slightly modified https://github.com/ungap/random-uuid/blob/main/index.js
|
||||
export function scramtag() {
|
||||
return (""+1e10).replace(/[018]/g,
|
||||
c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
|
||||
);
|
||||
}
|
48
rewriter/wasm/out/wasm.d.ts
vendored
Normal file
48
rewriter/wasm/out/wasm.d.ts
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export function rewrite_js(js: string, url: string, script_url: string, scramjet: object): RewriterOutput;
|
||||
export function rewrite_js_from_arraybuffer(js: Uint8Array, url: string, script_url: string, scramjet: object): RewriterOutput;
|
||||
|
||||
type RewriterOutput = { js: Uint8Array, errors: string[], duration: bigint };
|
||||
|
||||
|
||||
|
||||
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
||||
|
||||
export interface InitOutput {
|
||||
readonly rewrite_js: (a: number, b: number, c: number, d: number, e: number, f: number, g: any) => [number, number, number];
|
||||
readonly rewrite_js_from_arraybuffer: (a: number, b: number, c: number, d: number, e: number, f: number, g: any) => [number, number, number];
|
||||
readonly __wbindgen_exn_store: (a: number) => void;
|
||||
readonly __externref_table_alloc: () => number;
|
||||
readonly __wbindgen_export_2: WebAssembly.Table;
|
||||
readonly memory: WebAssembly.Memory;
|
||||
readonly __wbindgen_malloc: (a: number, b: number) => number;
|
||||
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
||||
readonly __wbindgen_free: (a: number, b: number, c: number) => void;
|
||||
readonly __externref_table_dealloc: (a: number) => void;
|
||||
readonly __wbindgen_thread_destroy: (a?: number, b?: number, c?: number) => void;
|
||||
readonly __wbindgen_start: (a: number) => void;
|
||||
}
|
||||
|
||||
export type SyncInitInput = BufferSource | WebAssembly.Module;
|
||||
/**
|
||||
* Instantiates the given `module`, which can either be bytes or
|
||||
* a precompiled `WebAssembly.Module`.
|
||||
*
|
||||
* @param {{ module: SyncInitInput, memory?: WebAssembly.Memory, thread_stack_size?: number }} module - Passing `SyncInitInput` directly is deprecated.
|
||||
* @param {WebAssembly.Memory} memory - Deprecated.
|
||||
*
|
||||
* @returns {InitOutput}
|
||||
*/
|
||||
export function initSync(module: { module: SyncInitInput, memory?: WebAssembly.Memory, thread_stack_size?: number } | SyncInitInput, memory?: WebAssembly.Memory): InitOutput;
|
||||
|
||||
/**
|
||||
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
|
||||
* for everything else, calls `WebAssembly.instantiate` directly.
|
||||
*
|
||||
* @param {{ module_or_path: InitInput | Promise<InitInput>, memory?: WebAssembly.Memory, thread_stack_size?: number }} module_or_path - Passing `InitInput` directly is deprecated.
|
||||
* @param {WebAssembly.Memory} memory - Deprecated.
|
||||
*
|
||||
* @returns {Promise<InitOutput>}
|
||||
*/
|
||||
export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput>, memory?: WebAssembly.Memory, thread_stack_size?: number } | InitInput | Promise<InitInput>, memory?: WebAssembly.Memory): Promise<InitOutput>;
|
390
rewriter/wasm/out/wasm.js
Normal file
390
rewriter/wasm/out/wasm.js
Normal file
|
@ -0,0 +1,390 @@
|
|||
import { scramtag } from './snippets/wasm-4b0f351a8e6eeb46/inline0.js';
|
||||
|
||||
let wasm;
|
||||
|
||||
function addToExternrefTable0(obj) {
|
||||
const idx = wasm.__externref_table_alloc();
|
||||
wasm.__wbindgen_export_2.set(idx, obj);
|
||||
return idx;
|
||||
}
|
||||
|
||||
function handleError(f, args) {
|
||||
try {
|
||||
return f.apply(this, args);
|
||||
} catch (e) {
|
||||
const idx = addToExternrefTable0(e);
|
||||
wasm.__wbindgen_exn_store(idx);
|
||||
}
|
||||
}
|
||||
|
||||
const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
|
||||
|
||||
if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); };
|
||||
|
||||
let cachedUint8ArrayMemory0 = null;
|
||||
|
||||
function getUint8ArrayMemory0() {
|
||||
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.buffer !== wasm.memory.buffer) {
|
||||
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachedUint8ArrayMemory0;
|
||||
}
|
||||
|
||||
function getStringFromWasm0(ptr, len) {
|
||||
ptr = ptr >>> 0;
|
||||
return cachedTextDecoder.decode(getUint8ArrayMemory0().slice(ptr, ptr + len));
|
||||
}
|
||||
|
||||
let WASM_VECTOR_LEN = 0;
|
||||
|
||||
const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } );
|
||||
|
||||
const encodeString = function (arg, view) {
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
view.set(buf);
|
||||
return {
|
||||
read: arg.length,
|
||||
written: buf.length
|
||||
};
|
||||
};
|
||||
|
||||
function passStringToWasm0(arg, malloc, realloc) {
|
||||
|
||||
if (realloc === undefined) {
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
const ptr = malloc(buf.length, 1) >>> 0;
|
||||
getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
|
||||
WASM_VECTOR_LEN = buf.length;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
let len = arg.length;
|
||||
let ptr = malloc(len, 1) >>> 0;
|
||||
|
||||
const mem = getUint8ArrayMemory0();
|
||||
|
||||
let offset = 0;
|
||||
|
||||
for (; offset < len; offset++) {
|
||||
const code = arg.charCodeAt(offset);
|
||||
if (code > 0x7F) break;
|
||||
mem[ptr + offset] = code;
|
||||
}
|
||||
|
||||
if (offset !== len) {
|
||||
if (offset !== 0) {
|
||||
arg = arg.slice(offset);
|
||||
}
|
||||
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
|
||||
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
|
||||
const ret = encodeString(arg, view);
|
||||
|
||||
offset += ret.written;
|
||||
ptr = realloc(ptr, len, offset, 1) >>> 0;
|
||||
}
|
||||
|
||||
WASM_VECTOR_LEN = offset;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
let cachedDataViewMemory0 = null;
|
||||
|
||||
function getDataViewMemory0() {
|
||||
if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer !== wasm.memory.buffer) {
|
||||
cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
|
||||
}
|
||||
return cachedDataViewMemory0;
|
||||
}
|
||||
|
||||
function isLikeNone(x) {
|
||||
return x === undefined || x === null;
|
||||
}
|
||||
|
||||
function getArrayU8FromWasm0(ptr, len) {
|
||||
ptr = ptr >>> 0;
|
||||
return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len);
|
||||
}
|
||||
|
||||
function takeFromExternrefTable0(idx) {
|
||||
const value = wasm.__wbindgen_export_2.get(idx);
|
||||
wasm.__externref_table_dealloc(idx);
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
* @param {string} js
|
||||
* @param {string} url
|
||||
* @param {string} script_url
|
||||
* @param {object} scramjet
|
||||
* @returns {RewriterOutput}
|
||||
*/
|
||||
export function rewrite_js(js, url, script_url, scramjet) {
|
||||
const ptr0 = passStringToWasm0(js, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ptr1 = passStringToWasm0(url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len1 = WASM_VECTOR_LEN;
|
||||
const ptr2 = passStringToWasm0(script_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len2 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.rewrite_js(ptr0, len0, ptr1, len1, ptr2, len2, scramjet);
|
||||
if (ret[2]) {
|
||||
throw takeFromExternrefTable0(ret[1]);
|
||||
}
|
||||
return takeFromExternrefTable0(ret[0]);
|
||||
}
|
||||
|
||||
function passArray8ToWasm0(arg, malloc) {
|
||||
const ptr = malloc(arg.length * 1, 1) >>> 0;
|
||||
getUint8ArrayMemory0().set(arg, ptr / 1);
|
||||
WASM_VECTOR_LEN = arg.length;
|
||||
return ptr;
|
||||
}
|
||||
/**
|
||||
* @param {Uint8Array} js
|
||||
* @param {string} url
|
||||
* @param {string} script_url
|
||||
* @param {object} scramjet
|
||||
* @returns {RewriterOutput}
|
||||
*/
|
||||
export function rewrite_js_from_arraybuffer(js, url, script_url, scramjet) {
|
||||
const ptr0 = passArray8ToWasm0(js, wasm.__wbindgen_malloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
const ptr1 = passStringToWasm0(url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len1 = WASM_VECTOR_LEN;
|
||||
const ptr2 = passStringToWasm0(script_url, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len2 = WASM_VECTOR_LEN;
|
||||
const ret = wasm.rewrite_js_from_arraybuffer(ptr0, len0, ptr1, len1, ptr2, len2, scramjet);
|
||||
if (ret[2]) {
|
||||
throw takeFromExternrefTable0(ret[1]);
|
||||
}
|
||||
return takeFromExternrefTable0(ret[0]);
|
||||
}
|
||||
|
||||
async function __wbg_load(module, imports) {
|
||||
if (typeof Response === 'function' && module instanceof Response) {
|
||||
if (typeof WebAssembly.instantiateStreaming === 'function') {
|
||||
try {
|
||||
return await WebAssembly.instantiateStreaming(module, imports);
|
||||
|
||||
} catch (e) {
|
||||
if (module.headers.get('Content-Type') != 'application/wasm') {
|
||||
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
|
||||
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const bytes = await module.arrayBuffer();
|
||||
return await WebAssembly.instantiate(bytes, imports);
|
||||
|
||||
} else {
|
||||
const instance = await WebAssembly.instantiate(module, imports);
|
||||
|
||||
if (instance instanceof WebAssembly.Instance) {
|
||||
return { instance, module };
|
||||
|
||||
} else {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function __wbg_get_imports() {
|
||||
const imports = {};
|
||||
imports.wbg = {};
|
||||
imports.wbg.__wbg_call_3b770f0d6eb4720e = function() { return handleError(function (arg0, arg1, arg2, arg3) {
|
||||
const ret = arg0.call(arg1, arg2, arg3);
|
||||
return ret;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_call_500db948e69c7330 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
const ret = arg0.call(arg1, arg2);
|
||||
return ret;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_call_b0d8e36992d9900d = function() { return handleError(function (arg0, arg1) {
|
||||
const ret = arg0.call(arg1);
|
||||
return ret;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_get_bbccf8970793c087 = function() { return handleError(function (arg0, arg1) {
|
||||
const ret = Reflect.get(arg0, arg1);
|
||||
return ret;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_new_17f755666e48d1d8 = function() { return handleError(function (arg0, arg1) {
|
||||
const ret = new URL(getStringFromWasm0(arg0, arg1));
|
||||
return ret;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_new_688846f374351c92 = function() {
|
||||
const ret = new Object();
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_newnoargs_fd9e4bf8be2bc16d = function(arg0, arg1) {
|
||||
const ret = new Function(getStringFromWasm0(arg0, arg1));
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_now_62a101fe35b60230 = function(arg0) {
|
||||
const ret = arg0.now();
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_scramtag_bd98edaa0eaec45e = function(arg0) {
|
||||
const ret = scramtag();
|
||||
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len1 = WASM_VECTOR_LEN;
|
||||
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
|
||||
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
|
||||
};
|
||||
imports.wbg.__wbg_set_4e647025551483bd = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
const ret = Reflect.set(arg0, arg1, arg2);
|
||||
return ret;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_static_accessor_GLOBAL_0be7472e492ad3e3 = function() {
|
||||
const ret = typeof global === 'undefined' ? null : global;
|
||||
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
||||
};
|
||||
imports.wbg.__wbg_static_accessor_GLOBAL_THIS_1a6eb482d12c9bfb = function() {
|
||||
const ret = typeof globalThis === 'undefined' ? null : globalThis;
|
||||
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
||||
};
|
||||
imports.wbg.__wbg_static_accessor_SELF_1dc398a895c82351 = function() {
|
||||
const ret = typeof self === 'undefined' ? null : self;
|
||||
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
||||
};
|
||||
imports.wbg.__wbg_static_accessor_WINDOW_ae1c80c7eea8d64a = function() {
|
||||
const ret = typeof window === 'undefined' ? null : window;
|
||||
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
||||
};
|
||||
imports.wbg.__wbg_toString_cbcf95f260c441ae = function(arg0) {
|
||||
const ret = arg0.toString();
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_array_new = function() {
|
||||
const ret = [];
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_array_push = function(arg0, arg1) {
|
||||
arg0.push(arg1);
|
||||
};
|
||||
imports.wbg.__wbindgen_boolean_get = function(arg0) {
|
||||
const v = arg0;
|
||||
const ret = typeof(v) === 'boolean' ? (v ? 1 : 0) : 2;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_error_new = function(arg0, arg1) {
|
||||
const ret = new Error(getStringFromWasm0(arg0, arg1));
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_init_externref_table = function() {
|
||||
const table = wasm.__wbindgen_export_2;
|
||||
const offset = table.grow(4);
|
||||
table.set(0, undefined);
|
||||
table.set(offset + 0, undefined);
|
||||
table.set(offset + 1, null);
|
||||
table.set(offset + 2, true);
|
||||
table.set(offset + 3, false);
|
||||
;
|
||||
};
|
||||
imports.wbg.__wbindgen_is_function = function(arg0) {
|
||||
const ret = typeof(arg0) === 'function';
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_is_undefined = function(arg0) {
|
||||
const ret = arg0 === undefined;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_number_new = function(arg0) {
|
||||
const ret = arg0;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_string_get = function(arg0, arg1) {
|
||||
const obj = arg1;
|
||||
const ret = typeof(obj) === 'string' ? obj : undefined;
|
||||
var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len1 = WASM_VECTOR_LEN;
|
||||
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
|
||||
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
|
||||
};
|
||||
imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
|
||||
const ret = getStringFromWasm0(arg0, arg1);
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_throw = function(arg0, arg1) {
|
||||
throw new Error(getStringFromWasm0(arg0, arg1));
|
||||
};
|
||||
imports.wbg.__wbindgen_uint8_array_new = function(arg0, arg1) {
|
||||
var v0 = getArrayU8FromWasm0(arg0, arg1).slice();
|
||||
wasm.__wbindgen_free(arg0, arg1 * 1, 1);
|
||||
const ret = v0;
|
||||
return ret;
|
||||
};
|
||||
|
||||
return imports;
|
||||
}
|
||||
|
||||
function __wbg_init_memory(imports, memory) {
|
||||
imports.wbg.memory = memory || new WebAssembly.Memory({initial:21,maximum:16384,shared:true});
|
||||
}
|
||||
|
||||
function __wbg_finalize_init(instance, module, thread_stack_size) {
|
||||
wasm = instance.exports;
|
||||
__wbg_init.__wbindgen_wasm_module = module;
|
||||
cachedDataViewMemory0 = null;
|
||||
cachedUint8ArrayMemory0 = null;
|
||||
|
||||
if (typeof thread_stack_size !== 'undefined' && (typeof thread_stack_size !== 'number' || thread_stack_size === 0 || thread_stack_size % 65536 !== 0)) { throw 'invalid stack size' }
|
||||
wasm.__wbindgen_start(thread_stack_size);
|
||||
return wasm;
|
||||
}
|
||||
|
||||
function initSync(module, memory) {
|
||||
if (wasm !== undefined) return wasm;
|
||||
|
||||
let thread_stack_size
|
||||
if (typeof module !== 'undefined') {
|
||||
if (Object.getPrototypeOf(module) === Object.prototype) {
|
||||
({module, memory, thread_stack_size} = module)
|
||||
} else {
|
||||
console.warn('using deprecated parameters for `initSync()`; pass a single object instead')
|
||||
}
|
||||
}
|
||||
|
||||
const imports = __wbg_get_imports();
|
||||
|
||||
__wbg_init_memory(imports, memory);
|
||||
|
||||
if (!(module instanceof WebAssembly.Module)) {
|
||||
module = new WebAssembly.Module(module);
|
||||
}
|
||||
|
||||
const instance = new WebAssembly.Instance(module, imports);
|
||||
|
||||
return __wbg_finalize_init(instance, module, thread_stack_size);
|
||||
}
|
||||
|
||||
async function __wbg_init(module_or_path, memory) {
|
||||
if (wasm !== undefined) return wasm;
|
||||
|
||||
let thread_stack_size
|
||||
if (typeof module_or_path !== 'undefined') {
|
||||
if (Object.getPrototypeOf(module_or_path) === Object.prototype) {
|
||||
({module_or_path, memory, thread_stack_size} = module_or_path)
|
||||
} else {
|
||||
console.warn('using deprecated parameters for the initialization function; pass a single object instead')
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof module_or_path === 'undefined') {
|
||||
module_or_path = new URL('wasm_bg.wasm', "");
|
||||
}
|
||||
const imports = __wbg_get_imports();
|
||||
|
||||
if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) {
|
||||
module_or_path = fetch(module_or_path);
|
||||
}
|
||||
|
||||
__wbg_init_memory(imports, memory);
|
||||
|
||||
const { instance, module } = await __wbg_load(await module_or_path, imports);
|
||||
|
||||
return __wbg_finalize_init(instance, module, thread_stack_size);
|
||||
}
|
||||
|
||||
export { initSync };
|
||||
export default __wbg_init;
|
BIN
rewriter/wasm/out/wasm_bg.wasm
Normal file
BIN
rewriter/wasm/out/wasm_bg.wasm
Normal file
Binary file not shown.
14
rewriter/wasm/out/wasm_bg.wasm.d.ts
vendored
Normal file
14
rewriter/wasm/out/wasm_bg.wasm.d.ts
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const rewrite_js: (a: number, b: number, c: number, d: number, e: number, f: number, g: any) => [number, number, number];
|
||||
export const rewrite_js_from_arraybuffer: (a: number, b: number, c: number, d: number, e: number, f: number, g: any) => [number, number, number];
|
||||
export const __wbindgen_exn_store: (a: number) => void;
|
||||
export const __externref_table_alloc: () => number;
|
||||
export const __wbindgen_export_2: WebAssembly.Table;
|
||||
export const memory: WebAssembly.Memory;
|
||||
export const __wbindgen_malloc: (a: number, b: number) => number;
|
||||
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
||||
export const __wbindgen_free: (a: number, b: number, c: number) => void;
|
||||
export const __externref_table_dealloc: (a: number) => void;
|
||||
export const __wbindgen_thread_destroy: (a?: number, b?: number, c?: number) => void;
|
||||
export const __wbindgen_start: (a: number) => void;
|
49
rewriter/wasm/src/error.rs
Normal file
49
rewriter/wasm/src/error.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
use js_sys::Error;
|
||||
use rewriter::RewriterError as InnerRewriterError;
|
||||
use thiserror::Error;
|
||||
use wasm_bindgen::{JsError, JsValue};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum RewriterError {
|
||||
#[error("JS: {0}")]
|
||||
Js(String),
|
||||
#[error("URL parse error: {0}")]
|
||||
Url(#[from] url::ParseError),
|
||||
#[error("str fromutf8 error: {0}")]
|
||||
Str(#[from] std::str::Utf8Error),
|
||||
#[error("Rewriter: {0}")]
|
||||
Rewriter(#[from] InnerRewriterError),
|
||||
#[error("reflect set failed: {0}")]
|
||||
ReflectSetFail(String),
|
||||
|
||||
#[error("{0} was not {1}")]
|
||||
Not(String, &'static str),
|
||||
}
|
||||
|
||||
impl From<JsValue> for RewriterError {
|
||||
fn from(value: JsValue) -> Self {
|
||||
Self::Js(Error::from(value).to_string().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RewriterError> for JsValue {
|
||||
fn from(value: RewriterError) -> Self {
|
||||
JsError::from(value).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl RewriterError {
|
||||
pub fn not_str(x: &str) -> Self {
|
||||
Self::Not(x.to_string(), "string")
|
||||
}
|
||||
|
||||
pub fn not_fn(x: &str) -> Self {
|
||||
Self::Not(x.to_string(), "function")
|
||||
}
|
||||
|
||||
pub fn not_bool(x: &str) -> Self {
|
||||
Self::Not(x.to_string(), "bool")
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, RewriterError>;
|
174
rewriter/wasm/src/lib.rs
Normal file
174
rewriter/wasm/src/lib.rs
Normal file
|
@ -0,0 +1,174 @@
|
|||
pub mod error;
|
||||
|
||||
use std::{str::FromStr, sync::Arc, time::Duration};
|
||||
|
||||
use error::{Result, RewriterError};
|
||||
use instant::Instant;
|
||||
use js_sys::{Function, Object, Reflect};
|
||||
use oxc::diagnostics::NamedSource;
|
||||
use rewriter::{
|
||||
cfg::{Config, EncodeFn},
|
||||
rewrite, RewriteResult,
|
||||
};
|
||||
use url::Url;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen(typescript_custom_section)]
|
||||
const REWRITER_OUTPUT: &'static str = r#"
|
||||
type RewriterOutput = { js: Uint8Array, errors: string[], duration: bigint };
|
||||
"#;
|
||||
|
||||
#[wasm_bindgen(inline_js = r#"
|
||||
// slightly modified https://github.com/ungap/random-uuid/blob/main/index.js
|
||||
export function scramtag() {
|
||||
return (""+1e10).replace(/[018]/g,
|
||||
c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
|
||||
);
|
||||
}
|
||||
"#)]
|
||||
extern "C" {
|
||||
pub fn scramtag() -> String;
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(typescript_type = "RewriterOutput")]
|
||||
pub type JsRewriterOutput;
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
fn error(s: &str);
|
||||
}
|
||||
|
||||
fn create_encode_function(encode: JsValue) -> Result<EncodeFn> {
|
||||
let encode = encode.dyn_into::<Function>()?;
|
||||
|
||||
Ok(Box::new(move |str| {
|
||||
encode
|
||||
.call1(&JsValue::NULL, &str.into())
|
||||
.unwrap()
|
||||
.as_string()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
}))
|
||||
}
|
||||
|
||||
fn get_obj(obj: &JsValue, k: &str) -> Result<JsValue> {
|
||||
Ok(Reflect::get(obj, &k.into())?)
|
||||
}
|
||||
|
||||
fn get_str(obj: &JsValue, k: &str) -> Result<String> {
|
||||
Reflect::get(obj, &k.into())?
|
||||
.as_string()
|
||||
.ok_or_else(|| RewriterError::not_str(k))
|
||||
}
|
||||
|
||||
fn set_obj(obj: &Object, k: &str, v: &JsValue) -> Result<()> {
|
||||
if !Reflect::set(&obj.into(), &k.into(), v)? {
|
||||
Err(RewriterError::ReflectSetFail(k.to_string()))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn get_flag(scramjet: &Object, url: &str, flag: &str) -> Result<bool> {
|
||||
let fenabled = get_obj(scramjet, "flagEnabled")?
|
||||
.dyn_into::<Function>()
|
||||
.map_err(|_| RewriterError::not_fn("scramjet.flagEnabled"))?;
|
||||
let ret = fenabled.call2(
|
||||
&JsValue::NULL,
|
||||
&flag.into(),
|
||||
&web_sys::Url::new(url)?.into(),
|
||||
)?;
|
||||
|
||||
ret.as_bool()
|
||||
.ok_or_else(|| RewriterError::not_bool("scramjet.flagEnabled return value"))
|
||||
}
|
||||
|
||||
fn get_config(scramjet: &Object, url: &str) -> Result<Config> {
|
||||
let codec = &get_obj(scramjet, "codec")?;
|
||||
let config = &get_obj(scramjet, "config")?;
|
||||
let globals = &get_obj(config, "globals")?;
|
||||
|
||||
Ok(Config {
|
||||
prefix: get_str(config, "prefix")?,
|
||||
encoder: create_encode_function(get_obj(codec, "encode")?)?,
|
||||
base: Url::from_str(url)?,
|
||||
sourcetag: scramtag(),
|
||||
|
||||
wrapfn: get_str(globals, "wrapfn")?,
|
||||
wrapthisfn: get_str(globals, "wrapthisfn")?,
|
||||
importfn: get_str(globals, "importfn")?,
|
||||
rewritefn: get_str(globals, "rewritefn")?,
|
||||
metafn: get_str(globals, "metafn")?,
|
||||
setrealmfn: get_str(globals, "setrealmfn")?,
|
||||
pushsourcemapfn: get_str(globals, "pushsourcemapfn")?,
|
||||
|
||||
do_sourcemaps: get_flag(scramjet, url, "sourcemaps")?,
|
||||
capture_errors: get_flag(scramjet, url, "captureErrors")?,
|
||||
scramitize: get_flag(scramjet, url, "scramitize")?,
|
||||
strict_rewrites: get_flag(scramjet, url, "strictRewrites")?,
|
||||
})
|
||||
}
|
||||
|
||||
fn duration_to_millis_f64(duration: Duration) -> f64 {
|
||||
(duration.as_secs() as f64) * 1_000f64 + (duration.subsec_nanos() as f64) / 1_000_000f64
|
||||
}
|
||||
|
||||
fn create_rewriter_output(
|
||||
out: RewriteResult,
|
||||
url: String,
|
||||
src: String,
|
||||
duration: Duration,
|
||||
) -> Result<JsRewriterOutput> {
|
||||
let src = Arc::new(NamedSource::new(url, src).with_language("javascript"));
|
||||
#[cfg(feature = "debug")]
|
||||
let errs: Vec<_> = out
|
||||
.errors
|
||||
.into_iter()
|
||||
.map(|x| format!("{}", x.with_source_code(src.clone())))
|
||||
.collect();
|
||||
|
||||
let obj = Object::new();
|
||||
set_obj(&obj, "js", &out.js.into())?;
|
||||
#[cfg(feature = "debug")]
|
||||
set_obj(&obj, "errors", &errs.into())?;
|
||||
#[cfg(not(feature = "debug"))]
|
||||
set_obj(&obj, "errors", &js_sys::Array::new())?;
|
||||
set_obj(&obj, "duration", &duration_to_millis_f64(duration).into())?;
|
||||
|
||||
Ok(JsRewriterOutput::from(JsValue::from(obj)))
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn rewrite_js(
|
||||
js: String,
|
||||
url: &str,
|
||||
script_url: String,
|
||||
scramjet: &Object,
|
||||
) -> Result<JsRewriterOutput> {
|
||||
let before = Instant::now();
|
||||
let out = rewrite(&js, get_config(scramjet, url)?)?;
|
||||
let after = Instant::now();
|
||||
|
||||
create_rewriter_output(out, script_url, js, after - before)
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn rewrite_js_from_arraybuffer(
|
||||
js: Vec<u8>,
|
||||
url: &str,
|
||||
script_url: String,
|
||||
scramjet: &Object,
|
||||
) -> Result<JsRewriterOutput> {
|
||||
// we know that this is a valid utf-8 string
|
||||
let js = unsafe { String::from_utf8_unchecked(js) };
|
||||
|
||||
let before = Instant::now();
|
||||
let out = rewrite(&js, get_config(scramjet, url)?)?;
|
||||
let after = Instant::now();
|
||||
|
||||
create_rewriter_output(out, script_url, js, after - before)
|
||||
}
|
|
@ -6,7 +6,7 @@ import {
|
|||
rewrite_js,
|
||||
rewrite_js_from_arraybuffer,
|
||||
RewriterOutput,
|
||||
} from "../../../rewriter/out/rewriter.js";
|
||||
} from "../../../rewriter/wasm/out/wasm.js";
|
||||
import { $scramjet, flagEnabled } from "../../scramjet";
|
||||
|
||||
initSync({
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue