Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions crates/perry-runtime/src/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub const BIGINT_LIMBS: usize = 16;
const BIGINT_BITS: usize = BIGINT_LIMBS * 64;

const ZERO_LIMBS: [u64; BIGINT_LIMBS] = [0; BIGINT_LIMBS];
const DIVISION_BY_ZERO_MESSAGE: &[u8] = b"Division by zero";

/// Decode a 1024-bit two's-complement value into a host i64 if it fits.
/// Layout: positive small → all upper limbs zero AND limb[0] high bit clear;
Expand Down Expand Up @@ -124,6 +125,16 @@ pub fn clean_bigint_ptr_mut(p: *mut BigIntHeader) -> *mut BigIntHeader {
clean_bigint_ptr(p as *const BigIntHeader) as *mut BigIntHeader
}

#[cold]
fn throw_bigint_division_by_zero() -> ! {
let msg = crate::string::js_string_from_bytes(
DIVISION_BY_ZERO_MESSAGE.as_ptr(),
DIVISION_BY_ZERO_MESSAGE.len() as u32,
);
let err = crate::error::js_rangeerror_new(msg);
crate::exception::js_throw(crate::value::js_nanbox_pointer(err as i64))
}

/// Create a BigInt from a u64 value
#[no_mangle]
pub extern "C" fn js_bigint_from_u64(value: u64) -> *mut BigIntHeader {
Expand Down Expand Up @@ -623,9 +634,8 @@ pub extern "C" fn js_bigint_div(
let a_limbs = bigint_limbs_or_zero(a);
let b_limbs = bigint_limbs_or_zero(b);

// Division by zero: return 0 instead of panicking (panic can't unwind in extern "C")
if b_limbs == ZERO_LIMBS {
return bigint_alloc_with_limbs(ZERO_LIMBS);
throw_bigint_division_by_zero();
}

// Fast path: both fit in i64. Rust's `/` on i64 truncates toward
Expand Down Expand Up @@ -674,9 +684,8 @@ pub extern "C" fn js_bigint_mod(
let a_limbs = bigint_limbs_or_zero(a);
let b_limbs = bigint_limbs_or_zero(b);

// Division by zero: return 0 instead of panicking (panic can't unwind in extern "C")
if b_limbs == ZERO_LIMBS {
return bigint_alloc_with_limbs(ZERO_LIMBS);
throw_bigint_division_by_zero();
}

// Fast path: both fit in i64. JavaScript's `%` returns the sign of
Expand Down
4 changes: 2 additions & 2 deletions crates/perry-runtime/src/node_stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ extern "C" fn ns_writable_final_callback_done(closure: *const ClosureHeader, err
}
return f64::from_bits(TAG_UNDEFINED);
}
schedule_writable_finish(
schedule_writable_finish_then_transform_end(
stream,
if is_callable_value(callback) {
Some(callback)
Expand Down Expand Up @@ -1192,7 +1192,7 @@ fn finish_stream(stream: f64, callback: Option<f64>) {
set_pending_writable_finish_callback(stream, callback);
return;
}
schedule_writable_finish(stream, callback);
schedule_writable_finish_then_transform_end(stream, callback);
}

fn finish_stream_with_args(stream: f64, chunk: f64, encoding: f64, cb: f64) {
Expand Down
13 changes: 12 additions & 1 deletion crates/perry-runtime/src/node_stream_readwrite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,17 @@ pub(super) fn schedule_writable_finish(stream: f64, callback: Option<f64>) {
crate::builtins::js_queue_microtask(closure as i64);
}

pub(super) fn schedule_writable_finish_then_transform_end(stream: f64, callback: Option<f64>) {
schedule_writable_finish(stream, callback);
if is_transform_stream(stream)
&& !has_truthy_hidden(stream, hidden_writable_final_pending_key())
&& (has_truthy_hidden(stream, hidden_finish_scheduled_key())
|| has_truthy_hidden(stream, hidden_finish_emitted_key()))
{
schedule_readable_end(stream);
}
}

pub(super) fn set_pending_writable_finish_callback(stream: f64, callback: Option<f64>) {
let value = callback.unwrap_or_else(|| f64::from_bits(TAG_UNDEFINED));
set_hidden_value(stream, hidden_writable_pending_finish_callback_key(), value);
Expand All @@ -743,7 +754,7 @@ pub(super) fn schedule_pending_writable_finish_if_ready(stream: f64) {
return;
}
let callback = take_pending_writable_finish_callback(stream);
schedule_writable_finish(stream, callback);
schedule_writable_finish_then_transform_end(stream, callback);
}

pub(super) fn emit_readable_end_once(stream: f64) {
Expand Down
17 changes: 17 additions & 0 deletions test-parity/node-suite/bigint/arithmetic/division-by-zero.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
function probe(label: string, fn: () => bigint): void {
try {
console.log(label, "ok", String(fn()));
} catch (e) {
const err = e as Error;
console.log(label, "throw", err.name, err.message, e instanceof RangeError);
}
}

const zero = BigInt(0);

probe("div literal zero", () => 1n / 0n);
probe("mod literal zero", () => 1n % 0n);
probe("div variable zero", () => 123n / zero);
probe("mod variable zero", () => 123n % zero);
probe("div nonzero", () => 7n / 2n);
probe("mod nonzero", () => -7n % 2n);