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
37 changes: 24 additions & 13 deletions src/jsc/ConsoleObject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,10 @@ fn message_with_type_and_level_(
// SAFETY: no other borrow of the console is live in this
// early-return arm (the deferred `_indent_guard` only holds the raw
// pointer, not a reference).
let ew = unsafe { vm_console_mut(global) }.error_writer();
let this = unsafe { vm_console_mut(global) };
let default_indent = this.default_indent;
let ew = this.error_writer();
let _ = formatter::write_indent_n(u32::from(default_indent), ew);
let _ = ew.write_all(text.as_bytes());
let _ = ew.flush();
return Ok(());
Expand All @@ -445,10 +448,7 @@ fn message_with_type_and_level_(
let default_indent = unsafe { vm_console_mut(global) }.default_indent;

// SAFETY: see [`vm_console`] — `console` points at the live boxed
// `ConsoleObject` for this VM; JS-thread-only. Kept as a raw deref (not
// `vm_console_mut`) so the resulting `writer` borrow does not pin a
// long-lived `&mut ConsoleObject` across the re-derive in the empty-`Log`
// arm below.
// `ConsoleObject` for this VM; JS-thread-only.
let raw_writer: &mut bun_core::io::Writer = unsafe {
if matches!(level, MessageLevel::Warning | MessageLevel::Error) {
(*console).error_writer()
Expand Down Expand Up @@ -541,13 +541,11 @@ fn message_with_type_and_level_(
print_options,
)?;
} else if message_type == MessageType::Log {
// SAFETY: see [`vm_console`]. `writer` (above) is dead in this arm —
// the only later uses are in the mutually-exclusive `Trace` block, and
// `message_type == Log` here.
let w = unsafe { (*console).writer() };
let _ = w.write_all(b"\n");
let _ = w.flush();
let _ = formatter::write_indent_n(u32::from(default_indent), writer);
let _ = writer.write_all(b"\n");
let _ = writer.flush();
} else if message_type != MessageType::Trace {
let _ = formatter::write_indent_n(u32::from(default_indent), writer);
let _ = writer.write_all(b"undefined\n");
}

Expand Down Expand Up @@ -2800,7 +2798,10 @@ pub mod formatter {
/// conflict with the `&self` borrow `Formatter::write_indent` takes.
/// `self.indent` is a disjoint field read, so passing it by value here
/// keeps the borrow checker happy.
fn write_indent_n(indent: u32, writer: &mut dyn bun_io::Write) -> bun_io::Result<()> {
pub(super) fn write_indent_n(
indent: u32,
writer: &mut dyn bun_io::Write,
) -> bun_io::Result<()> {
let mut total_remain: u32 = indent;
while total_remain > 0 {
let written: u8 = total_remain.min(32) as u8;
Expand Down Expand Up @@ -5785,7 +5786,9 @@ pub(crate) extern "C" fn Bun__ConsoleObject__count(
} + 1;
*counter.value_ptr = current;

let default_indent = this.default_indent;
let writer = this.writer();
let _ = formatter::write_indent_n(u32::from(default_indent), writer);
Comment thread
robobun marked this conversation as resolved.
if Output::enable_ansi_colors_stdout() {
let _ = writeln!(
writer,
Expand Down Expand Up @@ -5856,7 +5859,7 @@ pub(crate) extern "C" fn Bun__ConsoleObject__time(
#[crate::host_call]
pub(crate) extern "C" fn Bun__ConsoleObject__timeEnd(
_console: *mut ConsoleObject,
_global: &JSGlobalObject,
global_this: &JSGlobalObject,
chars: *const u8,
len: usize,
) {
Expand All @@ -5873,6 +5876,10 @@ pub(crate) extern "C" fn Bun__ConsoleObject__timeEnd(
return;
};
let Some(value) = prev else { return };
// SAFETY: top-level JS-thread host call ⇒ exclusive access to the
// set-once `VirtualMachine.console` box.
let default_indent = unsafe { vm_console_mut(global_this) }.default_indent;
let _ = formatter::write_indent_n(u32::from(default_indent), Output::error_writer());
// get the duration in microseconds, then display it in milliseconds
Output::print_elapsed(
(value.read() / bun_core::time::NS_PER_US) as f64 / bun_core::time::US_PER_MS as f64,
Expand Down Expand Up @@ -5905,6 +5912,10 @@ pub(crate) extern "C" fn Bun__ConsoleObject__timeLog(
let Some(Some(value)) = PENDING_TIME_LOGS.with_borrow(|m| m.get(&id).copied()) else {
return;
};
// SAFETY: top-level JS-thread host call ⇒ exclusive access to the
// set-once `VirtualMachine.console` box.
let default_indent = unsafe { vm_console_mut(global) }.default_indent;
let _ = formatter::write_indent_n(u32::from(default_indent), Output::error_writer());
// get the duration in microseconds, then display it in milliseconds
Output::print_elapsed(
(value.read() / bun_core::time::NS_PER_US) as f64 / bun_core::time::US_PER_MS as f64,
Expand Down
57 changes: 57 additions & 0 deletions test/js/web/console/console-log.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,63 @@ NamedError: console.error a named error
`);
});

it.concurrent("console.group indents count/time/assert and the no-args log paths", async () => {
const src = `
console.group("G1");
console.count("cnt");
console.count("cnt");
console.log();
console.info();
console.warn();
console.error();
console.group("G2");
console.count("cnt");
console.assert(false);
console.time("t");
console.timeLog("t");
console.timeEnd("t");
console.groupEnd();
console.count("cnt");
console.groupEnd();
console.count("cnt");
console.log();
console.assert(false);
console.time("u");
console.timeEnd("u");
`;
await using proc = Bun.spawn({
cmd: [bunExe(), "-e", src],
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);

expect(stdout.replaceAll("\r\n", "\n")).toBe(
"G1\n" + //
" cnt: 1\n" +
" cnt: 2\n" +
" \n" +
" \n" +
" G2\n" +
" cnt: 3\n" +
" cnt: 4\n" +
"cnt: 5\n" +
"\n",
);

const stderrLines = stderr.replaceAll("\r\n", "\n").replace(/\n$/, "").split("\n");
expect(stderrLines.length).toBe(7);
expect(stderrLines[0]).toBe(" ");
expect(stderrLines[1]).toBe(" ");
expect(stderrLines[2]).toBe(" Assertion failed");
expect(stderrLines[3]).toMatch(/^ \[[\d.]+[mnµ]?s\] t$/);
expect(stderrLines[4]).toMatch(/^ \[[\d.]+[mnµ]?s\] t$/);
expect(stderrLines[5]).toBe("Assertion failed");
expect(stderrLines[6]).toMatch(/^\[[\d.]+[mnµ]?s\] u$/);
expect(exitCode).toBe(0);
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.

it("console.log with SharedArrayBuffer", () => {
// console.log(x) === Bun.inspect(x) + "\n" written to stdout.
expect(Bun.inspect(new ArrayBuffer(0))).toBe("ArrayBuffer(0) []");
Expand Down
Loading