Compare commits

...

7 Commits

Author SHA1 Message Date
chloe caruso
422a09c7a1 add documentation 2025-01-06 15:20:38 -08:00
chloe caruso
e5352356f0 Merge remote-tracking branch 'origin/main' into dave/fix-leak-assertion 2025-01-06 15:15:56 -08:00
paperdave
adfcbb0739 bun run clang-format 2024-11-14 23:59:20 +00:00
dave caruso
25525ec744 Merge branch 'main' into dave/fix-leak-assertion 2024-11-14 15:58:08 -08:00
dave caruso
5578a4ca40 fix test crash 2024-08-06 13:48:15 -07:00
dave caruso
f0ff0f267d fix an assertion by removing it 2024-08-05 20:37:33 -07:00
dave caruso
19bb3fc103 leak 2024-08-05 20:17:57 -07:00
5 changed files with 111 additions and 65 deletions

View File

@@ -4228,7 +4228,7 @@ static void populateStackFrameMetadata(JSC::VM& vm, const JSC::StackFrame* stack
static void populateStackFramePosition(const JSC::StackFrame* stackFrame, BunString* source_lines,
OrdinalNumber* source_line_numbers, uint8_t source_lines_count,
ZigStackFramePosition* position, JSC::SourceProvider** referenced_source_provider)
ZigStackFramePosition* position)
{
auto code = stackFrame->codeBlock();
if (!code)
@@ -4256,78 +4256,102 @@ static void populateStackFramePosition(const JSC::StackFrame* stackFrame, BunStr
auto location = Bun::getAdjustedPositionForBytecode(code, stackFrame->bytecodeIndex());
*position = location;
}
if (source_lines_count > 1 && source_lines != nullptr && sourceString.is8Bit()) {
// Search for the beginning of the line
unsigned int lineStart = location.byte_position;
while (lineStart > 0 && sourceString[lineStart] != '\n') {
lineStart--;
}
// As `source_lines` is populated with referenced data from a source provider, that source provider is returned.
JSC::SourceProvider* Bun__getSourceCodeViewFromErrorInstance(
JSValue exceptionValue,
BunString* source_lines,
int32_t* source_line_numbers,
uint8_t source_lines_count)
{
JSC::ErrorInstance* error = nullptr;
// Search for the end of the line
unsigned int lineEnd = location.byte_position;
unsigned int maxSearch = sourceString.length();
while (lineEnd < maxSearch && sourceString[lineEnd] != '\n') {
lineEnd++;
}
const unsigned char* bytes = sourceString.span8().data();
// Most of the time, when you look at a stack trace, you want a couple lines above.
// It is key to not clone this data because source code strings are large.
// Usage of toStringView (non-owning) is safe as we ref the provider.
provider->ref();
if (*referenced_source_provider != nullptr) {
(*referenced_source_provider)->deref();
}
*referenced_source_provider = provider;
source_lines[0] = Bun::toStringView(sourceString.substring(lineStart, lineEnd - lineStart));
source_line_numbers[0] = location.line();
if (lineStart > 0) {
auto byte_offset_in_source_string = lineStart - 1;
uint8_t source_line_i = 1;
auto remaining_lines_to_grab = source_lines_count - 1;
{
// This should probably be code points instead of newlines
while (byte_offset_in_source_string > 0 && bytes[byte_offset_in_source_string] != '\n') {
byte_offset_in_source_string--;
}
byte_offset_in_source_string -= byte_offset_in_source_string > 0;
}
while (byte_offset_in_source_string > 0 && remaining_lines_to_grab > 0) {
unsigned int end_of_line_offset = byte_offset_in_source_string;
// This should probably be code points instead of newlines
while (byte_offset_in_source_string > 0 && bytes[byte_offset_in_source_string] != '\n') {
byte_offset_in_source_string--;
}
// We are at the beginning of the line
source_lines[source_line_i] = Bun::toStringView(sourceString.substring(byte_offset_in_source_string, end_of_line_offset - byte_offset_in_source_string + 1));
source_line_numbers[source_line_i] = location.line().fromZeroBasedInt(location.line().zeroBasedInt() - source_line_i);
source_line_i++;
remaining_lines_to_grab--;
byte_offset_in_source_string -= byte_offset_in_source_string > 0;
}
if (JSC::ErrorInstance* e = JSC::jsDynamicCast<JSC::ErrorInstance*>(exceptionValue)) {
error = e;
} else if (JSC::Exception* jscException = JSC::jsDynamicCast<JSC::Exception*>(exceptionValue)) {
if (JSC::ErrorInstance* e = JSC::jsDynamicCast<JSC::ErrorInstance*>(jscException->value())) {
error = e;
}
}
if (!error) return nullptr;
auto stack = error->stackTrace();
if (stack->isEmpty()) return nullptr;
JSC::StackFrame& frame = stack->first();
JSC::SourceProvider* provider = frame.codeBlock()->source().provider();
WTF::StringView view = provider->source();
;
if (!view.is8Bit()) return nullptr;
if (source_lines_count <= 1 || source_lines == nullptr) return nullptr;
auto location = Bun::getAdjustedPositionForBytecode(frame.codeBlock(), frame.bytecodeIndex());
// It is key to not clone this data because source code strings are large.
// Usage of toStringView (non-owning) is safe as we ref the provider here.
provider->ref();
// Search for the beginning of the line
unsigned int lineStart = location.byte_position;
while (lineStart > 0 && view[lineStart] != '\n') {
lineStart--;
}
// Search for the end of the line
unsigned int lineEnd = location.byte_position;
unsigned int maxSearch = view.length();
while (lineEnd < maxSearch && view[lineEnd] != '\n') {
lineEnd++;
}
const unsigned char* bytes = view.span8().data();
// Most of the time, when you look at a stack trace, you want a couple lines above.
source_lines[0] = Bun::toStringView(view.substring(lineStart, lineEnd - lineStart));
int line = location.line().oneBasedInt();
source_line_numbers[0] = line;
if (lineStart > 0) {
auto byte_offset_in_source_string = lineStart - 1;
uint8_t source_line_i = 1;
auto remaining_lines_to_grab = source_lines_count - 1;
while (byte_offset_in_source_string > 0 && remaining_lines_to_grab > 0) {
--line;
unsigned int end_of_line_offset = byte_offset_in_source_string;
// This should probably be code points instead of newlines
while (byte_offset_in_source_string > 0 && bytes[byte_offset_in_source_string] != '\n') {
byte_offset_in_source_string--;
}
// We are at the beginning of the line
source_lines[source_line_i] = Bun::toStringView(view.substring(byte_offset_in_source_string, end_of_line_offset - byte_offset_in_source_string + 1));
source_line_numbers[source_line_i] = line;
source_line_i++;
remaining_lines_to_grab--;
byte_offset_in_source_string -= byte_offset_in_source_string > 0;
}
}
return provider;
}
static void populateStackFrame(JSC::VM& vm, ZigStackTrace* trace, const JSC::StackFrame* stackFrame,
ZigStackFrame* frame, bool is_top, JSC::SourceProvider** referenced_source_provider)
ZigStackFrame* frame, bool is_top)
{
populateStackFrameMetadata(vm, stackFrame, frame);
populateStackFramePosition(stackFrame, is_top ? trace->source_lines_ptr : nullptr,
is_top ? trace->source_lines_numbers : nullptr,
is_top ? trace->source_lines_to_collect : 0, &frame->position, referenced_source_provider);
is_top ? trace->source_lines_to_collect : 0, &frame->position);
}
class V8StackTraceIterator {
@@ -4513,7 +4537,7 @@ static void populateStackTrace(JSC::VM& vm, const WTF::Vector<JSC::StackFrame>&
break;
ZigStackFrame* frame = &trace->frames_ptr[frame_i];
populateStackFrame(vm, trace, &frames[stack_frame_i], frame, frame_i == 0, &trace->referenced_source_provider);
populateStackFrame(vm, trace, &frames[stack_frame_i], frame, frame_i == 0);
stack_frame_i++;
frame_i++;
}

View File

@@ -228,6 +228,7 @@ pub const ResolvedSource = extern struct {
pub const SourceProvider = opaque {
extern fn JSC__SourceProvider__deref(*SourceProvider) void;
pub fn deref(provider: *SourceProvider) void {
JSC__SourceProvider__deref(provider);
}

View File

@@ -3573,6 +3573,11 @@ pub const VirtualMachine = struct {
}
}
// As `lines` is populated with referenced data from a source provider, that
// source provider is returned. Call `.deref()` on the returned provider to
// release memory. The `lines` should not have `deref` called on.
extern fn Bun__getSourceCodeViewFromErrorInstance(val: JSValue, lines: [*]bun.String, numbers: [*]i32, len: u8) ?*JSC.SourceProvider;
pub fn remapZigException(
this: *VirtualMachine,
exception: *ZigException,
@@ -3599,6 +3604,7 @@ pub const VirtualMachine = struct {
});
var frames: []JSC.ZigStackFrame = exception.stack.frames_ptr[0..exception.stack.frames_len];
if (this.hide_bun_stackframes) {
var start_index: ?usize = null;
for (frames, 0..) |frame, i| {
@@ -3748,6 +3754,15 @@ pub const VirtualMachine = struct {
exception.stack.source_lines_len = @as(u8, @truncate(lines.len));
}
} else {
if (Bun__getSourceCodeViewFromErrorInstance(
error_instance,
exception.stack.source_lines_ptr,
exception.stack.source_lines_numbers,
exception.stack.source_lines_len,
)) |source| {
exception.stack.referenced_source_provider = source;
}
}
if (frames.len > 1) {

View File

@@ -171,6 +171,7 @@ fn dumpSourceStringFailiable(vm: *VirtualMachine, specifier: string, written: []
specifier,
std.math.maxInt(u64),
) catch "";
defer bun.default_allocator.free(source_file);
var bufw = std.io.bufferedWriter(file.writer());
const w = bufw.writer();

View File

@@ -926,13 +926,18 @@ function runTest({
} = {}): string {
cwd ??= createTest(input);
try {
const { stderr } = spawnSync({
const { stderr, exitCode, success, signalCode } = spawnSync({
cwd,
cmd: [bunExe(), "test", ...args],
env: { ...bunEnv, ...env },
stderr: "pipe",
stdout: "ignore",
stdout: "inherit",
});
if (!success) {
if (signalCode) {
console.log(new Error(`bun test failed with signal ${signalCode}`));
}
}
return stderr.toString();
} finally {
rmSync(cwd, { recursive: true });