Skip to content

Commit

Permalink
fix(napi): do not support raw transfer on WASM32 (#9566)
Browse files Browse the repository at this point in the history
Prevent compilation errors on 32-bit platforms by avoiding calculating `usize: 1 << 32` unless on a 64-bit platform.

`parseSync` will throw an error if called with `experimentalRawTransfer` on an unsupported system.

Add a function `rawTransferSupported` to check if raw transfer is supported on current system or not, so user can avoid these errors.
  • Loading branch information
overlookmotel committed Mar 6, 2025
1 parent 4ca62ab commit 91c9932
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 4 deletions.
1 change: 1 addition & 0 deletions napi/parser/bindings.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 3 additions & 0 deletions napi/parser/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
15 changes: 15 additions & 0 deletions napi/parser/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
2 changes: 1 addition & 1 deletion napi/parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ mod convert;
mod raw_transfer;
mod raw_transfer_types;
mod types;
pub use raw_transfer::{get_buffer_offset, parse_sync_raw};
pub use raw_transfer::{get_buffer_offset, parse_sync_raw, raw_transfer_supported};
pub use types::{Comment, EcmaScriptModule, ParseResult, ParserOptions};

mod generated {
Expand Down
20 changes: 17 additions & 3 deletions napi/parser/src/raw_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ use crate::{
raw_transfer_types::{EcmaScriptModule, Error, RawTransferData},
};

// Only 64-bit little-endian platforms are supported at present.
const IS_SUPPORTED_PLATFORM: bool =
cfg!(all(target_pointer_width = "64", target_endian = "little"));

// 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.
Expand All @@ -26,7 +30,9 @@ use crate::{
// so this enables using `>>` bitshift operator in JS, rather than the more expensive `>>>`,
// without offsets being interpreted as negative.
const TWO_GIB: usize = 1 << 31;
const FOUR_GIB: usize = 1 << 32;
// `1 << 32`.
// We use `IS_SUPPORTED_PLATFORM as usize * 32` to avoid compilation failure on 32-bit platforms.
const FOUR_GIB: usize = 1 << (IS_SUPPORTED_PLATFORM as usize * 32);

const BUFFER_SIZE: usize = TWO_GIB;
const BUFFER_ALIGN: usize = FOUR_GIB;
Expand Down Expand Up @@ -75,8 +81,10 @@ pub unsafe fn parse_sync_raw(
source_len: u32,
options: Option<ParserOptions>,
) {
// 32-bit systems are not supported
const { assert!(std::mem::size_of::<usize>() >= 8) };
assert!(
IS_SUPPORTED_PLATFORM,
"Raw transfer is only supported on 64-bit little-endian platforms"
);

// Check buffer has expected size and alignment
let buffer = &mut *buffer;
Expand Down Expand Up @@ -176,6 +184,12 @@ pub unsafe fn parse_sync_raw(
}
}

/// Returns `true` if raw transfer is supported on this platform.
#[napi]
pub fn raw_transfer_supported() -> bool {
IS_SUPPORTED_PLATFORM
}

/// Returns `true` if `n` is a multiple of `divisor`.
const fn is_multiple_of(n: usize, divisor: usize) -> bool {
n % divisor == 0
Expand Down

0 comments on commit 91c9932

Please sign in to comment.