Compare commits

...

2 Commits

Author SHA1 Message Date
Meghan Denny
f0e8a9f216 Merge branch 'main' into nektro-patch-31345 2025-09-26 18:21:24 -08:00
Meghan Denny
8c2de1ff58 lsan: fix sql.postgres suppressions 2025-09-24 01:39:17 -07:00
15 changed files with 45 additions and 69 deletions

View File

@@ -830,13 +830,11 @@ pub fn onExit(this: *VirtualMachine) void {
}
}
extern fn Zig__GlobalObject__destructOnExit(*JSGlobalObject) void;
pub fn globalExit(this: *VirtualMachine) noreturn {
bun.assert(this.isShuttingDown());
if (this.shouldDestructMainThreadOnExit()) {
if (this.eventLoop().forever_timer) |t| t.deinit(true);
Zig__GlobalObject__destructOnExit(this.global);
bun.cpp.Zig__GlobalObject__destructOnExit(this.global);
this.transpiler.deinit();
this.gc_controller.deinit();
this.deinit();

View File

@@ -7,7 +7,6 @@ pub const JSObject = opaque {
extern fn JSC__JSObject__getIndex(this: JSValue, globalThis: *JSGlobalObject, i: u32) JSValue;
extern fn Bun__JSObject__getCodePropertyVMInquiry(global: *JSGlobalObject, obj: *JSObject) JSValue;
extern fn JSC__createStructure(global: *jsc.JSGlobalObject, owner: *jsc.JSCell, length: u32, names: [*]ExternColumnIdentifier) jsc.JSValue;
extern fn JSC__JSObject__create(global_object: *JSGlobalObject, length: usize, ctx: *anyopaque, initializer: InitializeCallback) JSValue;
pub fn toJS(obj: *JSObject) JSValue {
@@ -117,7 +116,7 @@ pub const JSObject = opaque {
pub fn createStructure(global: *JSGlobalObject, owner: jsc.JSValue, length: u32, names: [*]ExternColumnIdentifier) JSValue {
jsc.markBinding(@src());
return JSC__createStructure(global, owner.asCell(), length, names);
return bun.cpp.JSC__createStructure(global, owner.asCell(), names, length);
}
const InitializeCallback = *const fn (ctx: *anyopaque, obj: *JSObject, global: *JSGlobalObject) callconv(.C) void;

View File

@@ -60,7 +60,7 @@ typedef struct TypedArrayDataCell {
typedef union DataCellValue {
uint8_t null_value;
WTF::StringImpl* string;
BunString string;
double number;
int32_t integer;
int64_t bigint;
@@ -69,7 +69,7 @@ typedef union DataCellValue {
double date;
double date_with_time_zone;
size_t bytea[2];
WTF::StringImpl* json;
BunString json;
DataCellArray array;
TypedArrayDataCell typed_array;
DataCellRaw raw;
@@ -146,8 +146,8 @@ static JSC::JSValue toJS(JSC::VM& vm, JSC::JSGlobalObject* globalObject, DataCel
return uint8Array;
}
case DataCellTag::String: {
if (cell.value.string) {
return jsString(vm, WTF::String(cell.value.string));
if (cell.value.string.tag != BunStringTag::Dead) {
return jsString(vm, cell.value.string.transferToWTFString());
}
return jsEmptyString(vm);
}
@@ -186,8 +186,8 @@ static JSC::JSValue toJS(JSC::VM& vm, JSC::JSGlobalObject* globalObject, DataCel
return uint8Array;
}
case DataCellTag::Json: {
if (cell.value.json) {
auto str = WTF::String(cell.value.json);
if (cell.value.json.tag != BunStringTag::Dead) {
auto str = cell.value.json.transferToWTFString();
JSC::JSValue json = JSC::JSONParse(globalObject, str);
RETURN_IF_EXCEPTION(scope, {});
return json;
@@ -430,7 +430,7 @@ extern "C" EncodedJSValue JSC__constructObjectFromDataCell(
return JSValue::encode(toJS(array, structure, cells, count, globalObject, Bun::BunStructureFlags(flags), BunResultMode(result_mode), namesPtr, namesCount));
}
extern "C" EncodedJSValue JSC__createStructure(JSC::JSGlobalObject* globalObject, JSC::JSCell* owner, uint32_t capacity, ExternColumnIdentifier* namesPtr)
extern "C" [[ZIG_EXPORT(nothrow)]] EncodedJSValue JSC__createStructure(JSC::JSGlobalObject* globalObject, JSC::JSCell* owner, Bun::ExternColumnIdentifier* namesPtr, size_t capacity)
{
auto& vm = JSC::getVM(globalObject);
@@ -441,7 +441,7 @@ extern "C" EncodedJSValue JSC__createStructure(JSC::JSGlobalObject* globalObject
for (uint32_t i = 0; i < capacity; i++) {
ExternColumnIdentifier& name = names[i];
if (name.isNamedColumn()) {
propertyNames.add(Identifier::fromString(vm, name.name.toWTFString()));
propertyNames.add(Identifier::fromString(vm, name.name.transferToWTFString()));
}
nonDuplicateCount += !name.isDuplicateColumn();
if (nonDuplicateCount == JSFinalObject::maxInlineCapacity) {

View File

@@ -4640,7 +4640,7 @@ bool GlobalObject::untrackFFIFunction(JSC::JSFunction* function)
});
}
extern "C" void Zig__GlobalObject__destructOnExit(Zig::GlobalObject* globalObject)
extern "C" [[ZIG_EXPORT(nothrow)]] void Zig__GlobalObject__destructOnExit(Zig::GlobalObject* globalObject)
{
auto& vm = JSC::getVM(globalObject);
if (vm.entryScope) {

View File

@@ -55,6 +55,8 @@ export const sharedTypes: Record<string, string> = {
"JSC::CustomGetterSetter": "bun.jsc.CustomGetterSetter",
"JSC::SourceProvider": "bun.jsc.SourceProvider",
"JSC::CallFrame": "bun.jsc.CallFrame",
"JSC::JSCell": "bun.jsc.JSCell",
"Bun::ExternColumnIdentifier": "bun.jsc.JSObject.ExternColumnIdentifier",
"JSC::JSObject": "bun.jsc.JSObject",
"JSC::JSString": "bun.jsc.JSString",
};

View File

@@ -61,7 +61,7 @@ pub fn decodeBinaryValue(globalObject: *jsc.JSGlobalObject, field_type: types.Fi
}
var buffer: [22]u8 = undefined;
const slice = std.fmt.bufPrint(&buffer, "{d}", .{val}) catch unreachable;
return SQLDataCell{ .tag = .string, .value = .{ .string = if (slice.len > 0) bun.String.cloneUTF8(slice).value.WTFStringImpl else null }, .free_value = 1 };
return SQLDataCell{ .tag = .string, .value = .{ .string = if (slice.len > 0) bun.String.cloneUTF8(slice) else .dead }, .free_value = 1 };
}
const val = try reader.int(i64);
if (val >= std.math.minInt(i32) and val <= std.math.maxInt(i32)) {
@@ -72,7 +72,7 @@ pub fn decodeBinaryValue(globalObject: *jsc.JSGlobalObject, field_type: types.Fi
}
var buffer: [22]u8 = undefined;
const slice = std.fmt.bufPrint(&buffer, "{d}", .{val}) catch unreachable;
return SQLDataCell{ .tag = .string, .value = .{ .string = if (slice.len > 0) bun.String.cloneUTF8(slice).value.WTFStringImpl else null }, .free_value = 1 };
return SQLDataCell{ .tag = .string, .value = .{ .string = if (slice.len > 0) bun.String.cloneUTF8(slice) else .dead }, .free_value = 1 };
},
.MYSQL_TYPE_FLOAT => {
if (raw) {
@@ -135,7 +135,7 @@ pub fn decodeBinaryValue(globalObject: *jsc.JSGlobalObject, field_type: types.Fi
defer string_data.deinit();
const slice = string_data.slice();
return SQLDataCell{ .tag = .string, .value = .{ .string = if (slice.len > 0) bun.String.cloneUTF8(slice).value.WTFStringImpl else null }, .free_value = 1 };
return SQLDataCell{ .tag = .string, .value = .{ .string = if (slice.len > 0) bun.String.cloneUTF8(slice) else .dead }, .free_value = 1 };
},
.MYSQL_TYPE_JSON => {
@@ -147,7 +147,7 @@ pub fn decodeBinaryValue(globalObject: *jsc.JSGlobalObject, field_type: types.Fi
var string_data = try reader.encodeLenString();
defer string_data.deinit();
const slice = string_data.slice();
return SQLDataCell{ .tag = .json, .value = .{ .json = if (slice.len > 0) bun.String.cloneUTF8(slice).value.WTFStringImpl else null }, .free_value = 1 };
return SQLDataCell{ .tag = .json, .value = .{ .json = if (slice.len > 0) bun.String.cloneUTF8(slice) else .dead }, .free_value = 1 };
},
.MYSQL_TYPE_BIT => {
// BIT(1) is a special case, it's a boolean

View File

@@ -106,11 +106,11 @@ pub const Row = struct {
}
const slice = value.slice();
cell.* = SQLDataCell{ .tag = .string, .value = .{ .string = if (slice.len > 0) bun.String.cloneUTF8(slice).value.WTFStringImpl else null }, .free_value = 1 };
cell.* = SQLDataCell{ .tag = .string, .value = .{ .string = if (slice.len > 0) bun.String.cloneUTF8(slice) else .dead }, .free_value = 1 };
},
.MYSQL_TYPE_JSON => {
const slice = value.slice();
cell.* = SQLDataCell{ .tag = .json, .value = .{ .json = if (slice.len > 0) bun.String.cloneUTF8(slice).value.WTFStringImpl else null }, .free_value = 1 };
cell.* = SQLDataCell{ .tag = .json, .value = .{ .json = if (slice.len > 0) bun.String.cloneUTF8(slice) else .dead }, .free_value = 1 };
},
.MYSQL_TYPE_DATE, .MYSQL_TYPE_TIME, .MYSQL_TYPE_DATETIME, .MYSQL_TYPE_TIMESTAMP => {
@@ -135,7 +135,7 @@ pub const Row = struct {
},
else => {
const slice = value.slice();
cell.* = SQLDataCell{ .tag = .string, .value = .{ .string = if (slice.len > 0) bun.String.cloneUTF8(slice).value.WTFStringImpl else null }, .free_value = 1 };
cell.* = SQLDataCell{ .tag = .string, .value = .{ .string = if (slice.len > 0) bun.String.cloneUTF8(slice) else .dead }, .free_value = 1 };
},
};
}

View File

@@ -155,7 +155,7 @@ fn parseArray(bytes: []const u8, bigint: bool, comptime arrayType: types.Tag, gl
const buffer = if (needs_dynamic_buffer) try bun.default_allocator.alloc(u8, str_bytes.len) else stack_buffer[0..];
defer if (needs_dynamic_buffer) bun.default_allocator.free(buffer);
const unescaped = unescapePostgresString(str_bytes, buffer) catch return error.InvalidByteSequence;
try array.append(bun.default_allocator, SQLDataCell{ .tag = .json, .value = .{ .json = if (unescaped.len > 0) String.cloneUTF8(unescaped).value.WTFStringImpl else null }, .free_value = 1 });
try array.append(bun.default_allocator, SQLDataCell{ .tag = .json, .value = .{ .json = if (unescaped.len > 0) String.cloneUTF8(unescaped) else .dead }, .free_value = 1 });
slice = trySlice(slice, current_idx + 1);
continue;
},
@@ -164,7 +164,7 @@ fn parseArray(bytes: []const u8, bigint: bool, comptime arrayType: types.Tag, gl
const str_bytes = slice[1..current_idx];
if (str_bytes.len == 0) {
// empty string
try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = null }, .free_value = 1 });
try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = .dead }, .free_value = 1 });
slice = trySlice(slice, current_idx + 1);
continue;
}
@@ -172,7 +172,7 @@ fn parseArray(bytes: []const u8, bigint: bool, comptime arrayType: types.Tag, gl
const buffer = if (needs_dynamic_buffer) try bun.default_allocator.alloc(u8, str_bytes.len) else stack_buffer[0..];
defer if (needs_dynamic_buffer) bun.default_allocator.free(buffer);
const string_bytes = unescapePostgresString(str_bytes, buffer) catch return error.InvalidByteSequence;
try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = if (string_bytes.len > 0) String.cloneUTF8(string_bytes).value.WTFStringImpl else null }, .free_value = 1 });
try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = if (string_bytes.len > 0) String.cloneUTF8(string_bytes) else .dead }, .free_value = 1 });
slice = trySlice(slice, current_idx + 1);
continue;
@@ -243,9 +243,9 @@ fn parseArray(bytes: []const u8, bigint: bool, comptime arrayType: types.Tag, gl
} else {
// the only escape sequency possible here is \b
if (bun.strings.eqlComptime(element, "\\b")) {
try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = bun.String.cloneUTF8("\x08").value.WTFStringImpl }, .free_value = 1 });
try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = bun.String.static("\x08") }, .free_value = 1 });
} else {
try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = if (element.len > 0) bun.String.cloneUTF8(element).value.WTFStringImpl else null }, .free_value = 0 });
try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = if (element.len > 0) bun.String.cloneUTF8(element) else .dead }, .free_value = 0 });
}
}
slice = trySlice(slice, current_idx);
@@ -405,7 +405,7 @@ fn parseArray(bytes: []const u8, bigint: bool, comptime arrayType: types.Tag, gl
if (bigint) {
try array.append(bun.default_allocator, SQLDataCell{ .tag = .int8, .value = .{ .int8 = std.fmt.parseInt(i64, element, 0) catch return error.UnsupportedArrayFormat } });
} else {
try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = if (element.len > 0) bun.String.cloneUTF8(element).value.WTFStringImpl else null }, .free_value = 1 });
try array.append(bun.default_allocator, SQLDataCell{ .tag = .string, .value = .{ .string = if (element.len > 0) bun.String.cloneUTF8(element) else .dead }, .free_value = 1 });
}
slice = trySlice(slice, current_idx);
continue;
@@ -536,7 +536,7 @@ pub fn fromBytes(binary: bool, bigint: bool, oid: types.Tag, bytes: []const u8,
// .int8 is a 64-bit integer always string
return SQLDataCell{ .tag = .int8, .value = .{ .int8 = std.fmt.parseInt(i64, bytes, 0) catch 0 } };
} else {
return SQLDataCell{ .tag = .string, .value = .{ .string = if (bytes.len > 0) bun.String.cloneUTF8(bytes).value.WTFStringImpl else null }, .free_value = 1 };
return SQLDataCell{ .tag = .string, .value = .{ .string = if (bytes.len > 0) bun.String.cloneUTF8(bytes) else .dead }, .free_value = 1 };
}
},
.float8 => {
@@ -566,14 +566,14 @@ pub fn fromBytes(binary: bool, bigint: bool, oid: types.Tag, bytes: []const u8,
// if is binary format lets display as a string because JS cant handle it in a safe way
const result = parseBinaryNumeric(bytes, &numeric_buffer) catch return error.UnsupportedNumericFormat;
return SQLDataCell{ .tag = .string, .value = .{ .string = bun.String.cloneUTF8(result.slice()).value.WTFStringImpl }, .free_value = 1 };
return SQLDataCell{ .tag = .string, .value = .{ .string = bun.String.cloneUTF8(result.slice()) }, .free_value = 1 };
} else {
// nice text is actually what we want here
return SQLDataCell{ .tag = .string, .value = .{ .string = if (bytes.len > 0) String.cloneUTF8(bytes).value.WTFStringImpl else null }, .free_value = 1 };
return SQLDataCell{ .tag = .string, .value = .{ .string = if (bytes.len > 0) String.cloneUTF8(bytes) else .dead }, .free_value = 1 };
}
},
.jsonb, .json => {
return SQLDataCell{ .tag = .json, .value = .{ .json = if (bytes.len > 0) String.cloneUTF8(bytes).value.WTFStringImpl else null }, .free_value = 1 };
return SQLDataCell{ .tag = .json, .value = .{ .json = if (bytes.len > 0) String.cloneUTF8(bytes) else .dead }, .free_value = 1 };
},
.bool => {
if (binary) {
@@ -614,7 +614,7 @@ pub fn fromBytes(binary: bool, bigint: bool, oid: types.Tag, bytes: []const u8,
var buffer: [32]u8 = undefined;
const len = Postgres__formatTime(microseconds, &buffer, buffer.len);
return SQLDataCell{ .tag = .string, .value = .{ .string = bun.String.cloneUTF8(buffer[0..len]).value.WTFStringImpl }, .free_value = 1 };
return SQLDataCell{ .tag = .string, .value = .{ .string = bun.String.cloneUTF8(buffer[0..len]) }, .free_value = 1 };
} else if (tag == .timetz and bytes.len == 12) {
// PostgreSQL sends timetz as microseconds since midnight (8 bytes) + timezone offset in seconds (4 bytes)
const microseconds = @byteSwap(@as(i64, @bitCast(bytes[0..8].*)));
@@ -624,13 +624,13 @@ pub fn fromBytes(binary: bool, bigint: bool, oid: types.Tag, bytes: []const u8,
var buffer: [48]u8 = undefined;
const len = Postgres__formatTimeTz(microseconds, tz_offset_seconds, &buffer, buffer.len);
return SQLDataCell{ .tag = .string, .value = .{ .string = bun.String.cloneUTF8(buffer[0..len]).value.WTFStringImpl }, .free_value = 1 };
return SQLDataCell{ .tag = .string, .value = .{ .string = bun.String.cloneUTF8(buffer[0..len]) }, .free_value = 1 };
} else {
return error.InvalidBinaryData;
}
} else {
// Text format - just return as string
return SQLDataCell{ .tag = .string, .value = .{ .string = if (bytes.len > 0) bun.String.cloneUTF8(bytes).value.WTFStringImpl else null }, .free_value = 1 };
return SQLDataCell{ .tag = .string, .value = .{ .string = if (bytes.len > 0) bun.String.cloneUTF8(bytes) else .dead }, .free_value = 1 };
}
},
@@ -697,7 +697,7 @@ pub fn fromBytes(binary: bool, bigint: bool, oid: types.Tag, bytes: []const u8,
return try parseArray(bytes, bigint, tag, globalObject, null, false);
},
else => {
return SQLDataCell{ .tag = .string, .value = .{ .string = if (bytes.len > 0) bun.String.cloneUTF8(bytes).value.WTFStringImpl else null }, .free_value = 1 };
return SQLDataCell{ .tag = .string, .value = .{ .string = if (bytes.len > 0) bun.String.cloneUTF8(bytes) else .dead }, .free_value = 1 };
},
}
}

View File

@@ -1725,11 +1725,10 @@ pub fn on(this: *PostgresSQLConnection, comptime MessageType: @Type(.enum_litera
.ErrorResponse => {
var err: protocol.ErrorResponse = undefined;
try err.decodeInternal(Context, reader);
errdefer err.deinit();
if (this.status == .connecting or this.status == .sent_startup_message) {
defer {
err.deinit();
}
defer err.deinit();
this.failWithJSValue(err.toJS(this.globalObject));
// it shouldn't enqueue any requests while connecting
@@ -1742,11 +1741,7 @@ pub fn on(this: *PostgresSQLConnection, comptime MessageType: @Type(.enum_litera
return error.ExpectedRequest;
};
var is_error_owned = true;
defer {
if (is_error_owned) {
err.deinit();
}
}
defer if (is_error_owned) err.deinit();
if (request.statement) |stmt| {
if (stmt.status == PostgresSQLStatement.Status.parsing) {
stmt.status = PostgresSQLStatement.Status.failed;

View File

@@ -149,12 +149,7 @@ pub fn structure(this: *PostgresSQLStatement, owner: JSValue, globalObject: *jsc
if (nonDuplicatedCount > jsc.JSObject.maxInlineCapacity()) {
this.cached_structure.set(globalObject, null, ids);
} else {
this.cached_structure.set(globalObject, jsc.JSObject.createStructure(
globalObject,
owner,
@truncate(ids.len),
ids.ptr,
), null);
this.cached_structure.set(globalObject, jsc.JSObject.createStructure(globalObject, owner, @truncate(ids.len), ids.ptr), null);
}
return this.cached_structure;

View File

@@ -9,9 +9,7 @@ pub fn format(formatter: ErrorResponse, comptime _: []const u8, _: std.fmt.Forma
}
pub fn deinit(this: *ErrorResponse) void {
for (this.messages.items) |*message| {
message.deinit();
}
for (this.messages.items) |*message| message.deinit();
this.messages.deinit(bun.default_allocator);
}

View File

@@ -28,9 +28,7 @@ pub const FieldMessage = union(FieldType) {
pub fn deinit(this: *FieldMessage) void {
switch (this.*) {
inline else => |*message| {
message.deref();
},
inline else => |*message| message.deref(),
}
}

View File

@@ -21,9 +21,7 @@ pub fn deinit(this: *@This()) void {
this.structure.deinit();
if (this.fields) |fields| {
this.fields = null;
for (fields) |*name| {
name.deinit();
}
for (fields) |*name| name.deinit();
bun.default_allocator.free(fields);
}
}

View File

@@ -26,7 +26,7 @@ pub const SQLDataCell = extern struct {
pub const Value = extern union {
null: u8,
string: ?bun.WTF.StringImpl,
string: bun.String,
float8: f64,
int4: i32,
int8: i64,
@@ -34,7 +34,7 @@ pub const SQLDataCell = extern struct {
date: f64,
date_with_time_zone: f64,
bytea: [2]usize,
json: ?bun.WTF.StringImpl,
json: bun.String,
array: Array,
typed_array: TypedArray,
raw: Raw,
@@ -91,14 +91,10 @@ pub const SQLDataCell = extern struct {
switch (this.tag) {
.string => {
if (this.value.string) |str| {
str.deref();
}
this.value.string.deref();
},
.json => {
if (this.value.json) |str| {
str.deref();
}
this.value.json.deref();
},
.bytea => {
if (this.value.bytea[1] == 0) return;

View File

@@ -87,9 +87,6 @@ leak:WebCore__DOMURL__fileSystemPath
leak:bun.js.node.node_fs_watcher.FSWatcher.Arguments.fromJS
leak:WebWorker__updatePtr
leak:bun.js.node.zlib.NativeZlib.Context.init
leak:sql.postgres.PostgresSQLStatement.structure
leak:sql.postgres.DataCell.parseArray__anon
leak:sql.postgres.protocol.FieldMessage.FieldMessage.init
leak:JSC::intlCollatorAvailableLocales
leak:Bun__canonicalizeIP
leak:dlopen