mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
Handle optionals in bun.memory.deinit (#23027)
Currently, if you try to deinit an optional, `bun.memory.deinit` will silently do nothing, even if the optional's payload is a struct with a `deinit` method. This commit makes sure the payload is deinitialized. (For internal tracking: fixes STAB-1293)
This commit is contained in:
@@ -65,15 +65,17 @@ fn deinitIsVoid(comptime T: type) bool {
|
||||
};
|
||||
}
|
||||
|
||||
/// Calls `deinit` on `ptr_or_slice`, or on every element of `ptr_or_slice`, if such a `deinit`
|
||||
/// method exists.
|
||||
/// Calls `deinit` on `ptr_or_slice`, or on every element of `ptr_or_slice`, if the pointer points
|
||||
/// to a struct or tagged union.
|
||||
///
|
||||
/// This function first does the following:
|
||||
///
|
||||
/// * If `ptr_or_slice` is a single-item pointer, calls `ptr_or_slice.deinit()`, if that method
|
||||
/// exists.
|
||||
/// * If `ptr_or_slice` is a slice, calls `deinit` on every element of the slice, if the slice
|
||||
/// elements have a `deinit` method.
|
||||
/// * If `ptr_or_slice` is a single-item pointer of type `*T`:
|
||||
/// - If `T` is a struct or tagged union, calls `ptr_or_slice.deinit()`
|
||||
/// - If `T` is an optional, checks if `ptr_or_slice` points to a non-null value, and if so,
|
||||
/// calls `bun.memory.deinit` with a pointer to the payload.
|
||||
/// * If `ptr_or_slice` is a slice, for each element of the slice, calls `bun.memory.deinit` with
|
||||
/// a pointer to the element.
|
||||
///
|
||||
/// Then, if `ptr_or_slice` is non-const, this function also sets all memory referenced by the
|
||||
/// pointer to `undefined`.
|
||||
@@ -81,32 +83,43 @@ fn deinitIsVoid(comptime T: type) bool {
|
||||
/// This method does not free `ptr_or_slice` itself.
|
||||
pub fn deinit(ptr_or_slice: anytype) void {
|
||||
const ptr_info = @typeInfo(@TypeOf(ptr_or_slice));
|
||||
switch (comptime ptr_info.pointer.size) {
|
||||
.slice => {
|
||||
for (ptr_or_slice) |*elem| {
|
||||
deinit(elem);
|
||||
}
|
||||
return;
|
||||
},
|
||||
.one => {},
|
||||
else => @compileError("unsupported pointer type"),
|
||||
}
|
||||
|
||||
const Child = ptr_info.pointer.child;
|
||||
const mutable = !ptr_info.pointer.is_const;
|
||||
defer {
|
||||
if (comptime mutable) {
|
||||
ptr_or_slice.* = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const needs_deinit = comptime switch (@typeInfo(Child)) {
|
||||
.@"struct" => true,
|
||||
.@"union" => |u| u.tag_type != null,
|
||||
.optional => {
|
||||
if (ptr_or_slice.*) |*payload| {
|
||||
deinit(payload);
|
||||
}
|
||||
return;
|
||||
},
|
||||
else => false,
|
||||
};
|
||||
|
||||
const should_call_deinit = comptime needs_deinit and
|
||||
!exemptedFromDeinit(Child) and
|
||||
!deinitIsVoid(Child);
|
||||
|
||||
switch (comptime ptr_info.pointer.size) {
|
||||
.one => {
|
||||
if (comptime should_call_deinit) {
|
||||
ptr_or_slice.deinit();
|
||||
}
|
||||
if (comptime mutable) ptr_or_slice.* = undefined;
|
||||
},
|
||||
.slice => for (ptr_or_slice) |*elem| {
|
||||
if (comptime should_call_deinit) {
|
||||
elem.deinit();
|
||||
}
|
||||
if (comptime mutable) elem.* = undefined;
|
||||
},
|
||||
else => @compileError("unsupported pointer type"),
|
||||
if (comptime should_call_deinit) {
|
||||
ptr_or_slice.deinit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user