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
4 changes: 1 addition & 3 deletions src/js_parser_jsc/Macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -596,9 +596,7 @@ impl<'a> Run<'a> {

pub fn run(&mut self, value: JSValue) -> Result<Expr, MacroError> {
use ConsoleObject::formatter::Tag as T;
// `Tag::get` returns `TagResult { tag: TagPayload, .. }`;
// collapse the payload to its discriminant via `.tag()`.
match T::get(value, self.global)?.tag.tag() {
match T::get(value, self.global)?.tag {
T::Error => self.coerce(T::Error, value),
T::Undefined => self.coerce(T::Undefined, value),
T::Null => self.coerce(T::Null, value),
Expand Down
19 changes: 5 additions & 14 deletions src/js_parser_jsc/expr_jsc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use bun_ast::{E, Expr, ExprData, G, ToJSError};
use bun_collections::VecExt;
use bun_core::{StackCheck, String as BunString, strings};
use bun_core::{StackCheck, String as BunString};
use bun_jsc::{JSGlobalObject, JSValue, JsError, bun_string_jsc};

/// Map a `bun_jsc::JsError` into the AST-layer `ToJSError`. Orphan rules forbid
Expand Down Expand Up @@ -143,20 +143,11 @@ pub(crate) fn object_to_js(
Ok(obj)
}

/// Serialize UTF-8 bytes to a JS string, transcoding to UTF-16 only when the
/// bytes are not pure ASCII (`to_utf16_alloc` returns `Ok(None)` for
/// pure-ASCII, in which case the 8-bit Latin-1 form is kept).
/// Serialize UTF-8 bytes to a JS string. `createUTF8ForJS` allocates the final
/// WTF string in one pass (Latin-1 for pure-ASCII, UTF-16 with U+FFFD
/// replacement of invalid sequences otherwise).
fn utf8_bytes_to_js(bytes: &[u8], global: &JSGlobalObject) -> Result<JSValue, ToJSError> {
let utf16 = strings::to_utf16_alloc(bytes, false, false).map_err(|_| ToJSError::OutOfMemory)?;
if let Some(utf16) = utf16 {
let (mut out, chars) = BunString::create_uninitialized_utf16(utf16.len());
chars.copy_from_slice(&utf16);
bun_string_jsc::transfer_to_js(&mut out, global).map_err(js_err)
} else {
let (mut out, chars) = BunString::create_uninitialized_latin1(bytes.len());
chars.copy_from_slice(bytes);
bun_string_jsc::transfer_to_js(&mut out, global).map_err(js_err)
}
bun_string_jsc::create_utf8_for_js(global, bytes).map_err(js_err)
}

/// `E.String` → JS string conversion.
Expand Down
285 changes: 123 additions & 162 deletions src/jsc/AsyncModule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,116 @@ impl AsyncModule {
drop(unsafe { bun_core::heap::take(this) });
}

/// Shared builder for the package resolve/download error objects: creates
/// the error instance from `msg` and sets the `url` (when present),
/// `name`, and `pkg` properties.
fn package_error_instance(
global_this: &JSGlobalObject,
msg: &[u8],
name: &[u8],
url: &[u8],
pkg: &[u8],
) -> JSValue {
let error_instance = ZigString::from_bytes(msg)
.with_encoding()
.to_error_instance(global_this);
if !url.is_empty() {
error_instance.put(
global_this,
b"url",
ZigString::from_bytes(url)
.with_encoding()
.to_js(global_this),
);
}
error_instance.put(
global_this,
b"name",
ZigString::from_bytes(name)
.with_encoding()
.to_js(global_this),
);
error_instance.put(
global_this,
b"pkg",
ZigString::from_bytes(pkg)
.with_encoding()
.to_js(global_this),
);
error_instance
}

fn put_referrer(global_this: &JSGlobalObject, error_instance: JSValue, referrer: &[u8]) {
if !referrer.is_empty() && referrer != b"undefined" {
error_instance.put(
global_this,
b"referrer",
ZigString::from_bytes(referrer)
.with_encoding()
.to_js(global_this),
);
}
}

/// Sets `sourceURL`/`line`/`lineText`/`column` from the import record's
/// source location.
fn put_import_location(
&self,
global_this: &JSGlobalObject,
error_instance: JSValue,
import_record_id: u32,
) {
let location = bun_ast::range_data(
Some(&self.parse_result.source),
self.parse_result.ast.import_records[import_record_id as usize].range,
b"",
)
.location
.unwrap();
error_instance.put(
global_this,
b"sourceURL",
ZigString::from_bytes(self.parse_result.source.path.text)
.with_encoding()
.to_js(global_this),
);
error_instance.put(
global_this,
b"line",
JSValue::js_number(location.line as f64),
);
if let Some(line_text) = location.line_text.as_deref() {
error_instance.put(
global_this,
b"lineText",
ZigString::from_bytes(line_text)
.with_encoding()
.to_js(global_this),
);
}
error_instance.put(
global_this,
b"column",
JSValue::js_number(location.column as f64),
);
}

/// Rejects the module's promise with `error_instance` and drops the event
/// loop keepalive. The caller (`Queue::retain_mut`) returns `false` and
/// Vec drops the element, running Drop.
fn reject_with(&mut self, global_this: &JSGlobalObject, error_instance: JSValue) {
let promise_value = self.promise.swap();
let promise = promise_value.as_internal_promise().unwrap();
promise_value.ensure_still_alive();
self.poll_ref.unref(bun_io::posix_event_loop::get_vm_ctx(
bun_io::AllocatorType::Js,
));
// `JSInternalPromise` is an `opaque_ffi!` ZST handle; `opaque_mut` is
// the centralised non-null deref proof.
let _ =
JSInternalPromise::opaque_mut(promise).reject_as_handled(global_this, error_instance);
}

// write! into Vec<u8>
// is infallible here; `.ok()` collapses the `fmt::Result`, so this never
// actually returns Err — the wide Result is kept for call-site uniformity.
Expand Down Expand Up @@ -870,96 +980,20 @@ impl AsyncModule {
b"PackageResolveError"
};

let error_instance = ZigString::from_bytes(&msg)
.with_encoding()
.to_error_instance(global_this);
if !result.url.is_empty() {
error_instance.put(
global_this,
b"url",
ZigString::from_bytes(result.url)
.with_encoding()
.to_js(global_this),
);
}
error_instance.put(
global_this,
b"name",
ZigString::from_bytes(name)
.with_encoding()
.to_js(global_this),
);
error_instance.put(
global_this,
b"pkg",
ZigString::from_bytes(result.name)
.with_encoding()
.to_js(global_this),
);
let error_instance =
Self::package_error_instance(global_this, &msg, name, result.url, result.name);
error_instance.put(
global_this,
b"specifier",
ZigString::from_bytes(self.specifier())
.with_encoding()
.to_js(global_this),
);
let location = bun_ast::range_data(
Some(&self.parse_result.source),
self.parse_result.ast.import_records[import_record_id as usize].range,
b"",
)
.location
.unwrap();
error_instance.put(
global_this,
b"sourceURL",
ZigString::from_bytes(self.parse_result.source.path.text)
.with_encoding()
.to_js(global_this),
);
error_instance.put(
global_this,
b"line",
JSValue::js_number(location.line as f64),
);
if let Some(line_text) = location.line_text.as_deref() {
error_instance.put(
global_this,
b"lineText",
ZigString::from_bytes(line_text)
.with_encoding()
.to_js(global_this),
);
}
error_instance.put(
global_this,
b"column",
JSValue::js_number(location.column as f64),
);
let referrer = self.referrer();
if !referrer.is_empty() && referrer != b"undefined" {
error_instance.put(
global_this,
b"referrer",
ZigString::from_bytes(referrer)
.with_encoding()
.to_js(global_this),
);
}
self.put_import_location(global_this, error_instance, import_record_id);
Self::put_referrer(global_this, error_instance, self.referrer());

let promise_value = self.promise.swap();
let promise = promise_value.as_internal_promise().unwrap();
promise_value.ensure_still_alive();
let _ = vm;
self.poll_ref.unref(bun_io::posix_event_loop::get_vm_ctx(
bun_io::AllocatorType::Js,
));
// The caller (Queue::retain_mut) returns `false` and Vec drops the
// element, running Drop.
// `JSInternalPromise` is an `opaque_ffi!` ZST handle; `opaque_mut` is
// the centralised non-null deref proof.
let _ =
JSInternalPromise::opaque_mut(promise).reject_as_handled(global_this, error_instance);
self.reject_with(global_this, error_instance);
Ok(())
}

Expand Down Expand Up @@ -1085,50 +1119,12 @@ impl AsyncModule {
b"TarballDownloadError"
};

let error_instance = ZigString::from_bytes(&msg)
.with_encoding()
.to_error_instance(global_this);
if !result.url.is_empty() {
error_instance.put(
global_this,
b"url",
ZigString::from_bytes(result.url)
.with_encoding()
.to_js(global_this),
);
}
error_instance.put(
global_this,
b"name",
ZigString::from_bytes(name)
.with_encoding()
.to_js(global_this),
);
error_instance.put(
global_this,
b"pkg",
ZigString::from_bytes(result.name)
.with_encoding()
.to_js(global_this),
);
let specifier = self.specifier();
if !specifier.is_empty() && specifier != b"undefined" {
error_instance.put(
global_this,
b"referrer",
ZigString::from_bytes(specifier)
.with_encoding()
.to_js(global_this),
);
}

let location = bun_ast::range_data(
Some(&self.parse_result.source),
self.parse_result.ast.import_records[import_record_id as usize].range,
b"",
)
.location
.unwrap();
let error_instance =
Self::package_error_instance(global_this, &msg, name, result.url, result.name);
Self::put_referrer(global_this, error_instance, self.specifier());
// `sourceURL` et al. follow `specifier` here (the resolve-error path
// puts `specifier` first), so the helper runs after this put; the
// location computation itself is pure.
error_instance.put(
global_this,
b"specifier",
Expand All @@ -1140,45 +1136,10 @@ impl AsyncModule {
.with_encoding()
.to_js(global_this),
);
error_instance.put(
global_this,
b"sourceURL",
ZigString::from_bytes(self.parse_result.source.path.text)
.with_encoding()
.to_js(global_this),
);
error_instance.put(
global_this,
b"line",
JSValue::js_number(location.line as f64),
);
if let Some(line_text) = location.line_text.as_deref() {
error_instance.put(
global_this,
b"lineText",
ZigString::from_bytes(line_text)
.with_encoding()
.to_js(global_this),
);
}
error_instance.put(
global_this,
b"column",
JSValue::js_number(location.column as f64),
);
self.put_import_location(global_this, error_instance, import_record_id);

let promise_value = self.promise.swap();
let promise = promise_value.as_internal_promise().unwrap();
promise_value.ensure_still_alive();
let _ = vm;
self.poll_ref.unref(bun_io::posix_event_loop::get_vm_ctx(
bun_io::AllocatorType::Js,
));
// Caller drops via retain_mut → false.
// `JSInternalPromise` is an `opaque_ffi!` ZST handle; `opaque_mut` is
// the centralised non-null deref proof.
let _ =
JSInternalPromise::opaque_mut(promise).reject_as_handled(global_this, error_instance);
self.reject_with(global_this, error_instance);
Ok(())
}

Expand Down
Loading
Loading