Skip to content
Open
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
10 changes: 8 additions & 2 deletions src/jsc/JSGlobalObject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,10 @@ impl JSGlobalObject {
let actual_type = if value.js_type().is_array() {
bun_core::ZigString::static_(b"array")
} else {
value.js_type_string(self).get_zig_string(self)
match value.js_type_string(self).get_zig_string(self) {
Ok(s) => s,
Err(e) => return e,
}
};
self.err(
JscError::INVALID_ARG_TYPE,
Expand Down Expand Up @@ -644,7 +647,10 @@ impl JSGlobalObject {
) -> JsError {
// `ZigStringSlice` is RAII: `Owned` frees
// its `Vec<u8>`, `WTF` derefs the backing `WTFStringImpl` in `Drop`.
let ty_str = value.js_type_string(self).to_slice(self);
let ty_str = match value.js_type_string(self).to_slice(self) {
Ok(s) => s,
Err(e) => return e,
};
self.err(
JscError::INVALID_ARG_TYPE,
format_args!(
Expand Down
32 changes: 19 additions & 13 deletions src/jsc/JSString.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,31 +33,39 @@ impl JSString {
JSValue::from_cell(self)
}

pub(crate) fn to_zig_string(&self, global: &JSGlobalObject, zig_str: &mut ZigString) {
JSC__JSString__toZigString(self, global, zig_str)
/// Flattening a rope can throw (JSC throws its "Out of memory" `RangeError`
/// when the flat buffer cannot be allocated). The C++ shim returns an empty
/// string in that case and leaves the exception on the VM, so it is observed
/// here, the same way `JSValue::to_zig_string` does.
pub(crate) fn to_zig_string(
&self,
global: &JSGlobalObject,
zig_str: &mut ZigString,
) -> JsResult<()> {
crate::from_js_host_call_generic(global, || {
JSC__JSString__toZigString(self, global, zig_str)
})
}

pub fn ensure_still_alive(&self) {
// Keep the cell pointer observable to the GC's conservative stack scan.
core::hint::black_box(std::ptr::from_ref::<Self>(self));
}

pub fn get_zig_string(&self, global: &JSGlobalObject) -> ZigString {
pub fn get_zig_string(&self, global: &JSGlobalObject) -> JsResult<ZigString> {
let mut out = ZigString::init(b"");
self.to_zig_string(global, &mut out);
out
self.to_zig_string(global, &mut out)?;
Ok(out)
}

#[inline]
pub fn view(&self, global: &JSGlobalObject) -> ZigString {
pub fn view(&self, global: &JSGlobalObject) -> JsResult<ZigString> {
self.get_zig_string(global)
}

/// doesn't always allocate
pub fn to_slice(&self, global: &JSGlobalObject) -> ZigStringSlice {
let mut str = ZigString::init(b"");
self.to_zig_string(global, &mut str);
str.to_slice()
pub fn to_slice(&self, global: &JSGlobalObject) -> JsResult<ZigStringSlice> {
Ok(self.get_zig_string(global)?.to_slice())
}

// `to_slice_clone` always allocates
Expand All @@ -66,9 +74,7 @@ impl JSString {
// callers a use-after-free once the cell is collected.

pub fn to_slice_clone(&self, global: &JSGlobalObject) -> JsResult<ZigStringSlice> {
let mut str = ZigString::init(b"");
self.to_zig_string(global, &mut str);
Ok(str.to_slice_clone())
Ok(self.get_zig_string(global)?.to_slice_clone())
}

// `to_slice_z` guarantees a trailing NUL
Expand Down
20 changes: 13 additions & 7 deletions src/jsc/VirtualMachine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6748,13 +6748,19 @@ fn wrap_unhandled_rejection_error_for_uncaught_exception(
if reason_str.is_string() {
// SAFETY: `as_string()` returns a non-null `*mut JSString` when
// `is_string()` is true; `view()` borrows it for the `write!` below.
let view = unsafe { (*reason_str.as_string()).view(global_object) };
return global_object
.err(
crate::ErrorCode::ERR_UNHANDLED_REJECTION,
format_args!("{MSG_1}{view}\"."),
)
.to_js();
match unsafe { (*reason_str.as_string()).view(global_object) } {
Ok(view) => {
return global_object
.err(
crate::ErrorCode::ERR_UNHANDLED_REJECTION,
format_args!("{MSG_1}{view}\"."),
)
.to_js();
}
// The reason is a string that cannot be flattened; report it the
// same way as a reason that could not be stringified at all.
Err(_) => global_object.clear_exception(),
}
}
global_object
.err(
Expand Down
41 changes: 26 additions & 15 deletions src/runtime/api/JSBundler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1455,24 +1455,35 @@ pub mod js_bundler {
{
resolve.value = ResolveValue::NoMatch;
} else {
let global = bv2_plugin(resolve.bv2).global_object();
let plugin = bv2_plugin(resolve.bv2);
let global = plugin.global_object();
// `to_slice_clone` already heap-allocates; `into_vec` moves that
// buffer out instead of allocating a second copy.
let path = path_value
.to_slice_clone(global)
.expect("Unexpected: path is not a string")
.into_vec()
.into_boxed_slice();
let namespace = namespace_value
.to_slice_clone(global)
.expect("Unexpected: namespace is not a string")
.into_vec()
.into_boxed_slice();
resolve.value = ResolveValue::Success(ResolveSuccess {
path,
namespace,
external: external_value.to_boolean(),
let strings = path_value.to_slice_clone(global).and_then(|path| {
let namespace = namespace_value.to_slice_clone(global)?;
Ok((path, namespace))
});
resolve.value = match strings {
Ok((path, namespace)) => ResolveValue::Success(ResolveSuccess {
path: path.into_vec().into_boxed_slice(),
namespace: namespace.into_vec().into_boxed_slice(),
external: external_value.to_boolean(),
}),
// Both values are strings (BundlerPlugin.ts checked), but
// flattening one can still throw. The exception has to be taken
// here: left pending, it rejects `runOnResolvePlugins`, whose
// rejection handler calls `JSBundlerPlugin__addError` and
// answers this same request a second time. Report it the way
// `addError` would have instead.
Err(err) => {
let exception = global.take_exception(err);
ResolveValue::Err(plugin_msg_from_js(
plugin,
&resolve.import_record.source_file,
exception,
))
}
};
}

bv2_mut(resolve.bv2).on_resolve_async(resolve);
Expand Down
14 changes: 7 additions & 7 deletions src/runtime/api/bun/h2_frame_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8046,7 +8046,7 @@ impl H2FrameParser {
}
};

let value_slice = value_str.to_slice(global_object);
let value_slice = value_str.to_slice(global_object)?;
let value = value_slice.slice();

if let Some(ret) = handle_encode(this, value, never_index)? {
Expand Down Expand Up @@ -8080,7 +8080,7 @@ impl H2FrameParser {
}
};

let value_slice = value_str.to_slice(global_object);
let value_slice = value_str.to_slice(global_object)?;
let value = value_slice.slice();
bun_output::scoped_log!(
H2FrameParser,
Expand Down Expand Up @@ -8447,7 +8447,7 @@ impl H2FrameParser {
};
let mut encode_value = |item: JSValue| -> JsResult<Option<JSValue>> {
let value_str = item.to_js_string(global_object)?;
let value_slice = value_str.to_slice(global_object);
let value_slice = value_str.to_slice(global_object)?;
let value = value_slice.slice();
if !is_valid_header_value(value) {
return Err(global_object
Expand Down Expand Up @@ -8852,7 +8852,7 @@ impl H2FrameParser {
}

let name_str = name_js.to_js_string(global_object)?;
let name_slice = name_str.to_slice(global_object);
let name_slice = name_str.to_slice(global_object)?;
let name = name_slice.slice();
if name.is_empty() {
continue;
Expand Down Expand Up @@ -8920,7 +8920,7 @@ impl H2FrameParser {
}
};

let value_slice = value_str.to_slice(global_object);
let value_slice = value_str.to_slice(global_object)?;
let value = value_slice.slice();
if !is_valid_header_value(value) {
return Err(global_object
Expand Down Expand Up @@ -9087,7 +9087,7 @@ impl H2FrameParser {
}
};

let value_slice = value_str.to_slice(global_object);
let value_slice = value_str.to_slice(global_object)?;
let value = value_slice.slice();
if !is_valid_header_value(value) {
return Err(global_object
Expand Down Expand Up @@ -9157,7 +9157,7 @@ impl H2FrameParser {
}
};

let value_slice = value_str.to_slice(global_object);
let value_slice = value_str.to_slice(global_object)?;
let value = value_slice.slice();
if !is_valid_header_value(value) {
return Err(global_object
Expand Down
6 changes: 3 additions & 3 deletions src/runtime/dns_jsc/dns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4993,7 +4993,7 @@ impl Resolver {
if record_type_str.length() == 0 {
break 'brk RecordType::DEFAULT;
}
match RECORD_TYPE_MAP.get(record_type_str.to_slice(global_this).slice()) {
match RECORD_TYPE_MAP.get(record_type_str.to_slice(global_this)?.slice()) {
Some(r) => *r,
None => {
return Err(global_this.throw_invalid_argument_property_value(
Expand Down Expand Up @@ -5186,7 +5186,7 @@ impl Resolver {
};
}

let name = name_str.to_slice(global_this);
let name = name_str.to_slice(global_this)?;
let resolver = global_resolver(global_this);

resolver.do_lookup(name.slice(), port, options, global_this)
Expand Down Expand Up @@ -5966,7 +5966,7 @@ impl Resolver {
"non-empty string",
));
}
let addr_slice = addr_str.to_slice(global_this);
let addr_slice = addr_str.to_slice(global_this)?;
let addr_s = addr_slice.slice();

let port_value = arguments[1];
Expand Down
8 changes: 7 additions & 1 deletion src/runtime/ipc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2022,7 +2022,13 @@ fn import_windows_socket_payload(global: &JSGlobalObject, msg_data: JSValue) ->
return None;
}
};
let hex = jsc::JSString::opaque_ref(info_value.as_string()).to_slice(global);
let hex = match jsc::JSString::opaque_ref(info_value.as_string()).to_slice(global) {
Ok(hex) => hex,
Err(_) => {
global.clear_exception();
return None;
}
};
let expected = bun_uws::socket_transfer::bsd_socket_export_size() as usize;
let mut info = vec![0u8; expected];
let decoded = strings::decode_hex_to_bytes_truncate(&mut info, hex.slice());
Expand Down
8 changes: 4 additions & 4 deletions src/runtime/node/node_cluster_binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ pub(crate) fn cluster_raw_bind(global: &JSGlobalObject, frame: &CallFrame) -> Js
let atype = address_type.to_int32();

let host_owned: Vec<u8> = if address.is_string() {
let s = bun_jsc::JSString::opaque_ref(address.as_string()).to_slice(global);
let s = bun_jsc::JSString::opaque_ref(address.as_string()).to_slice(global)?;
let mut v = s.slice().to_vec();
v.push(0);
v
Expand Down Expand Up @@ -422,7 +422,7 @@ pub(crate) fn cluster_raw_bind(global: &JSGlobalObject, frame: &CallFrame) -> Js
let mut is_udp = false;
let atype: i32;
if address_type.is_string() {
let s = bun_jsc::JSString::opaque_ref(address_type.as_string()).to_slice(global);
let s = bun_jsc::JSString::opaque_ref(address_type.as_string()).to_slice(global)?;
is_udp = true;
atype = if s.slice() == b"udp6" { 6 } else { 4 };
} else {
Expand All @@ -447,7 +447,7 @@ pub(crate) fn cluster_raw_bind(global: &JSGlobalObject, frame: &CallFrame) -> Js
if !address.is_string() {
return Err(global.throw_invalid_argument_type_value("address", "string", address));
}
let path_slice = bun_jsc::JSString::opaque_ref(address.as_string()).to_slice(global);
let path_slice = bun_jsc::JSString::opaque_ref(address.as_string()).to_slice(global)?;
let path_bytes = path_slice.slice();
// SAFETY: sockaddr_un is plain C data; all-zero is a valid value.
let mut sun: libc::sockaddr_un = unsafe { bun_core::ffi::zeroed_unchecked() };
Expand Down Expand Up @@ -599,7 +599,7 @@ pub(crate) fn cluster_raw_bind(global: &JSGlobalObject, frame: &CallFrame) -> Js
let fd: c_int;
let bound_family: c_int;
if address.is_string() {
let addr_slice = bun_jsc::JSString::opaque_ref(address.as_string()).to_slice(global);
let addr_slice = bun_jsc::JSString::opaque_ref(address.as_string()).to_slice(global)?;
let addr_bytes = addr_slice.slice();
let mut addr_z: [u8; 256] = [0; 256];
if addr_bytes.len() >= addr_z.len() {
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/node/node_util_binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ pub(crate) fn parse_env(global: &JSGlobalObject, frame: &CallFrame) -> JsResult<

// `validate_string` accepts StringObject, so coerce to a primitive JSString
// before slicing.
let str = content.to_js_string(global)?.to_slice(global);
let str = content.to_js_string(global)?.to_slice(global)?;

let mut p = envloader::Loader::init();
p.load_from_string::<true, false>(str.slice())?;
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/node/node_zlib_binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,10 @@ pub(crate) fn crc32(global_this: &JSGlobalObject, callframe: &CallFrame) -> JsRe
// `is_string_literal()` guarantees `as_string()` is non-null and points to a
// live JSString cell on the JSC heap. `JSString` is an `opaque_ffi!`
// ZST handle; `opaque_ref` is the centralised deref proof.
break 'blk bun_jsc::JSString::opaque_ref(data.as_string()).to_slice(global_this);
break 'blk bun_jsc::JSString::opaque_ref(data.as_string()).to_slice(global_this)?;
}
let Some(buffer) = data.as_array_buffer(global_this) else {
let ty_str = data.js_type_string(global_this).to_slice(global_this);
let ty_str = data.js_type_string(global_this).to_slice(global_this)?;
// ty_str drops at end of scope
return Err(global_this
.err(
Expand Down
11 changes: 7 additions & 4 deletions src/runtime/node/util/validators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ use core::fmt;
use bun_core::ZigString;
use bun_jsc::{self as jsc, JSGlobalObject, JSValue, JsError, JsResult};

fn get_type_name(global_object: &JSGlobalObject, value: JSValue) -> ZigString {
fn get_type_name(global_object: &JSGlobalObject, value: JSValue) -> JsResult<ZigString> {
let js_type = value.js_type();
if js_type.is_array() {
return ZigString::static_("array");
return Ok(ZigString::static_("array"));
}
value
.js_type_string(global_object)
Expand Down Expand Up @@ -43,7 +43,10 @@ pub(crate) fn throw_err_invalid_arg_type(
expected_type: &str,
value: JSValue,
) -> JsError {
let actual_type = get_type_name(global_this, value);
let actual_type = match get_type_name(global_this, value) {
Ok(actual_type) => actual_type,
Err(e) => return e,
};
throw_err_invalid_arg_type_with_message(
global_this,
format_args!(
Expand Down Expand Up @@ -395,7 +398,7 @@ pub(crate) fn validate_array(
min_length: Option<i32>,
) -> JsResult<()> {
if !value.js_type().is_array() {
let actual_type = get_type_name(global_this, value);
let actual_type = get_type_name(global_this, value)?;
return Err(throw_err_invalid_arg_type_with_message(
global_this,
format_args!(
Expand Down
Loading
Loading