Skip to content
Closed
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
33 changes: 16 additions & 17 deletions src/jsc/web_worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -490,14 +490,16 @@ impl WebWorker {
// SAFETY: `parent` is the calling thread's live VM (BACKREF).
let parent_ref = unsafe { &mut *parent };
let prev_log = parent_ref.transpiler.log;
let mut temp_log = bun_ast::Log::default();
parent_ref.transpiler.set_log(&raw mut temp_log);
// RAII: log pointer restored and temp log dropped on every return path.
let mut restore = scopeguard::guard((parent_ref, temp_log), |(p, log)| {
// `temp_log` is moved into the guard FIRST, then `set_log` targets its
// final address — the transpiler stores a raw `*mut Log`, so setting it
// before the move would leave a dangling pointer to moved-from stack.
let mut restore = scopeguard::guard((parent_ref, bun_ast::Log::default()), |(p, log)| {
p.transpiler.set_log(prev_log);
drop(log);
});
let (parent_ref, temp_log) = &mut *restore;
parent_ref.transpiler.set_log(&raw mut *temp_log);

// SAFETY: caller passed valid (ptr,len) (or `(null,0)`); slice borrowed from C++.
let preload_modules: &[BunString] =
Expand All @@ -509,12 +511,7 @@ impl WebWorker {
// SAFETY: `parent_ref` is the live VM on the calling (parent)
// thread — its `transpiler` is uniquely owned here.
if let Some(preload) = unsafe {
resolve_entry_point_specifier(
*parent_ref,
utf8_slice.slice(),
error_message,
temp_log,
)
resolve_entry_point_specifier(*parent_ref, utf8_slice.slice(), error_message)
} {
preloads.push(preload.to_vec().into_boxed_slice());
}
Expand Down Expand Up @@ -1043,21 +1040,16 @@ impl WebWorker {
// spin() goes through shutdown() which is noreturn, so a `defer free`
// here would never run anyway.
let mut resolve_error = BunString::empty();
let vm_log = vm.log_mut().unwrap();
// SAFETY: `vm_ptr` is the live worker-thread VM; the fn takes a raw ptr
// (no `&mut`) because `vm` is already published under `vm_lock` — see
// `resolve_entry_point_specifier` Safety contract.
let path = match unsafe {
resolve_entry_point_specifier(
vm_ptr,
&self.unresolved_specifier,
&mut resolve_error,
vm_log,
)
resolve_entry_point_specifier(vm_ptr, &self.unresolved_specifier, &mut resolve_error)
} {
Some(p) => p,
None => {
vm.as_mut().exit_handler.exit_code = 1;
let vm_log = vm.log_mut().unwrap();
if vm_log.errors == 0 && !resolve_error.is_empty() {
let err = resolve_error.to_utf8();
// `Log::add_error` takes `impl IntoText`; pass an owned
Expand Down Expand Up @@ -1537,7 +1529,6 @@ unsafe fn resolve_entry_point_specifier<'s>(
parent: *mut VirtualMachine,
str: &'s [u8],
error_message: &mut BunString,
log: &mut bun_ast::Log,
) -> Option<&'s [u8]> {
// SAFETY: per fn contract; read-only field.
if let Some(graph) = unsafe { (*parent).standalone_module_graph } {
Expand Down Expand Up @@ -1638,6 +1629,14 @@ unsafe fn resolve_entry_point_specifier<'s>(
Err(_) => {
// `global` valid for VM lifetime; safe ZST-handle deref (panics on null).
let global = JSGlobalObject::opaque_ref(global);
// `resolve_entry_point` wrote the error to `transpiler.log`. Read
// it back through the same raw pointer — a separate `&mut Log`
// parameter would be `noalias` and miss the write under
// optimization (the two point at the same allocation).
// SAFETY: `transpiler.log` is non-null after init and outlives
// this call (both callers swap in a log that lives on their
// stack or VM for the duration).
let log = unsafe { &*(*parent).transpiler.log };
let out: jsc::JsResult<BunString> = (|| {
let out = log.to_js(global, "Error resolving Worker entry point")?;
out.to_bun_string(global)
Expand Down
16 changes: 16 additions & 0 deletions test/js/web/workers/worker-preload-resolve-error.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { expect, test } from "bun:test";

test("Worker preload: unresolvable module surfaces the resolve error, not 'undefined'", () => {
let caught: unknown;
try {
new Worker(new URL("worker-fixture-preload-entry.js", import.meta.url).href, {
preload: ["./this-preload-does-not-exist.js"],
});
} catch (e) {
caught = e;
}
expect(caught).toBeInstanceOf(Error);
const message = String((caught as Error).message);
expect(message).not.toBe("undefined");
expect(message).toContain("this-preload-does-not-exist");
});
Comment thread
robobun marked this conversation as resolved.
1 change: 1 addition & 0 deletions test/no-validate-leaksan.txt
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ test/js/node/fs/abort-signal-leak-read-write-file.test.ts
test/js/node/process/process.test.js
test/js/web/websocket/websocket.test.js
test/js/web/workers/worker.test.ts
test/js/web/workers/worker-preload-resolve-error.test.ts
Comment thread
robobun marked this conversation as resolved.
test/regression/issue/11664.test.ts

# ASSERTION FAILED: m_cellState == CellState::DefinitelyWhite
Expand Down
Loading