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
1 change: 0 additions & 1 deletion mordant-baseline.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@

[bun_runtime]
"bare_bool_args:src/runtime/api.rs" = 2
"bare_bool_args:src/runtime/node/types.rs" = 4
"defaulted_failure:src/runtime/ffi/ffi_body.rs" = 1
"defaulted_failure:src/runtime/server/RequestContext.rs" = 1
"defaulted_failure:src/runtime/test_runner/pretty_format.rs" = 4
Expand Down
2 changes: 1 addition & 1 deletion src/jsc/node_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub struct ThreadSafe<T: Unprotect>(T);

impl<T: Unprotect> ThreadSafe<T> {
/// Wrap an **already-protected** `T`. Use when the protect was taken
/// elsewhere (e.g. inside `from_js_maybe_async(.., is_async=true)`).
/// elsewhere (e.g. inside `from_js_maybe_async(.., Flavor::Async, ..)`).
#[inline]
pub fn adopt(value: T) -> Self {
Self(value)
Expand Down
7 changes: 3 additions & 4 deletions src/runtime/api/BunObject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2726,12 +2726,11 @@ pub mod JSZstd {

let level = get_level(global_this, options_val)?;

let allow_string_object = true;
if let Some(buffer) = node::StringOrBuffer::from_js_maybe_async(
global_this,
buffer_value,
true,
allow_string_object,
node::Flavor::Async,
node::StringObjects::Allow,
)? {
return Ok((buffer, options_val, level));
}
Expand Down Expand Up @@ -2807,7 +2806,7 @@ pub mod JSZstd {

/// `Bun.zstdCompress` / `Bun.zstdDecompress` off the JS thread.
pub(crate) struct ZstdJob {
/// Created with `is_async=true` (JS-backed buffer protected); the
/// Created with `Flavor::Async` (JS-backed buffer protected); the
/// [`bun_jsc::ThreadSafe`] releases that with the job.
pub buffer: bun_jsc::ThreadSafe<node::StringOrBuffer>,
pub is_compress: bool,
Expand Down
9 changes: 4 additions & 5 deletions src/runtime/api/JSTranspiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use bun_options_types::TargetExt as _;
use std::io::Write as _;

use crate::Error;
use crate::node::{Encoding, StringOrBuffer};
use crate::node::{Encoding, Flavor, StringObjects, StringOrBuffer};
use bun_alloc::{Arena, ArenaVec}; // bumpalo::Bump / bumpalo::collections::Vec re-exports
use bun_ast::Expr;
use bun_ast::Loader;
Expand Down Expand Up @@ -1392,13 +1392,12 @@ impl JSTranspiler {
));
};

let allow_string_object = true;
let Some(code) = StringOrBuffer::from_js_with_encoding_maybe_async(
global,
code_arg,
Encoding::Utf8,
true,
allow_string_object,
Flavor::Async,
StringObjects::Allow,
)?
else {
return Err(global.throw_invalid_argument_type(
Expand All @@ -1415,7 +1414,7 @@ impl JSTranspiler {
code = StringOrBuffer::EncodedSlice(bun_core::ZigStringSlice::init_owned(bytes));
}
// `errdefer code.deinitAndUnprotect()` — `from_js_with_encoding_maybe_async`
// (is_async=true) already protected; adopt into a `ThreadSafe` so any
// (`Flavor::Async`) already protected; adopt into a `ThreadSafe` so any
// early-return drop unprotects. `TransformTask::create` takes the guard.
let code = bun_jsc::ThreadSafe::adopt(code);

Expand Down
23 changes: 11 additions & 12 deletions src/runtime/crypto/PBKDF2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use bun_jsc::{
JsThread,
};

use crate::node::StringOrBuffer;
use crate::node::{Flavor, StringObjects, StringOrBuffer};

use crate::crypto::evp::{self, Algorithm};

Expand Down Expand Up @@ -68,7 +68,7 @@ impl PBKDF2 {
pub(crate) fn from_js(
global_this: &JSGlobalObject,
call_frame: &CallFrame,
is_async: bool,
flavor: Flavor,
) -> JsResult<PBKDF2> {
let [arg0, arg1, arg2, arg3, arg4, arg5] = call_frame.arguments_as_array::<6>();

Expand Down Expand Up @@ -181,17 +181,16 @@ impl PBKDF2 {
};
// Non-async path: `StringOrBuffer` fields drop with `out` on early return — no explicit call needed.
let mut guard = scopeguard::guard(&mut out, |out| {
if global_this.has_exception() && is_async {
if global_this.has_exception() && flavor == Flavor::Async {
bun_jsc::Unprotect::unprotect(out);
}
});

let allow_string_object = true;
guard.salt = match StringOrBuffer::from_js_maybe_async(
global_this,
arg1,
is_async,
allow_string_object,
flavor,
StringObjects::Allow,
)? {
Some(v) => v,
None => {
Expand All @@ -210,8 +209,8 @@ impl PBKDF2 {
guard.password = match StringOrBuffer::from_js_maybe_async(
global_this,
arg0,
is_async,
allow_string_object,
flavor,
StringObjects::Allow,
)? {
Some(v) => v,
None => {
Expand All @@ -227,13 +226,13 @@ impl PBKDF2 {
return Err(global_this.throw_invalid_arguments(format_args!("password is too long")));
}

if !is_async {
if flavor == Flavor::Sync {
if let StringOrBuffer::Buffer(buffer) = &mut guard.salt {
buffer.buffer = ArrayBuffer::from_typed_array(global_this, buffer.buffer.value);
}
}

if is_async {
if flavor == Flavor::Async {
if !arg5.is_function() {
return Err(global_this.throw_invalid_argument_type_value(
b"callback",
Expand All @@ -260,7 +259,7 @@ impl bun_jsc::Unprotect for PBKDF2 {

/// `crypto.pbkdf2` off the JS thread.
pub(crate) struct Pbkdf2Job {
/// `from_js(.., is_async=true)` protected the input buffers; the
/// `from_js(.., Flavor::Async)` protected the input buffers; the
/// [`bun_jsc::ThreadSafe`] releases that with the job.
pub pbkdf2: bun_jsc::ThreadSafe<PBKDF2>,
pub output: Vec<u8>,
Expand Down Expand Up @@ -316,7 +315,7 @@ pub(crate) fn create_job(global_this: &JSGlobalObject, data: PBKDF2) -> JSValue
Job::<Pbkdf2Job>::schedule(
&cx,
Pbkdf2Job {
// `from_js(.., is_async=true)` already protected — adopt, don't re-protect.
// `from_js(.., Flavor::Async)` already protected — adopt, don't re-protect.
pbkdf2: bun_jsc::ThreadSafe::adopt(data),
output: Vec::new(),
err: false,
Expand Down
5 changes: 3 additions & 2 deletions src/runtime/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ pub mod assert {
#[path = "node/types.rs"]
pub mod types;
pub use types::{
BlobOrStringOrBuffer, Dirent, Encoding, FileSystemFlags, PathLike, PathOrBlob,
PathOrFileDescriptor, StringOrBuffer, Valid, VectorArrayBuffer, mode_from_js,
BlobOrStringOrBuffer, Dirent, Encoding, FileBlobs, FileSystemFlags, Flavor, PathLike,
PathOrBlob, PathOrFileDescriptor, StringObjects, StringOrBuffer, Valid, VectorArrayBuffer,
mode_from_js,
};

pub use bun_jsc::MarkedArrayBuffer as Buffer;
Expand Down
34 changes: 24 additions & 10 deletions src/runtime/node/node_crypto_binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use bun_jsc::{
JsThread, Protected, Strong,
};

use crate::node::StringOrBuffer;
use crate::node::{Flavor, StringObjects, StringOrBuffer};

// `&JSGlobalObject` is ABI-identical to a non-null pointer; remaining params
// are by-value `JSValue`, so no caller-side preconditions remain.
Expand Down Expand Up @@ -735,7 +735,8 @@ pub mod random {
pub(crate) struct Scrypt {
// Plain `StringOrBuffer` — NOT `ThreadSafe<_>`. The struct serves both
// `scryptSync` (no protect taken) and async `scrypt` (protect taken in
// `from_js_maybe_async(.., true)`, adopted into a `ThreadSafe` by the job).
// `from_js_maybe_async(.., Flavor::Async, ..)`, adopted into a `ThreadSafe`
// by the job).
Comment thread
alii marked this conversation as resolved.
password: StringOrBuffer,
salt: StringOrBuffer,
n: u32,
Expand Down Expand Up @@ -768,6 +769,11 @@ mod _impl {
] = call_frame.arguments_as_array::<5>();
let mut maybe_options_value: Option<JSValue> = Some(options_arg);
let mut callback = callback_arg;
let flavor = if IS_ASYNC {
Flavor::Async
} else {
Flavor::Sync
};

if IS_ASYNC {
if callback.is_undefined() {
Expand All @@ -776,8 +782,12 @@ mod _impl {
}
}

let Some(password) =
StringOrBuffer::from_js_maybe_async(global, password_value, IS_ASYNC, true)?
let Some(password) = StringOrBuffer::from_js_maybe_async(
global,
password_value,
flavor,
StringObjects::Allow,
)?
else {
return Err(global.throw_invalid_argument_type_value(
b"password",
Expand All @@ -795,8 +805,12 @@ mod _impl {
}
});

let Some(salt) =
StringOrBuffer::from_js_maybe_async(global, salt_value, IS_ASYNC, true)?
let Some(salt) = StringOrBuffer::from_js_maybe_async(
global,
salt_value,
flavor,
StringObjects::Allow,
)?
else {
return Err(global.throw_invalid_argument_type_value(
b"salt",
Expand Down Expand Up @@ -1028,8 +1042,8 @@ mod _impl {
}

impl bun_jsc::Unprotect for Scrypt {
/// Release the `protect()` taken by `from_js_maybe_async(.., true)` on the
/// async path (via the job's `ThreadSafe`). The sync path never calls this.
/// Release the `protect()` taken by `from_js_maybe_async(.., Flavor::Async, ..)`
/// on the async path (via the job's `ThreadSafe`). The sync path never calls this.
Comment thread
alii marked this conversation as resolved.
#[inline]
fn unprotect(&mut self) {
bun_jsc::Unprotect::unprotect(&mut self.password);
Expand Down Expand Up @@ -1109,14 +1123,14 @@ mod _impl {

#[bun_jsc::host_fn]
fn pbkdf2(global_this: &JSGlobalObject, call_frame: &CallFrame) -> JsResult<JSValue> {
let data = PBKDF2::from_js(global_this, call_frame, true)?;
let data = PBKDF2::from_js(global_this, call_frame, Flavor::Async)?;

Ok(pbkdf2::create_job(global_this, data))
}

#[bun_jsc::host_fn]
fn pbkdf2_sync(global_this: &JSGlobalObject, call_frame: &CallFrame) -> JsResult<JSValue> {
let data = PBKDF2::from_js(global_this, call_frame, false)?;
let data = PBKDF2::from_js(global_this, call_frame, Flavor::Sync)?;
// `PBKDF2`'s `StringOrBuffer` fields release on `Drop`, so the local
// just goes out of scope.
let mut data = data;
Expand Down
26 changes: 11 additions & 15 deletions src/runtime/node/node_fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,13 @@ use super::stat::Stats;
use super::time_like::TimeLike;
use super::types::{
ArgumentsSlice, Dirent, Encoding, FdArgExt as _, FileSystemFlags, FileSystemFlagsKind,
NameTooLong, PathLike, PathLikeExt as _, PathOrFdExt as _, StringOrBuffer, VectorArrayBuffer,
NameTooLong, PathLike, PathLikeExt as _, PathOrFdExt as _, StringObjects, StringOrBuffer,
VectorArrayBuffer,
};
// Re-exported publicly: `crate::node::fs::PathOrFileDescriptor` is the
// canonical path used by `cli/build_command.rs` et al.
pub use super::types::PathOrFileDescriptor;
// canonical path used by `cli/build_command.rs` et al., and `node_fs::Flavor`
// by every caller that runs an operation directly (`read_file(.., Flavor::Sync)`).
Comment thread
alii marked this conversation as resolved.
pub use super::types::{Flavor, PathOrFileDescriptor};

/// Local alias for the many `node::foo` call sites below, routing to `super::*`.
mod node {
Expand Down Expand Up @@ -473,15 +475,6 @@ pub(crate) const DEFAULT_PERMISSION: Mode = 0;
// `&AbortSignal` inherent methods — the former `AbortSignalRefExt` shim with
// per-call `unsafe { self.as_ref() }` is gone. `unref()` is handled by `Drop`.

/// All async FS functions are run in a thread pool, but some implementations may
/// decide to do something slightly different. For example, reading a file has
/// an extra stack buffer in the async case.
#[derive(Copy, Clone, PartialEq, Eq, core::marker::ConstParamTy)]
pub enum Flavor {
Sync,
Async,
}

// ──────────────────────────────────────────────────────────────────────────
// Async task type aliases
// ──────────────────────────────────────────────────────────────────────────
Expand Down Expand Up @@ -4213,11 +4206,14 @@ pub mod args {
}
}
}
let flavor = if arguments.will_be_async {
Flavor::Async
} else {
Flavor::Sync
};
// String objects not allowed (typeof new String("hi") === "object")
// https://github.com/nodejs/node/blob/6f946c95b9da75c70e868637de8161bc8d048379/lib/internal/fs/utils.js#L916
let allow_string_object = false;
let is_async = arguments.will_be_async;
let data = StringOrBuffer::from_js_with_encoding_maybe_async(ctx, data_value, encoding, is_async, allow_string_object)?
let data = StringOrBuffer::from_js_with_encoding_maybe_async(ctx, data_value, encoding, flavor, StringObjects::Reject)?
.ok_or_else(|| validators::throw_err_invalid_arg_type_with_message(ctx, format_args!("The \"data\" argument must be of type string or an instance of Buffer, TypedArray, or DataView")))?;
let abort_signal = scopeguard::ScopeGuard::into_inner(abort_signal);
Ok(WriteFile {
Expand Down
Loading
Loading