Skip to content

Commit

Permalink
zig: eliminate errorUnionToCPP (#15416)
Browse files Browse the repository at this point in the history
  • Loading branch information
nektro authored Nov 26, 2024
1 parent 68f026b commit 5e46422
Show file tree
Hide file tree
Showing 4 changed files with 1 addition and 168 deletions.
110 changes: 0 additions & 110 deletions src/bun.js/bindings/bindings.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2996,116 +2996,6 @@ pub const JSGlobalObject = opaque {
return .zero;
}

/// Pass a JSOrMemoryError!JSValue and variants through the C ABI boundary
///
/// In C++, WebKit represents a thrown JavaScript expression as
/// JSValue.zero/nullptr, and stores the actual exception on the global. In
/// Zig, we represent this zero as a distinct Zig error type
/// 'error.JSError'. Instead of using JSValue.zero directly, we pass the
/// Zig error to this function as "proof" there is an error. In debug, this
/// will also assert that an error is actually present.
///
/// If .zero is exposed to JS, it will be considered a JSCell but with a
/// null pointer and will segfault via null-pointer dereference.
///
/// Ideally, we can use this function as little as possible, and instead
/// have auto-generated wrappers that do this conversion. It is ugly to use
/// on purpose.
pub inline fn errorUnionToCPP(global: *JSGlobalObject, result: anytype) RemoveError(@TypeOf(result)) {
const T = @TypeOf(result);

const unwrapped = switch (@typeInfo(T)) {
.ErrorUnion => result catch |err| {
bun.handleErrorReturnTrace(err, @errorReturnTrace());
return global.errorSetToCPP(err, T);
},
.ErrorSet => return global.errorSetToCPP(result, T),
else => result,
};

// Validate exception state aligns with return value.
// Currently only enabled for JSValue.
const Return = RemoveError(T);
if (bun.Environment.isDebug and Return == JSValue) {
const null_value: Return = comptime if (Return == JSC.JSValue)
.zero
else
null;

if (unwrapped == null_value) {
std.debug.assert(global.hasException()); // Exception was cleared, yet returned.
} else if (Return == JSC.JSValue and unwrapped == .undefined) {
// TODO: a lot of our code returns undefined when it throws an error.
} else {
if (global.tryTakeException()) |exception| {
bun.Output.err("assertion failure", "Pending exception while returning non-empty JSValue", .{});
bun.Output.printErrorln("Exception thrown:", .{});
bun.Output.flush();
global.bunVM().printErrorLikeObjectToConsole(exception);
bun.Output.printErrorln("Value returned:", .{});
bun.Output.flush();
if (Return == JSValue) {
unwrapped.print(global, .Log, .Error);
} else {
bun.Output.printErrorln(" {any}", .{unwrapped});
}
bun.Output.flush();
@panic("Pending exception while returning non-empty JSValue");
}
}
}

return unwrapped;
}

pub fn RemoveError(T: type) type {
return switch (@typeInfo(T)) {
.ErrorSet => bun.JSC.JSValue,
.ErrorUnion => |eu| if (@typeInfo(eu.payload) == .Pointer)
?eu.payload
else
eu.payload,
else => T,
};
}

inline fn errorSetToCPP(global: *JSGlobalObject, err: anytype, T: type) RemoveError(T) {
const info = @typeInfo(@TypeOf(err));
comptime bun.assert(info == .ErrorSet);
const Return = RemoveError(T);
const null_value: Return = comptime if (Return == JSC.JSValue)
.zero
else
null;

const possible_errors = comptime parseErrorSet(
T,
info.ErrorSet orelse
@compileError("host function cannot return 'anyerror!JSValue'"),
);

if (possible_errors.OutOfMemory and err == error.OutOfMemory) {
if (global.hasException()) {
if (comptime bun.Environment.isDebug) bun.Output.panic("attempted to throw OutOfMemory without an exception", .{});
} else {
global.throwOutOfMemory();
}
return null_value;
}

if (possible_errors.JSError and err == error.JSError) {
if (!global.hasException()) {
if (comptime bun.Environment.isDebug) bun.Output.panic("attempted to throw JSError without an exception", .{});
global.throwOutOfMemory();
}
return null_value;
}

// all errors have now been handled. parseErrorSet will report
// a compile error if there is another possible error
unreachable;
}

pub fn createInvalidArgumentType(
this: *JSGlobalObject,
comptime name_: []const u8,
Expand Down
3 changes: 0 additions & 3 deletions src/bun.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4118,6 +4118,3 @@ pub inline fn isComptimeKnown(x: anytype) bool {
pub inline fn itemOrNull(comptime T: type, slice: []const T, index: usize) ?T {
return if (index < slice.len) slice[index] else null;
}

/// Code used by the classes generator
pub const gen_classes_lib = @import("gen_classes_lib.zig");
4 changes: 1 addition & 3 deletions src/codegen/generate-js2native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,7 @@ export function getJS2NativeZig(gs2NativeZigPath: string) {
.filter(x => x.type === "zig")
.flatMap(call => [
`export fn ${symbol(call)}_workaround(global: *JSC.JSGlobalObject) callconv(JSC.conv) JSC.JSValue {`,
` return global.errorUnionToCPP(@import(${JSON.stringify(path.relative(path.dirname(gs2NativeZigPath), call.filename))}).${
call.symbol
}(global));`,
` return JSC.toJSHostValue(global, @import(${JSON.stringify(path.relative(path.dirname(gs2NativeZigPath), call.filename))}).${call.symbol}(global));`,
"}",
]),
...wrapperCalls
Expand Down
52 changes: 0 additions & 52 deletions src/gen_classes_lib.zig

This file was deleted.

0 comments on commit 5e46422

Please sign in to comment.