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
2 changes: 1 addition & 1 deletion src/jsc/ZigStackFrame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ impl<'a> fmt::Display for SourceURLFormatter<'a> {

if self.line_column == LineColumn::Include
&& !source_slice.is_empty()
&& (self.position.line.is_valid() || self.position.column.is_valid())
&& self.position.line.is_valid()
{
if self.enable_color {
f.write_str(Output::pretty_fmt!("<r><d>:", true))?;
Expand Down
36 changes: 34 additions & 2 deletions src/jsc/bindings/ErrorStackFrame.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
#include "root.h"
#include "ErrorStackFrame.h"
#include "JavaScriptCore/CodeBlock.h"
#include "headers-handwritten.h"
#include "JavaScriptCore/BytecodeIndex.h"
#include "JavaScriptCore/StackFrame.h"
#include "wtf/Assertions.h"
#include "wtf/text/OrdinalNumber.h"

namespace Bun {
using namespace JSC;

void applyNegativeSourceStart(CodeBlock* code, int& lineZeroBased, int& columnZeroBased)
{
auto* provider = code->source().provider();
if (!provider)
return;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

// Nested functions share the program's provider, so this is the whole source's start.
TextPosition start = provider->startPosition();
int startLine = start.m_line.zeroBasedInt();
int startColumn = start.m_column.zeroBasedInt();

// JSC applies the start column to the first physical line only, reported as the clamped start line.
if (startColumn < 0 && lineZeroBased == std::max(startLine, 0))
columnZeroBased += startColumn;
if (startLine < 0)
lineZeroBased += startLine;
}

/// Adjust a `ZigStackFramePosition` by a number of bytes. This accounts for when the adjustment
/// crosses line boundaries, and thus requires the source code in order to properly compute
/// the result.
Expand Down Expand Up @@ -95,6 +113,20 @@
break;
}

// After the walk: it works in JSC's coordinates and may end on a different line.
applyNegativeSourceStart(code, pos.line_zero_based, pos.column_zero_based);
return pos;

Check warning on line 118 in src/jsc/bindings/ErrorStackFrame.cpp

View check run for this annotation

Claude / Claude Code Review

Bun's error-printer caret is shifted left by |columnOffset| for first-line vm errors

`applyNegativeSourceStart` shifts `column_zero_based` but leaves `byte_position` at the physical divot, so `populateStackFramePosition`'s `OnlySourceLines` path extracts the *physical* line text while the Rust printer (`VirtualMachine.rs:6243`) indents the caret by the *offset-adjusted* column — the `^` lands `|columnOffset|` chars left of the token in `Bun.inspect` / the uncaught-error excerpt for a first-line vm error with negative `columnOffset`. The arrow header you fixed in `handleException
Comment on lines +116 to +118

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 applyNegativeSourceStart shifts column_zero_based but leaves byte_position at the physical divot, so populateStackFramePosition's OnlySourceLines path extracts the physical line text while the Rust printer (VirtualMachine.rs:6243) indents the caret by the offset-adjusted column — the ^ lands |columnOffset| chars left of the token in Bun.inspect / the uncaught-error excerpt for a first-line vm error with negative columnOffset. The arrow header you fixed in handleException (which recomputes the physical column via std::max(startColumn, 0)) is correct; this is the other renderer, and the positive-offset variant of the same misalignment already existed, so this just makes negative offsets consistently wrong instead of accidentally right.

Extended reasoning...

What the bug is

getAdjustedPositionForBytecode now ends with applyNegativeSourceStart(code, pos.line_zero_based, pos.column_zero_based) (ErrorStackFrame.cpp:117), which for a negative columnOffset on the first physical line sets column_zero_based = physical_col + columnOffset. It does not touch pos.byte_position, which stays at the physical divot into the source text.

populateStackFramePosition (ZigException.cpp) memcpys this position into frame.position, then for PopulateStackTraceFlags::OnlySourceLines extracts the source-line excerpt by scanning backward and forward from location.byte_position — i.e. the physical line text — and stores it in source_lines[0] alongside source_line_numbers[0] = location.line().

Bun's error printer in src/jsc/VirtualMachine.rs then renders that physical text and draws the caret under it:

let col = top.position.column.zero_based();
...
let indent = max_line_number_pad + b" | ".len() as u64 + col.max(0) as u64;
splat_space(writer, indent)?;
pretty_write!(writer, "<red><b>^<r>\n")?;

col is the offset-adjusted column, the text is physical, so the caret lands |columnOffset| characters to the left of the token it should mark.

Concrete example

new (require("vm").Script)("      throw new Error('x')", { filename: "c.js", columnOffset: -3 })
  .runInThisContext({ displayErrors: false });

Left uncaught (or Bun.inspected): JSC reports the divot at, say, physical 0-based column 15. applyNegativeSourceStart makes column_zero_based = 12. byte_position stays 15, so source_lines[0] is the physical string " throw new Error('x')". The printer emits:

1 |       throw new Error('x')
               ^

The ^ sits at column 12 of the physical text — 3 characters to the left of where an un-offset run puts it.

Why this is new for negative offsets, but the class is pre-existing

Before this PR, JSC's SourceCode clamped a negative start column to 1, so getAdjustedPositionForBytecode returned the physical column, and the caret lined up with the physical text. This PR re-adds the negative part, so the negative-offset case regresses from accidentally-correct to shifted-left.

However, the same misalignment already existed for positive columnOffset: JSC adds positive start columns unclamped, so column_zero_based was already physical + columnOffset and the caret was already shifted right. This PR extends a pre-existing cosmetic bug to the negative direction rather than introducing a new class.

Why the tests don't catch it

The PR's "caret stays on the text" test with columnOffset: -3 goes through handleException's arrow header, which was fixed correctly — it recomputes the physical caret column with std::max(start.m_column.zeroBasedInt(), 0) and subtracts only what JSC added. The Bun.inspect() test and the uncaught errors are printed subprocess test (the two paths that reach OnlySourceLines and the Rust printer's caret) only exercise lineOffset, never columnOffset on physical line 1.

Fix

Either have the source-excerpt renderer subtract the same min(startColumn, 0) back out before drawing the caret (mirroring what handleException does for the arrow header), or keep a separate physical column alongside the offset-adjusted one in ZigStackFramePosition for renderers that pair the position with physical text. Given the positive-offset case is already wrong the same way, whichever fix is chosen probably wants to handle both signs together in a follow-up.

}

ZigStackFramePosition getLineColumnForStackFrame(const StackFrame& frame)
{
auto lineColumn = frame.computeLineAndColumn();
ZigStackFramePosition pos {
.line_zero_based = OrdinalNumber::fromOneBasedInt(lineColumn.line).zeroBasedInt(),
.column_zero_based = OrdinalNumber::fromOneBasedInt(lineColumn.column).zeroBasedInt(),
.byte_position = -1,
};
applyNegativeSourceStart(frame.codeBlock(), pos.line_zero_based, pos.column_zero_based);
return pos;
}

Expand Down
13 changes: 13 additions & 0 deletions src/jsc/bindings/ErrorStackFrame.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
#pragma once

#include "root.h"
#include "headers-handwritten.h"
#include "JavaScriptCore/BytecodeIndex.h"

namespace JSC {
class CodeBlock;
class StackFrame;
}

namespace Bun {

ZigStackFramePosition getAdjustedPositionForBytecode(JSC::CodeBlock* code, JSC::BytecodeIndex bc);

/// Re-adds the part of a negative node:vm lineOffset / columnOffset that JSC::SourceCode clamps away; no-op for other sources.
void applyNegativeSourceStart(JSC::CodeBlock* code, int& lineZeroBased, int& columnZeroBased);

/// StackFrame::computeLineAndColumn() of a frame with a code block, with the above applied.
ZigStackFramePosition getLineColumnForStackFrame(const JSC::StackFrame& frame);

} // namespace Bun
30 changes: 18 additions & 12 deletions src/jsc/bindings/FormatStackTraceForJS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#include "BunClientData.h"
#include "CallSite.h"
#include "ErrorStackFrame.h"
#include "ErrorStackTrace.h"
#include "headers-handwritten.h"

Expand Down Expand Up @@ -243,11 +244,11 @@
// file's map once instead of per frame.
WTF::Vector<ZigStackFrame, 8> remappedFrames;
WTF::Vector<WTF::String, 8> sourceURLs;
WTF::Vector<LineColumn, 8> originalLineColumns;
WTF::Vector<ZigStackFramePosition, 8> originalPositions;
remappedFrames.grow(framesCount);
memset(remappedFrames.begin(), 0, sizeof(ZigStackFrame) * framesCount);
sourceURLs.grow(framesCount);
originalLineColumns.grow(framesCount);
originalPositions.grow(framesCount);
bool anyRemap = false;

for (size_t i = 0; i < framesCount; i++) {
Expand All @@ -260,11 +261,11 @@
remappedFrame.position.line_zero_based = -1;
remappedFrame.position.column_zero_based = -1;
remappedFrame.position.byte_position = -1;
originalLineColumns[i] = {};
originalPositions[i] = remappedFrame.position;

if (!frame.hasLineAndColumnInfo()) continue;

originalLineColumns[i] = frame.computeLineAndColumn();
originalPositions[i] = Bun::getLineColumnForStackFrame(frame);

Check warning on line 268 in src/jsc/bindings/FormatStackTraceForJS.cpp

View check run for this annotation

Claude / Claude Code Review

error.stack drops :line:col for line-1 frames when negative columnOffset pushes column ≤ 0

For a line-1 frame whose negative `columnOffset` pushes the zero-based column ≤ 0, `error.stack` now renders the frame as bare `at c.js` (no `:line:col`): the pre-existing gate at line 373 (`displayLine.zeroBasedInt() > 0 || displayColumn.zeroBasedInt() > 0`) evaluates `0 > 0 || ≤0 > 0` → false now that `originalPositions[i]` carries the offset-adjusted column. Node prints `at c.js:1:<negative>`, and this PR renders negative *lines* in error.stack (`nn.js:-6:18` passes the gate on the column sid

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 For a line-1 frame whose negative columnOffset pushes the zero-based column ≤ 0, error.stack now renders the frame as bare at c.js (no :line:col): the pre-existing gate at line 373 (displayLine.zeroBasedInt() > 0 || displayColumn.zeroBasedInt() > 0) evaluates 0 > 0 || ≤0 > 0 → false now that originalPositions[i] carries the offset-adjusted column. Node prints at c.js:1:<negative>, and this PR renders negative lines in error.stack (nn.js:-6:18 passes the gate on the column side), so this one case is inconsistent — either render the negative column like V8 or treat a non-positive column as unset the way handleException does for the header line.

Extended reasoning...

What the bug is

formatStackTrace builds the error.stack string. Its render gate at src/jsc/bindings/FormatStackTraceForJS.cpp:373 decides whether to append :line:col after the source URL:

if (displayLine.zeroBasedInt() > 0 || displayColumn.zeroBasedInt() > 0) {
    sb.append(':');
    sb.append(displayLine.oneBasedInt());
    if (displayColumn.zeroBasedInt() > 0) { ... }
}

That gate is pre-existing, but this PR changes what feeds it. Line 268 now sets originalPositions[i] = Bun::getLineColumnForStackFrame(frame), which folds a negative columnOffset into the column via applyNegativeSourceStart; lines 319–320 copy that into displayLine/displayColumn. Before this PR the gate was fed frame.computeLineAndColumn() directly, whose column is always ≥ 1 one-based because JSC's SourceCode constructor clamps the negative start away.

The specific code path

For a frame on the first physical line (JSC reports it as one-based line 1, i.e. displayLine.zeroBasedInt() == 0) with a columnOffset negative enough to push the zero-based column to ≤ 0, both sides of the OR are now false — 0 > 0 is false, ≤0 > 0 is false — so no :line:col is appended and the frame renders as bare at c.js. ZigStackFramePosition::column() returns OrdinalNumber::fromZeroBasedInt(column_zero_based), so the negative flows through as a negative zeroBasedInt().

Step-by-step example

try {
  new (require('vm').Script)("throw new Error('x')", { filename: 'c.js', columnOffset: -100 }).runInThisContext();
} catch (e) { console.log(e.stack); }
  1. JSC attributes the throw to one-based column ~16 on physical line 1 (zero-based line 0, column ~15).
  2. applyNegativeSourceStart: startColumn = -100 < 0 and lineZeroBased == max(-100, 0) == 0, so columnZeroBased += -100 → ~−85. startLine is 0, so the line stays 0.
  3. getLineColumnForStackFrame returns { line_zero_based: 0, column_zero_based: -85 }, stored into originalPositions[i] at line 268.
  4. Lines 319–320: displayLine = originalPositions[i].line() (zero-based 0), displayColumn = originalPositions[i].column() (zero-based −85).
  5. Gate at line 373: 0 > 0 || -85 > 0 → false. Nothing appended after c.js.
  6. The vm frame in err.stack reads at c.js.

Node prints at c.js:1:-93; Bun before this PR printed at c.js:1:16 (wrong column, but the line was there).

Why the PR's tests miss it

Every negative-columnOffset test in the PR uses an offset small enough (-2 or -3 against ≥ 4 columns of leading whitespace) that the adjusted zero-based column stays > 0, so the gate still passes on the column side. The one test that produces a below-zero position ("header lines at or below zero") does so via a large negative lineOffset; those frames have positive columns and pass the gate on the column side, which is why nn.js:-6:18 renders correctly while this case does not.

Impact and why it's a nit

The PR description says "error.stack and CallSite#toJSON carry them as V8 would (at f (nn.js:-6:18))", and the negative-line case does exactly that; only this negative-column-on-line-1 corner regresses from "wrong column shown" to "no position shown", moving further from Node than before. It only fires when |columnOffset| exceeds the physical column of the frame on line 1 — the realistic wrapper-prefix use case never hits it (meaningful positions are past the prefix), and the arrow header still shows c.js:1 + source line + caret via handleException, so the user isn't blind. The pre-PR output was already wrong here, just differently.

Fix

Either change the outer gate to test displayLine.oneBasedInt() > 0 (so line 1 always renders and negative lines still don't rely on the column side alone; negative one-based columns then print like V8 does), or clamp a non-positive one-based column to "unset" before the gate (mirroring how handleException treats a non-positive line for the caret and how toUnsignedOneBased handles error.column). Both are one-line edits at the gate; the first matches Node's output, the second matches the rest of this PR's "non-positive → no position" convention for Bun-side renderers.


JSC::JSGlobalObject* globalObjectForFrame = lexicalGlobalObject;
if (auto* callee = frame.callee()) {
Expand All @@ -280,8 +281,7 @@
if (isDefinitelyNotRunninginNodeVMGlobalObject || isDefaultGlobalObjectInAFinalizer) {
// https://github.com/oven-sh/bun/issues/3595
if (!sourceURLs[i].isEmpty()) {
remappedFrame.position.line_zero_based = OrdinalNumber::fromOneBasedInt(originalLineColumns[i].line).zeroBasedInt();
remappedFrame.position.column_zero_based = OrdinalNumber::fromOneBasedInt(originalLineColumns[i].column).zeroBasedInt();
remappedFrame.position = originalPositions[i];
remappedFrame.source_url = Bun::toStringRef(sourceURLs[i]);
anyRemap = true;
}
Expand Down Expand Up @@ -316,8 +316,8 @@
WTF::String sourceURLForFrame = sourceURLs[i];

if (frame.hasLineAndColumnInfo()) {
originalLine = OrdinalNumber::fromOneBasedInt(originalLineColumns[i].line);
originalColumn = OrdinalNumber::fromOneBasedInt(originalLineColumns[i].column);
originalLine = originalPositions[i].line();
originalColumn = originalPositions[i].column();
displayLine = originalLine;
displayColumn = originalColumn;

Expand Down Expand Up @@ -575,6 +575,12 @@
return computeErrorInfoToJSValueWithoutSkipping(vm, stackTrace, line, column, sourceURL, errorInstance, bunErrorData);
}

// ErrorInstance keeps error.line / error.column unsigned; a node:vm offset can put a position at or below 0, which reads as unset (0) there.
static unsigned toUnsignedOneBased(OrdinalNumber ordinal)
{
return static_cast<unsigned>(std::max(ordinal.oneBasedInt(), 0));
}

WTF::String computeErrorInfoWrapperToString(JSC::VM& vm, Vector<StackFrame>& stackTrace, unsigned int& line_in, unsigned int& column_in, String& sourceURL, void* bunErrorData)
{
UNUSED_PARAM(bunErrorData);
Expand Down Expand Up @@ -607,8 +613,8 @@
result = WTF::emptyString();
}

line_in = line.oneBasedInt();
column_in = column.oneBasedInt();
line_in = toUnsignedOneBased(line);
column_in = toUnsignedOneBased(column);

return result;
}
Expand Down Expand Up @@ -644,8 +650,8 @@

JSValue result = computeErrorInfoToJSValue(vm, stackTrace, line, column, sourceURL, errorInstance, bunErrorData);

line_in = line.oneBasedInt();
column_in = column.oneBasedInt();
line_in = toUnsignedOneBased(line);
column_in = toUnsignedOneBased(column);

// materializeErrorInfoIfNeeded putDirect()s this unconditionally; an empty JSValue
// in property storage crashes the next read. https://github.com/oven-sh/bun/issues/34095
Expand Down
26 changes: 13 additions & 13 deletions src/jsc/bindings/NodeVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,15 +187,11 @@ JSC::JSFunction* constructAnonymousFunction(JSC::JSGlobalObject* globalObject, c
String code = stringifyAnonymousFunction(globalObject, args, throwScope, &startOffset);
EXCEPTION_ASSERT(!!throwScope.exception() == code.isNull());

// The user's body starts on line 2 of the wrapped program (after the
// "(function () {\n" prefix). Shift the provider's start position up one
// line so reported positions line up with the body the way V8's
// CompileFunction does: body line 1 reports as lineOffset+1. JSC clamps
// non-positive provider start positions to zero, so once the input is
// already <= 0 there is nothing to gain by going further negative; clamp
// there to keep the value bounded for downstream arithmetic.
int lineZeroBased = position.m_line.zeroBasedInt();
TextPosition wrappedPosition(OrdinalNumber::fromZeroBasedInt(lineZeroBased > 0 ? lineZeroBased - 1 : lineZeroBased), position.m_column);
// The body is line 2 of the wrapped program; a negative start is honored by Bun::applyNegativeSourceStart.
int startLine = position.m_line.zeroBasedInt();
if (startLine != std::numeric_limits<int>::min())
startLine--;
TextPosition wrappedPosition(OrdinalNumber::fromZeroBasedInt(startLine), position.m_column);

SourceCode sourceCode(
JSC::StringSourceProvider::create(code, sourceOrigin, WTF::move(options.filename), sourceTaintOrigin, wrappedPosition, SourceProviderSourceType::Program),
Expand Down Expand Up @@ -570,23 +566,27 @@ bool handleException(JSGlobalObject* globalObject, VM& vm, NakedPtr<JSC::Excepti
// Node decorates vm script errors with the offending source line and a
// caret marker (AppendExceptionLine):
// <url>:<line>\n<source line>\n<caret>\n\n<stack>
int reportedLine = static_cast<int>(line_and_column.line);
String sourceLineText;
unsigned caretColumn = 0;
if (JSC::CodeBlock* codeBlock = stack_frame.codeBlock()) {
if (JSC::SourceProvider* provider = codeBlock->source().provider()) {
int64_t startLineZeroBased = provider->startPosition().m_line.zeroBasedInt();
int64_t physicalLine = static_cast<int64_t>(line_and_column.line) - startLineZeroBased;
// line_and_column was counted from the start clamped to 1:1 (see Bun::applyNegativeSourceStart).
TextPosition start = provider->startPosition();
int startLineZeroBased = start.m_line.zeroBasedInt();
int64_t physicalLine = static_cast<int64_t>(line_and_column.line) - std::max(startLineZeroBased, 0);
reportedLine = static_cast<int>(physicalLine + startLineZeroBased);
sourceLineText = nthSourceLineForArrowHeader(provider->source(), physicalLine);
if (!sourceLineText.isNull()) {
caretColumn = line_and_column.column;
unsigned startColumnZeroBased = static_cast<unsigned>(provider->startPosition().m_column.zeroBasedInt());
unsigned startColumnZeroBased = static_cast<unsigned>(std::max(start.m_column.zeroBasedInt(), 0));
if (physicalLine == 1 && caretColumn > startColumnZeroBased)
caretColumn -= startColumnZeroBased;
}
}
}

writeArrowHeaderStack(vm, errorInstance, source_url, static_cast<int>(line_and_column.line), sourceLineText, caretColumn, stack);
writeArrowHeaderStack(vm, errorInstance, source_url, reportedLine, sourceLineText, caretColumn, stack);

JSC::throwException(globalObject, throwScope, exception.get());
return true;
Expand Down
17 changes: 6 additions & 11 deletions src/jsc/bindings/ZigException.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,7 @@ static void populateStackFramePosition(const JSC::StackFrame& stackFrame, BunStr
return;

if (!stackFrame.hasBytecodeIndex()) {
if (stackFrame.hasLineAndColumnInfo()) {
auto lineColumn = stackFrame.computeLineAndColumn();
position.line_zero_based = OrdinalNumber::fromOneBasedInt(lineColumn.line).zeroBasedInt();
position.column_zero_based = OrdinalNumber::fromOneBasedInt(lineColumn.column).zeroBasedInt();
}

position.byte_position = -1;
position = Bun::getLineColumnForStackFrame(stackFrame);
return;
}

Expand Down Expand Up @@ -333,7 +327,8 @@ class V8StackTraceIterator {
auto segment1 = StringView_slice(lineInner, marker1, marker2);
auto segment2 = StringView_slice(lineInner, marker2 + 1, marker3);

if (auto int1 = WTF::parseIntegerAllowingTrailingJunk<unsigned int>(segment2)) {
// Signed: a node:vm lineOffset can push positions to 0 or below.
if (auto int1 = WTF::parseIntegerAllowingTrailingJunk<int>(segment2)) {
frame.sourceURL = segment1;
frame.lineNumber = WTF::OrdinalNumber::fromOneBasedInt(int1.value());
} else {
Expand Down Expand Up @@ -363,8 +358,8 @@ class V8StackTraceIterator {
auto segment2 = StringView_slice(lineInner, marker2 + 1, marker3);
auto segment3 = StringView_slice(lineInner, marker3 + 1, marker4);

if (auto int1 = WTF::parseIntegerAllowingTrailingJunk<unsigned int>(segment2)) {
if (auto int2 = WTF::parseIntegerAllowingTrailingJunk<unsigned int>(segment3)) {
if (auto int1 = WTF::parseIntegerAllowingTrailingJunk<int>(segment2)) {
if (auto int2 = WTF::parseIntegerAllowingTrailingJunk<int>(segment3)) {
frame.sourceURL = segment1;
frame.lineNumber = WTF::OrdinalNumber::fromOneBasedInt(int1.value());
frame.columnNumber = WTF::OrdinalNumber::fromOneBasedInt(int2.value());
Expand All @@ -373,7 +368,7 @@ class V8StackTraceIterator {
frame.lineNumber = WTF::OrdinalNumber::fromOneBasedInt(int1.value());
}
} else {
if (auto int2 = WTF::parseIntegerAllowingTrailingJunk<unsigned int>(segment3)) {
if (auto int2 = WTF::parseIntegerAllowingTrailingJunk<int>(segment3)) {
frame.sourceURL = StringView_slice(lineInner, marker1, marker3);
frame.lineNumber = WTF::OrdinalNumber::fromOneBasedInt(int2.value());
} else {
Expand Down
11 changes: 5 additions & 6 deletions test/js/node/test/parallel/test-vm-basic.js
Original file line number Diff line number Diff line change
Expand Up @@ -278,16 +278,15 @@ const vm = require('vm');
Error.stackTraceLimit = 1;

// Bun's compileFunction stack frames differ from Node's: JSC attributes
// the throw to a different column (and columnOffset is not applied), the
// wrapper costs one line when lineOffset is 0, and the anonymous source is
// labeled differently.
// the throw to a different column (and columnOffset is not applied), and
// the anonymous source is labeled differently.
assert.throws(() => {
vm.compileFunction('throw new Error("Sample Error")')();
}, {
message: 'Sample Error',
stack: typeof Bun === 'undefined'
? 'Error: Sample Error\n at <anonymous>:1:7'
: 'Error: Sample Error\n at <anonymous> (file:///:2:16)'
: 'Error: Sample Error\n at <anonymous> (file:///:1:16)'
});

assert.throws(() => {
Expand All @@ -313,7 +312,7 @@ const vm = require('vm');
message: 'Sample Error',
stack: typeof Bun === 'undefined'
? 'Error: Sample Error\n at <anonymous>:1:10'
: 'Error: Sample Error\n at <anonymous> (file:///:2:16)'
: 'Error: Sample Error\n at <anonymous> (file:///:1:16)'
});

assert.strictEqual(
Expand All @@ -337,7 +336,7 @@ const vm = require('vm');
// Bun's compileFunction stack frames differ from Node's (see above).
stack: typeof Bun === 'undefined'
? 'ReferenceError: varInContext is not defined\n at <anonymous>:1:1'
: 'ReferenceError: varInContext is not defined\n at <anonymous> (file:///:2:20)'
: 'ReferenceError: varInContext is not defined\n at <anonymous> (file:///:1:20)'
});

assert.notDeepStrictEqual(
Expand Down
Loading
Loading