diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9c1ae06e9b000..1eddb56680f42 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,7 +92,7 @@ jobs: test-wasm32-wasip1-threads: name: Test wasm32-wasip1-threads - if: ${{ github.ref_name == 'main' }} + # if: ${{ github.ref_name == 'main' }} runs-on: ubuntu-latest env: RUSTFLAGS: "--cfg tokio_unstable -C target-feature=+atomics,+bulk-memory,+mutable-globals,+simd128 -C link-args=--max-memory=67108864" @@ -105,8 +105,12 @@ jobs: with: cache-key: wasi save-cache: ${{ github.ref_name == 'main' }} + - uses: ./.github/actions/pnpm - run: rustup target add wasm32-wasip1-threads - uses: bytecodealliance/actions/wasmtime/setup@3b93676295fd6f7eaa7af2c2785539e052fa8349 # v1.1.1 + - run: pnpm napi build --target wasm32-wasip1-threads --manifest-path ./napi/parser/Cargo.toml + - run: pnpm napi build --target wasm32-wasip1-threads --manifest-path ./napi/transform/Cargo.toml + - run: pnpm napi build --target wasm32-wasip1-threads --manifest-path ./napi/minify/Cargo.toml - run: cargo test --target wasm32-wasip1-threads ${TEST_FLAGS} - run: git diff --exit-code # Must commit everything diff --git a/napi/parser/bindings.js b/napi/parser/bindings.js index fd3b6a8d4759b..12571e097e64a 100644 --- a/napi/parser/bindings.js +++ b/napi/parser/bindings.js @@ -379,4 +379,5 @@ module.exports.ImportNameKind = nativeBinding.ImportNameKind module.exports.parseAsync = nativeBinding.parseAsync module.exports.parseSync = nativeBinding.parseSync module.exports.parseSyncRaw = nativeBinding.parseSyncRaw +module.exports.rawTransferSupported = nativeBinding.rawTransferSupported module.exports.Severity = nativeBinding.Severity diff --git a/napi/parser/index.d.ts b/napi/parser/index.d.ts index a20f3510e8df9..6616982e57825 100644 --- a/napi/parser/index.d.ts +++ b/napi/parser/index.d.ts @@ -201,6 +201,9 @@ export declare function parseSync(filename: string, sourceText: string, options? */ export declare function parseSyncRaw(filename: string, buffer: Uint8Array, sourceLen: number, options?: ParserOptions | undefined | null): void +/** Returns `true` if raw transfer is supported on this platform. */ +export declare function rawTransferSupported(): boolean + export declare const enum Severity { Error = 'Error', Warning = 'Warning', diff --git a/napi/parser/index.js b/napi/parser/index.js index 6863a1d65abcc..3935a5a1c80d8 100644 --- a/napi/parser/index.js +++ b/napi/parser/index.js @@ -68,6 +68,10 @@ module.exports.parseSync = function parseSync(filename, sourceText, options) { let buffer, encoder; function parseSyncRaw(filename, sourceText, options) { + if (!rawTransferSupported()) { + throw new Error('`experimentalRawTransfer` option is not supported on 32-bit or big-endian systems'); + } + // Delete `experimentalRawTransfer` option let experimentalRawTransfer; ({ experimentalRawTransfer, ...options } = options); @@ -138,3 +142,14 @@ function createBuffer() { const offset = bindings.getBufferOffset(new Uint8Array(arrayBuffer)); return new Uint8Array(arrayBuffer, offset, TWO_GIB); } + +let rawTransferIsSupported = null; + +// Returns `true` if `experimentalRawTransfer` is option is supported. +// Raw transfer is only available on 64-bit little-endian systems. +function rawTransferSupported() { + if (rawTransferIsSupported === null) rawTransferIsSupported = bindings.rawTransferSupported(); + return rawTransferIsSupported; +} + +module.exports.rawTransferSupported = rawTransferSupported; diff --git a/napi/parser/src/lib.rs b/napi/parser/src/lib.rs index c60176710543e..5d7dc5275d945 100644 --- a/napi/parser/src/lib.rs +++ b/napi/parser/src/lib.rs @@ -17,18 +17,30 @@ use oxc::{ use oxc_napi::OxcError; mod convert; -mod raw_transfer; -mod raw_transfer_types; mod types; -pub use raw_transfer::{get_buffer_offset, parse_sync_raw}; pub use types::{Comment, EcmaScriptModule, ParseResult, ParserOptions}; +// Only compile raw transfer APIs on 64-bit little-endian systems +#[cfg(all(target_pointer_width = "64", target_endian = "little"))] +mod raw_transfer; +#[cfg(all(target_pointer_width = "64", target_endian = "little"))] +mod raw_transfer_types; +#[cfg(all(target_pointer_width = "64", target_endian = "little"))] +pub use raw_transfer::*; + +#[cfg(all(target_pointer_width = "64", target_endian = "little"))] mod generated { // Note: We intentionally don't import `generated/derive_estree.rs`. It's not needed. #[cfg(debug_assertions)] pub mod assert_layouts; } +// Expose stubs on unsupported platforms +#[cfg(not(all(target_pointer_width = "64", target_endian = "little")))] +mod raw_transfer_stubs; +#[cfg(not(all(target_pointer_width = "64", target_endian = "little")))] +pub use raw_transfer_stubs::*; + #[derive(Clone, Copy, PartialEq, Eq)] enum AstType { JavaScript, diff --git a/napi/parser/src/raw_transfer.rs b/napi/parser/src/raw_transfer.rs index 729f6519c668b..94148060b9cf4 100644 --- a/napi/parser/src/raw_transfer.rs +++ b/napi/parser/src/raw_transfer.rs @@ -18,6 +18,13 @@ use crate::{ raw_transfer_types::{EcmaScriptModule, Error, RawTransferData}, }; +const _: () = { + #[cfg(not(target_pointer_width = "64"))] + panic!("Only 64-bit systems are supported"); + #[cfg(target_endian = "big")] + panic!("Little-endian systems are not supported"); +}; + // For raw transfer, use a buffer 4 GiB in size, with 4 GiB alignment. // This ensures that all 64-bit pointers have the same value in upper 32 bits, // so JS only needs to read the lower 32 bits to get an offset into the buffer. @@ -69,15 +76,12 @@ pub fn get_buffer_offset(buffer: Uint8Array) -> u32 { /// Panics if source text is too long, or AST takes more memory than is available in the buffer. #[allow(clippy::items_after_statements, clippy::allow_attributes)] #[napi] -pub unsafe fn parse_sync_raw( +pub fn parse_sync_raw( filename: String, mut buffer: Uint8Array, source_len: u32, options: Option, ) { - // 32-bit systems are not supported - const { assert!(std::mem::size_of::() >= 8) }; - // Check buffer has expected size and alignment let buffer = &mut *buffer; assert_eq!(buffer.len(), BUFFER_SIZE); @@ -176,6 +180,14 @@ pub unsafe fn parse_sync_raw( } } +/// Returns `true` if raw transfer is supported on this platform. +#[napi] +pub fn raw_transfer_supported() -> bool { + // On unsupported platforms, the stub function in `raw_transfer_stubs.rs` will be compiled instead. + // It returns `false`. + true +} + /// Returns `true` if `n` is a multiple of `divisor`. const fn is_multiple_of(n: usize, divisor: usize) -> bool { n % divisor == 0 diff --git a/napi/parser/src/raw_transfer_stubs.rs b/napi/parser/src/raw_transfer_stubs.rs new file mode 100644 index 0000000000000..dfcca3adf379d --- /dev/null +++ b/napi/parser/src/raw_transfer_stubs.rs @@ -0,0 +1,29 @@ +//! Stubs for raw transfer functions on unsupported platforms. +//! +//! These exports are required to avoid type-checking errors. + +#![expect(unused_variables, unused_mut)] + +use napi::bindgen_prelude::Uint8Array; +use napi_derive::napi; + +use crate::ParserOptions; + +#[napi] +pub fn get_buffer_offset(buffer: Uint8Array) -> u32 { + 0 +} + +#[napi] +pub fn parse_sync_raw( + filename: String, + mut buffer: Uint8Array, + source_len: u32, + options: Option, +) { +} + +#[napi] +pub fn raw_transfer_supported() -> bool { + false +} diff --git a/package.json b/package.json index 96f593339c149..e2f8be30339c5 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ }, "devDependencies": { "@napi-rs/cli": "catalog:", + "emnapi": "1.3.1", "typescript": "catalog:", "vitest": "catalog:" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 64660b5620337..98fa847cdb172 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,7 +22,10 @@ importers: devDependencies: '@napi-rs/cli': specifier: 'catalog:' - version: 3.0.0-alpha.72(@emnapi/runtime@1.3.1)(@types/node@22.13.8) + version: 3.0.0-alpha.72(@emnapi/runtime@1.3.1)(@types/node@22.13.8)(emnapi@1.3.1) + emnapi: + specifier: 1.3.1 + version: 1.3.1 typescript: specifier: 'catalog:' version: 5.8.2 @@ -1906,6 +1909,14 @@ packages: electron-to-chromium@1.5.109: resolution: {integrity: sha512-AidaH9JETVRr9DIPGfp1kAarm/W6hRJTPuCnkF+2MqhF4KaAgRIcBc8nvjk+YMXZhwfISof/7WG29eS4iGxQLQ==} + emnapi@1.3.1: + resolution: {integrity: sha512-8rnw2VLJmHAXBSyhtrL9O5aW1VdbXA1ovRslp0IyTwnM62Fz83jQIo+VaIObgzdo6r1A98J9AHEq4KTqIR67Aw==} + peerDependencies: + node-addon-api: '>= 6.1.0' + peerDependenciesMeta: + node-addon-api: + optional: true + emoji-regex@10.4.0: resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} @@ -4054,7 +4065,7 @@ snapshots: dependencies: tslib: 2.8.1 - '@napi-rs/cli@3.0.0-alpha.72(@emnapi/runtime@1.3.1)(@types/node@22.13.8)': + '@napi-rs/cli@3.0.0-alpha.72(@emnapi/runtime@1.3.1)(@types/node@22.13.8)(emnapi@1.3.1)': dependencies: '@inquirer/prompts': 7.3.2(@types/node@22.13.8) '@napi-rs/cross-toolchain': 0.0.19 @@ -4071,6 +4082,7 @@ snapshots: wasm-sjlj: 1.0.6 optionalDependencies: '@emnapi/runtime': 1.3.1 + emnapi: 1.3.1 transitivePeerDependencies: - '@napi-rs/cross-toolchain-arm64-target-aarch64' - '@napi-rs/cross-toolchain-arm64-target-armv7' @@ -5136,6 +5148,8 @@ snapshots: electron-to-chromium@1.5.109: {} + emnapi@1.3.1: {} + emoji-regex@10.4.0: {} emoji-regex@8.0.0: {}