mirror of
https://github.com/oven-sh/bun
synced 2026-02-03 07:28:53 +00:00
Compare commits
4 Commits
dylan/byte
...
jarred/req
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
822b8f80f2 | ||
|
|
a9ea41f433 | ||
|
|
1227d81260 | ||
|
|
c504f8a21e |
@@ -32,6 +32,10 @@ function generate(name) {
|
||||
fn: "doRequestIP",
|
||||
length: 1,
|
||||
},
|
||||
requestID: {
|
||||
fn: "doRequestID",
|
||||
length: 1,
|
||||
},
|
||||
port: {
|
||||
getter: "getPort",
|
||||
},
|
||||
|
||||
@@ -1298,6 +1298,7 @@ fn NewFlags(comptime debug_mode: bool) type {
|
||||
response_protected: bool = false,
|
||||
aborted: bool = false,
|
||||
has_finalized: bun.DebugOnly(bool) = bun.DebugOnlyDefault(false),
|
||||
must_bust_abort_reason_cache_to_prevent_leaking_error_instance: bool = false,
|
||||
|
||||
is_error_promise_pending: bool = false,
|
||||
};
|
||||
@@ -1305,12 +1306,15 @@ fn NewFlags(comptime debug_mode: bool) type {
|
||||
|
||||
/// A generic wrapper for the HTTP(s) Server`RequestContext`s.
|
||||
/// Only really exists because of `NewServer()` and `NewRequestContext()` generics.
|
||||
/// When the RequestContext is finalized, this points to the request ID.
|
||||
pub const AnyRequestContext = struct {
|
||||
const RequestIDInt = usize;
|
||||
pub const Pointer = bun.TaggedPointerUnion(.{
|
||||
HTTPServer.RequestContext,
|
||||
HTTPSServer.RequestContext,
|
||||
DebugHTTPServer.RequestContext,
|
||||
DebugHTTPSServer.RequestContext,
|
||||
RequestIDInt,
|
||||
});
|
||||
|
||||
tagged_pointer: Pointer,
|
||||
@@ -1339,7 +1343,7 @@ pub const AnyRequestContext = struct {
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPSServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(DebugHTTPSServer.RequestContext).getRemoteSocketInfo();
|
||||
},
|
||||
else => @panic("Unexpected AnyRequestContext tag"),
|
||||
else => return null,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1366,6 +1370,34 @@ pub const AnyRequestContext = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getRequestID(self: AnyRequestContext) ?RequestID {
|
||||
if (self.tagged_pointer.isNull()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (self.tagged_pointer.tag()) {
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(HTTPServer.RequestContext).getRequestID();
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(HTTPSServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(HTTPSServer.RequestContext).getRequestID();
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(DebugHTTPServer.RequestContext).getRequestID();
|
||||
},
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPSServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(DebugHTTPSServer.RequestContext).getRequestID();
|
||||
},
|
||||
Pointer.Tag.usize => return self.tagged_pointer.repr._ptr,
|
||||
else => @panic("Unexpected AnyRequestContext tag"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setRequestID(self: *AnyRequestContext, id: JSC.CommonAbortReason.Cacheable.ID) void {
|
||||
self.tagged_pointer.repr._ptr = @intCast(id);
|
||||
self.tagged_pointer.repr.data = @intFromEnum(Pointer.Tag.usize);
|
||||
}
|
||||
|
||||
pub fn getRequest(self: AnyRequestContext) ?*uws.Request {
|
||||
if (self.tagged_pointer.isNull()) {
|
||||
return null;
|
||||
@@ -1384,11 +1416,27 @@ pub const AnyRequestContext = struct {
|
||||
@field(Pointer.Tag, bun.meta.typeBaseName(@typeName(DebugHTTPSServer.RequestContext))) => {
|
||||
return self.tagged_pointer.as(DebugHTTPSServer.RequestContext).req;
|
||||
},
|
||||
Pointer.Tag.usize => return null,
|
||||
else => @panic("Unexpected AnyRequestContext tag"),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const RequestID = struct {
|
||||
pub const Int = u48;
|
||||
};
|
||||
|
||||
pub fn nextRequestID() JSC.CommonAbortReason.Cacheable.SizedID {
|
||||
const Holder = struct {
|
||||
pub var current_request_id = std.atomic.Value(u64).init(1);
|
||||
};
|
||||
|
||||
const max = Holder.current_request_id.fetchAdd(1, .monotonic);
|
||||
// Never return 0
|
||||
// Number between 1 and 2^31 - 1
|
||||
return @truncate((max % JSC.CommonAbortReason.Cacheable.max_id) + 1);
|
||||
}
|
||||
|
||||
// This is defined separately partially to work-around an LLVM debugger bug.
|
||||
fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comptime ThisServer: type) type {
|
||||
return struct {
|
||||
@@ -1445,6 +1493,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
|
||||
/// Defer finalization until after the request handler task is completed?
|
||||
defer_deinit_until_callback_completes: ?*bool = null,
|
||||
request_id: JSC.CommonAbortReason.Cacheable.ID = 0,
|
||||
|
||||
// TODO: support builtin compression
|
||||
const can_sendfile = !ssl_enabled and !Environment.isWindows;
|
||||
@@ -1455,7 +1504,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
|
||||
fn drainMicrotasks(this: *const RequestContext) void {
|
||||
if (this.isAsync()) return;
|
||||
if(this.server) |server| server.vm.drainMicrotasks();
|
||||
if (this.server) |server| server.vm.drainMicrotasks();
|
||||
}
|
||||
|
||||
pub fn setAbortHandler(this: *RequestContext) void {
|
||||
@@ -1484,7 +1533,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
var class_name = value.getClassInfoName() orelse bun.String.empty;
|
||||
defer class_name.deref();
|
||||
|
||||
if(ctx.server) |server| {
|
||||
if (ctx.server) |server| {
|
||||
const globalThis: *JSC.JSGlobalObject = server.globalThis;
|
||||
|
||||
Output.enableBuffering();
|
||||
@@ -1516,7 +1565,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
return;
|
||||
}
|
||||
|
||||
if(ctx.server == null) {
|
||||
if (ctx.server == null) {
|
||||
ctx.renderMissingInvalidResponse(value);
|
||||
return;
|
||||
}
|
||||
@@ -1588,7 +1637,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
this.request_body = null;
|
||||
}
|
||||
|
||||
if(this.server) |server| {
|
||||
if (this.server) |server| {
|
||||
this.server = null;
|
||||
server.request_pool_allocator.put(this);
|
||||
server.onRequestComplete();
|
||||
@@ -1784,7 +1833,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
}
|
||||
this.detachResponse();
|
||||
this.endRequestStreamingAndDrain();
|
||||
this.deref();
|
||||
this.deref();
|
||||
}
|
||||
|
||||
/// Drain a partial response buffer
|
||||
@@ -1882,6 +1931,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
.req = req,
|
||||
.method = HTTP.Method.which(req.method()) orelse .GET,
|
||||
.server = server,
|
||||
.request_id = nextRequestID(),
|
||||
};
|
||||
|
||||
ctxLog("create<d> ({*})<r>", .{this});
|
||||
@@ -1911,9 +1961,10 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
this.signal = null;
|
||||
defer signal.unref();
|
||||
if (!signal.aborted()) {
|
||||
const reason = JSC.WebCore.AbortSignal.createAbortError(JSC.ZigString.static("The user aborted a request"), &JSC.ZigString.Empty, globalThis);
|
||||
reason.ensureStillAlive();
|
||||
_ = signal.signal(reason);
|
||||
this.flags.must_bust_abort_reason_cache_to_prevent_leaking_error_instance = true;
|
||||
signal.signal(globalThis, .{
|
||||
.ConnectionClosed = this.request_id,
|
||||
});
|
||||
any_js_calls = true;
|
||||
}
|
||||
}
|
||||
@@ -1921,6 +1972,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
//if have sink, call onAborted on sink
|
||||
if (this.sink) |wrapper| {
|
||||
wrapper.sink.abort();
|
||||
this.flags.must_bust_abort_reason_cache_to_prevent_leaking_error_instance = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1928,7 +1980,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
if (this.isDeadRequest()) {
|
||||
this.finalizeWithoutDeinit();
|
||||
} else {
|
||||
if(this.endRequestStreaming()) {
|
||||
if (this.endRequestStreaming()) {
|
||||
any_js_calls = true;
|
||||
}
|
||||
|
||||
@@ -1954,6 +2006,8 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
assert(this.server != null);
|
||||
const globalThis = this.server.?.globalThis;
|
||||
|
||||
var must_bust_cache = this.flags.must_bust_abort_reason_cache_to_prevent_leaking_error_instance;
|
||||
|
||||
if (comptime Environment.allow_assert) {
|
||||
ctxLog("finalizeWithoutDeinit: has_finalized {any}", .{this.flags.has_finalized});
|
||||
this.flags.has_finalized = true;
|
||||
@@ -1973,13 +2027,14 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
this.signal = null;
|
||||
defer signal.unref();
|
||||
if (this.flags.aborted and !signal.aborted()) {
|
||||
const reason = JSC.WebCore.AbortSignal.createAbortError(JSC.ZigString.static("The user aborted a request"), &JSC.ZigString.Empty, globalThis);
|
||||
reason.ensureStillAlive();
|
||||
_ = signal.signal(reason);
|
||||
this.flags.must_bust_abort_reason_cache_to_prevent_leaking_error_instance = true;
|
||||
signal.signal(globalThis, .{
|
||||
.ConnectionClosed = this.request_id,
|
||||
});
|
||||
must_bust_cache = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Case 1:
|
||||
// User called .blob(), .json(), text(), or .arrayBuffer() on the Request object
|
||||
// but we received nothing or the connection was aborted
|
||||
@@ -1988,11 +2043,13 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
// User ignored the body and the connection was aborted or ended
|
||||
// Case 3:
|
||||
// Stream was not consumed and the connection was aborted or ended
|
||||
_ = this.endRequestStreaming();
|
||||
|
||||
if (this.endRequestStreaming()) {
|
||||
must_bust_cache = true;
|
||||
}
|
||||
|
||||
if (this.byte_stream) |stream| {
|
||||
ctxLog("finalizeWithoutDeinit: stream != null", .{});
|
||||
|
||||
must_bust_cache = true;
|
||||
this.byte_stream = null;
|
||||
stream.unpipeWithoutDeref();
|
||||
}
|
||||
@@ -2003,6 +2060,11 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
this.pathname.deref();
|
||||
this.pathname = bun.String.empty;
|
||||
}
|
||||
|
||||
if (must_bust_cache) {
|
||||
this.flags.must_bust_abort_reason_cache_to_prevent_leaking_error_instance = false;
|
||||
JSC.CommonAbortReason.Cacheable.bust(.{ .ConnectionClosed = this.request_id }, globalThis);
|
||||
}
|
||||
}
|
||||
|
||||
fn writeHeaders(
|
||||
@@ -2306,9 +2368,9 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
if (comptime can_sendfile) {
|
||||
return this.renderSendFile(blob);
|
||||
}
|
||||
if(this.server) |server| {
|
||||
this.ref();
|
||||
this.blob.Blob.doReadFileInternal(*RequestContext, this, onReadFile, server.globalThis);
|
||||
if (this.server) |server| {
|
||||
this.ref();
|
||||
this.blob.Blob.doReadFileInternal(*RequestContext, this, onReadFile, server.globalThis);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2320,7 +2382,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
}
|
||||
|
||||
if (result == .err) {
|
||||
if(this.server) |server| {
|
||||
if (this.server) |server| {
|
||||
this.runErrorHandler(result.err.toErrorInstance(server.globalThis));
|
||||
}
|
||||
return;
|
||||
@@ -2475,7 +2537,6 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
streamLog("returned a promise", .{});
|
||||
this.drainMicrotasks();
|
||||
|
||||
|
||||
switch (promise.status(globalThis.vm())) {
|
||||
.Pending => {
|
||||
streamLog("promise still Pending", .{});
|
||||
@@ -2574,10 +2635,10 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
}
|
||||
|
||||
fn toAsyncWithoutAbortHandler(ctx: *RequestContext, req: *uws.Request, request_object: *Request) void {
|
||||
const id = ctx.getRequestID();
|
||||
request_object.request_context.setRequest(req);
|
||||
assert(ctx.server != null);
|
||||
|
||||
|
||||
request_object.ensureURL() catch {
|
||||
request_object.url = bun.String.empty;
|
||||
};
|
||||
@@ -2589,7 +2650,11 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
|
||||
// This object dies after the stack frame is popped
|
||||
// so we have to clear it in here too
|
||||
request_object.request_context = JSC.API.AnyRequestContext.Null;
|
||||
request_object.request_context.setRequestID(id);
|
||||
}
|
||||
|
||||
pub fn getRequestID(this: *const RequestContext) JSC.CommonAbortReason.Cacheable.ID {
|
||||
return this.request_id;
|
||||
}
|
||||
|
||||
fn toAsync(
|
||||
@@ -2608,19 +2673,19 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
fn endRequestStreamingAndDrain(this: *RequestContext) void {
|
||||
assert(this.server != null);
|
||||
|
||||
if(this.endRequestStreaming()) {
|
||||
if (this.endRequestStreaming()) {
|
||||
this.server.?.vm.drainMicrotasks();
|
||||
}
|
||||
}
|
||||
fn endRequestStreaming(this: *RequestContext) bool {
|
||||
assert(this.server != null);
|
||||
// if we cannot, we have to reject pending promises
|
||||
// first, we reject the request body promise
|
||||
if (this.request_body) |body| {
|
||||
// if we cannot, we have to reject pending promises
|
||||
// first, we reject the request body promise
|
||||
if (this.request_body) |body| {
|
||||
// User called .blob(), .json(), text(), or .arrayBuffer() on the Request object
|
||||
// but we received nothing or the connection was aborted
|
||||
if (body.value == .Locked) {
|
||||
body.value.toErrorInstance(.{ .Aborted = {} }, this.server.?.globalThis);
|
||||
body.value.toErrorInstance(.{ .ConnectionClosed = this.request_id }, this.server.?.globalThis);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -2629,7 +2694,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
fn detachResponse(this: *RequestContext) void {
|
||||
if (this.resp) |resp| {
|
||||
this.resp = null;
|
||||
|
||||
|
||||
if (this.flags.is_waiting_for_request_body) {
|
||||
this.flags.is_waiting_for_request_body = false;
|
||||
resp.clearOnData();
|
||||
@@ -2807,8 +2872,8 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
resp.body.value = .{ .Used = {} };
|
||||
}
|
||||
}
|
||||
|
||||
if(req.isAbortedOrEnded()) {
|
||||
|
||||
if (req.isAbortedOrEnded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2883,7 +2948,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
|
||||
req.endStream(true);
|
||||
if (comptime debug_mode) {
|
||||
if(req.server) |server| {
|
||||
if (req.server) |server| {
|
||||
if (!err.isEmptyOrUndefinedOrNull()) {
|
||||
var exception_list: std.ArrayList(Api.JsException) = std.ArrayList(Api.JsException).init(req.allocator);
|
||||
defer exception_list.deinit();
|
||||
@@ -3148,7 +3213,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
}
|
||||
|
||||
fn finishRunningErrorHandler(this: *RequestContext, value: JSC.JSValue, status: u16) void {
|
||||
if(this.server == null) return this.renderProductionError(status);
|
||||
if (this.server == null) return this.renderProductionError(status);
|
||||
var vm: *JSC.VirtualMachine = this.server.?.vm;
|
||||
const globalThis = this.server.?.globalThis;
|
||||
if (comptime debug_mode) {
|
||||
@@ -3182,7 +3247,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
status: u16,
|
||||
) void {
|
||||
JSC.markBinding(@src());
|
||||
if(this.server) |server| {
|
||||
if (this.server) |server| {
|
||||
if (!server.config.onError.isEmpty() and !this.flags.has_called_error_handler) {
|
||||
this.flags.has_called_error_handler = true;
|
||||
const result = server.config.onError.call(
|
||||
@@ -3565,7 +3630,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp
|
||||
}
|
||||
const max_request_body_preallocate_length = 1024 * 256;
|
||||
pub fn onStartBuffering(this: *RequestContext) void {
|
||||
if(this.server) |server| {
|
||||
if (this.server) |server| {
|
||||
ctxLog("onStartBuffering", .{});
|
||||
// TODO: check if is someone calling onStartBuffering other than onStartBufferingCallback
|
||||
// if is not, this should be removed and only keep protect + setAbortHandler
|
||||
@@ -5302,6 +5367,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
|
||||
pub const doReload = onReload;
|
||||
pub const doFetch = onFetch;
|
||||
pub const doRequestIP = JSC.wrapInstanceMethod(ThisServer, "requestIP", false);
|
||||
pub const doRequestID = JSC.wrapInstanceMethod(ThisServer, "requestID", false);
|
||||
|
||||
pub usingnamespace NamespaceType;
|
||||
pub usingnamespace bun.New(@This());
|
||||
@@ -5328,6 +5394,14 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
|
||||
JSValue.jsNull();
|
||||
}
|
||||
|
||||
pub fn requestID(_: *ThisServer, request: *JSC.WebCore.Request) JSC.JSValue {
|
||||
if (request.request_context.getRequestID()) |id| {
|
||||
return JSValue.jsNumber(id);
|
||||
}
|
||||
|
||||
return JSValue.jsNull();
|
||||
}
|
||||
|
||||
pub fn publish(this: *ThisServer, globalThis: *JSC.JSGlobalObject, topic: ZigString, message_value: JSValue, compress_value: ?JSValue, exception: JSC.C.ExceptionRef) JSValue {
|
||||
if (this.config.websocket == null)
|
||||
return JSValue.jsNumber(0);
|
||||
@@ -5511,7 +5585,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
|
||||
// obviously invalid pointer marks it as used
|
||||
upgrader.upgrade_context = @as(*uws.uws_socket_context_s, @ptrFromInt(std.math.maxInt(usize)));
|
||||
// set the abort handler so we can receive onAbort to deref the context
|
||||
upgrader.setAbortHandler();
|
||||
upgrader.setAbortHandler();
|
||||
// after upgrading we should not use the response anymore
|
||||
upgrader.resp = null;
|
||||
request.upgrader = null;
|
||||
@@ -6321,9 +6395,10 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
|
||||
request_value.ensureStillAlive();
|
||||
|
||||
const response_value = this.config.onRequest.call(this.globalThis, this.thisObject, &args);
|
||||
const id = ctx.getRequestID();
|
||||
defer {
|
||||
// uWS request will not live longer than this function
|
||||
request_object.request_context = JSC.API.AnyRequestContext.Null;
|
||||
request_object.request_context.setRequestID(id);
|
||||
}
|
||||
const original_state = ctx.defer_deinit_until_callback_completes;
|
||||
var should_deinit_context = false;
|
||||
@@ -6338,7 +6413,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
|
||||
ctx.defer_deinit_until_callback_completes = original_state;
|
||||
|
||||
if (should_deinit_context) {
|
||||
request_object.request_context = JSC.API.AnyRequestContext.Null;
|
||||
request_object.request_context.setRequestID(id);
|
||||
ctx.deinit();
|
||||
return;
|
||||
}
|
||||
@@ -6387,11 +6462,11 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
|
||||
const request_value = args[0];
|
||||
request_value.ensureStillAlive();
|
||||
const response_value = this.config.onRequest.call(this.globalThis, this.thisObject, &args);
|
||||
const id = ctx.getRequestID();
|
||||
defer {
|
||||
// uWS request will not live longer than this function
|
||||
request_object.request_context = JSC.API.AnyRequestContext.Null;
|
||||
request_object.request_context.setRequestID(id);
|
||||
}
|
||||
|
||||
const original_state = ctx.defer_deinit_until_callback_completes;
|
||||
var should_deinit_context = false;
|
||||
ctx.defer_deinit_until_callback_completes = &should_deinit_context;
|
||||
@@ -6405,7 +6480,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
|
||||
ctx.defer_deinit_until_callback_completes = original_state;
|
||||
|
||||
if (should_deinit_context) {
|
||||
request_object.request_context = JSC.API.AnyRequestContext.Null;
|
||||
request_object.request_context.setRequestID(id);
|
||||
ctx.deinit();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,29 +1,178 @@
|
||||
#include "root.h"
|
||||
|
||||
#include "JavaScriptCore/Error.h"
|
||||
#include "JavaScriptCore/ErrorType.h"
|
||||
#include "JavaScriptCore/ObjectConstructor.h"
|
||||
#include "JavaScriptCore/WriteBarrier.h"
|
||||
#include "root.h"
|
||||
#include "headers-handwritten.h"
|
||||
#include "BunClientData.h"
|
||||
#include "helpers.h"
|
||||
#include "JavaScriptCore/JSCJSValue.h"
|
||||
#include "JavaScriptCore/ErrorInstance.h"
|
||||
#include "JavaScriptCore/ExceptionScope.h"
|
||||
#include "JavaScriptCore/JSString.h"
|
||||
#include "JavaScriptCore/JSType.h"
|
||||
#include "JavaScriptCore/Symbol.h"
|
||||
#include "wtf/text/ASCIILiteral.h"
|
||||
#include "wtf/text/MakeString.h"
|
||||
#include "wtf/text/WTFString.h"
|
||||
#include <cstdio>
|
||||
#include "AbortSignal.h"
|
||||
#include "JavaScriptCore/ErrorInstanceInlines.h"
|
||||
#include "JavaScriptCore/JSInternalFieldObjectImplInlines.h"
|
||||
|
||||
JSC::EncodedJSValue JSC__JSValue__createTypeError(const ZigString* message, const ZigString* arg1, JSC::JSGlobalObject* globalObject);
|
||||
JSC::EncodedJSValue JSC__JSValue__createRangeError(const ZigString* message, const ZigString* arg1, JSC::JSGlobalObject* globalObject);
|
||||
#include "NodeError.h"
|
||||
|
||||
static JSC::JSObject* createErrorPrototype(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::ErrorType type, WTF::ASCIILiteral name, WTF::ASCIILiteral code)
|
||||
{
|
||||
JSC::JSObject* prototype;
|
||||
|
||||
switch (type) {
|
||||
case JSC::ErrorType::TypeError:
|
||||
prototype = JSC::constructEmptyObject(globalObject, globalObject->m_typeErrorStructure.prototype(globalObject));
|
||||
break;
|
||||
case JSC::ErrorType::RangeError:
|
||||
prototype = JSC::constructEmptyObject(globalObject, globalObject->m_rangeErrorStructure.prototype(globalObject));
|
||||
break;
|
||||
case JSC::ErrorType::Error:
|
||||
prototype = JSC::constructEmptyObject(globalObject, globalObject->errorPrototype());
|
||||
break;
|
||||
default: {
|
||||
RELEASE_ASSERT_NOT_REACHED_WITH_MESSAGE("TODO: Add support for more error types");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
prototype->putDirect(vm, vm.propertyNames->name, jsString(vm, String(name)), 0);
|
||||
prototype->putDirect(vm, WebCore::builtinNames(vm).codePublicName(), jsString(vm, String(code)), 0);
|
||||
|
||||
return prototype;
|
||||
}
|
||||
|
||||
static JSC::Structure* createErrorStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::ErrorType type, WTF::ASCIILiteral name, WTF::ASCIILiteral code)
|
||||
{
|
||||
JSC::JSObject* prototype = createErrorPrototype(vm, globalObject, type, name, code);
|
||||
return JSC::ErrorInstance::createStructure(vm, globalObject, prototype);
|
||||
}
|
||||
|
||||
extern "C" JSC::EncodedJSValue Bun__ERR_INVALID_ARG_TYPE(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue val_arg_name, JSC::EncodedJSValue val_expected_type, JSC::EncodedJSValue val_actual_value);
|
||||
extern "C" JSC::EncodedJSValue Bun__ERR_MISSING_ARGS(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue arg1, JSC::EncodedJSValue arg2, JSC::EncodedJSValue arg3);
|
||||
extern "C" JSC::EncodedJSValue Bun__ERR_IPC_CHANNEL_CLOSED(JSC::JSGlobalObject* globalObject);
|
||||
|
||||
// clang-format on
|
||||
|
||||
namespace Bun {
|
||||
|
||||
using namespace JSC;
|
||||
|
||||
struct NodeErrorData {
|
||||
JSC::ErrorType type;
|
||||
WTF::ASCIILiteral name;
|
||||
WTF::ASCIILiteral code;
|
||||
};
|
||||
static constexpr NodeErrorData errors[NODE_ERROR_COUNT] = {
|
||||
#define DECLARE_ERROR_WITH_CODE_ENUM(E, name, code) { JSC::ErrorType::E, #name ""_s, #code ""_s },
|
||||
FOR_EACH_NODE_ERROR_WITH_CODE(DECLARE_ERROR_WITH_CODE_ENUM)
|
||||
#undef DECLARE_ERROR_WITH_CODE_ENUM
|
||||
};
|
||||
|
||||
const ClassInfo NodeErrorCache::s_info = { "NodeErrorCache"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(NodeErrorCache) };
|
||||
|
||||
NodeErrorCache::NodeErrorCache(VM& vm, Structure* structure)
|
||||
: Base(vm, structure)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename Visitor>
|
||||
void NodeErrorCache::visitChildrenImpl(JSCell* cell, Visitor& visitor)
|
||||
{
|
||||
auto* thisObject = jsCast<NodeErrorCache*>(cell);
|
||||
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
|
||||
Base::visitChildren(thisObject, visitor);
|
||||
visitor.append(thisObject->m_cachedReason);
|
||||
}
|
||||
|
||||
DEFINE_VISIT_CHILDREN_WITH_MODIFIER(JS_EXPORT_PRIVATE, NodeErrorCache);
|
||||
|
||||
Structure* NodeErrorCache::createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
|
||||
{
|
||||
return Structure::create(vm, globalObject, jsNull(), TypeInfo(InternalFieldTupleType, StructureFlags), info(), 0, 0);
|
||||
}
|
||||
|
||||
NodeErrorCache* NodeErrorCache::create(VM& vm, Structure* structure)
|
||||
{
|
||||
NodeErrorCache* object = new (NotNull, allocateCell<NodeErrorCache>(vm)) NodeErrorCache(vm, structure);
|
||||
object->finishCreation(vm);
|
||||
return object;
|
||||
}
|
||||
|
||||
void NodeErrorCache::finishCreation(VM& vm)
|
||||
{
|
||||
Base::finishCreation(vm);
|
||||
ASSERT(inherits(info()));
|
||||
|
||||
for (unsigned i = 0; i < NODE_ERROR_COUNT; i++) {
|
||||
this->internalField(i).clear();
|
||||
}
|
||||
|
||||
this->m_cachedReason.clear();
|
||||
}
|
||||
|
||||
static NodeErrorCache* errorCache(Zig::GlobalObject* globalObject)
|
||||
{
|
||||
return static_cast<NodeErrorCache*>(globalObject->nodeErrorCache());
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
static Structure* createErrorStructure(JSC::VM& vm, JSGlobalObject* globalObject, JSC::ErrorType type, WTF::ASCIILiteral name, WTF::ASCIILiteral code)
|
||||
{
|
||||
auto* prototype = createErrorPrototype(vm, globalObject, type, name, code);
|
||||
return ErrorInstance::createStructure(vm, globalObject, prototype);
|
||||
}
|
||||
|
||||
JSObject* NodeErrorCache::createError(VM& vm, Zig::GlobalObject* globalObject, NodeErrorCode code, JSValue message, JSValue options)
|
||||
{
|
||||
auto* cache = errorCache(globalObject);
|
||||
if (!cache->internalField(static_cast<unsigned>(code))) {
|
||||
const auto& data = errors[code];
|
||||
auto* structure = createErrorStructure(vm, globalObject, data.type, data.name, data.code);
|
||||
cache->internalField(static_cast<unsigned>(code)).set(vm, cache, structure);
|
||||
}
|
||||
|
||||
auto* structure = jsCast<Structure*>(cache->internalField(static_cast<unsigned>(code)).get());
|
||||
return JSC::ErrorInstance::create(globalObject, structure, message, options, nullptr, JSC::RuntimeType::TypeNothing, errors[code].type, true);
|
||||
}
|
||||
|
||||
JSObject* createError(VM& vm, Zig::GlobalObject* globalObject, NodeErrorCode code, const String& message)
|
||||
{
|
||||
return errorCache(globalObject)->createError(vm, globalObject, code, jsString(vm, message), jsUndefined());
|
||||
}
|
||||
|
||||
JSObject* createError(VM& vm, JSC::JSGlobalObject* globalObject, NodeErrorCode code, JSValue message)
|
||||
{
|
||||
if (auto* zigGlobalObject = jsDynamicCast<Zig::GlobalObject*>(globalObject))
|
||||
return createError(vm, zigGlobalObject, code, message, jsUndefined());
|
||||
|
||||
auto* structure = createErrorStructure(vm, globalObject, errors[code].type, errors[code].name, errors[code].code);
|
||||
return JSC::ErrorInstance::create(globalObject, structure, message, jsUndefined(), nullptr, JSC::RuntimeType::TypeNothing, errors[code].type, true);
|
||||
}
|
||||
|
||||
JSC::JSObject* createError(VM& vm, Zig::GlobalObject* globalObject, NodeErrorCode code, JSValue message, JSValue options)
|
||||
{
|
||||
return errorCache(globalObject)->createError(vm, globalObject, code, message, options);
|
||||
}
|
||||
|
||||
JSObject* createError(JSC::JSGlobalObject* globalObject, NodeErrorCode code, const String& message)
|
||||
{
|
||||
auto& vm = globalObject->vm();
|
||||
return createError(vm, globalObject, code, jsString(vm, message));
|
||||
}
|
||||
|
||||
JSObject* createError(Zig::JSGlobalObject* globalObject, NodeErrorCode code, JSC::JSValue message)
|
||||
{
|
||||
auto& vm = globalObject->vm();
|
||||
return createError(vm, globalObject, code, message);
|
||||
}
|
||||
|
||||
WTF::String JSValueToStringSafe(JSC::JSGlobalObject* globalObject, JSValue arg)
|
||||
{
|
||||
ASSERT(!arg.isEmpty());
|
||||
@@ -45,48 +194,6 @@ WTF::String JSValueToStringSafe(JSC::JSGlobalObject* globalObject, JSValue arg)
|
||||
return arg.toString(globalObject)->getString(globalObject);
|
||||
}
|
||||
|
||||
JSC::JSValue createErrorWithCode(JSC::JSGlobalObject* globalObject, String message, ASCIILiteral code)
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
JSC::JSObject* result = JSC::createError(globalObject, message);
|
||||
JSC::EnsureStillAliveScope ensureAlive(result);
|
||||
auto typeError = JSC::JSValue(result).asCell()->getObject();
|
||||
|
||||
auto clientData = WebCore::clientData(vm);
|
||||
typeError->putDirect(vm, clientData->builtinNames().codePublicName(), jsString(vm, String(code)), 0);
|
||||
|
||||
return typeError;
|
||||
}
|
||||
|
||||
JSC::JSValue createTypeErrorWithCode(JSC::JSGlobalObject* globalObject, String message, ASCIILiteral code)
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
JSC::JSObject* result = JSC::createTypeError(globalObject, message);
|
||||
JSC::EnsureStillAliveScope ensureAlive(result);
|
||||
auto typeError = JSC::JSValue(result).asCell()->getObject();
|
||||
|
||||
auto clientData = WebCore::clientData(vm);
|
||||
typeError->putDirect(vm, clientData->builtinNames().codePublicName(), jsString(vm, String(code)), 0);
|
||||
|
||||
return typeError;
|
||||
}
|
||||
|
||||
JSC::JSValue createRangeErrorWithCode(JSC::JSGlobalObject* globalObject, String message, ASCIILiteral code)
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
|
||||
JSC::JSObject* result = JSC::createRangeError(globalObject, message);
|
||||
JSC::EnsureStillAliveScope ensureAlive(result);
|
||||
auto typeError = JSC::JSValue(result).asCell()->getObject();
|
||||
|
||||
auto clientData = WebCore::clientData(vm);
|
||||
typeError->putDirect(vm, clientData->builtinNames().codePublicName(), jsString(vm, String(code)), 0);
|
||||
|
||||
return typeError;
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_INVALID_ARG_TYPE, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
@@ -117,7 +224,7 @@ extern "C" JSC::EncodedJSValue Bun__ERR_INVALID_ARG_TYPE(JSC::JSGlobalObject* gl
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
auto message = makeString("The \""_s, arg_name, "\" argument must be of type "_s, expected_type, ". Received "_s, actual_value);
|
||||
return JSC::JSValue::encode(createTypeErrorWithCode(globalObject, message, "ERR_INVALID_ARG_TYPE"_s));
|
||||
return JSValue::encode(createError(globalObject, NodeErrorCode::ERR_INVALID_ARG_TYPE, message));
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_OUT_OF_RANGE, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
|
||||
@@ -141,17 +248,17 @@ JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_OUT_OF_RANGE, (JSC::JSGlobalObject * glo
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
auto message = makeString("The value of \""_s, arg_name, "\" is out of range. It must be "_s, range, ". Received "_s, input);
|
||||
return JSC::JSValue::encode(createRangeErrorWithCode(globalObject, message, "ERR_OUT_OF_RANGE"_s));
|
||||
return JSC::JSValue::encode(createError(globalObject, NodeErrorCode::ERR_OUT_OF_RANGE, message));
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_IPC_DISCONNECTED, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*))
|
||||
{
|
||||
return JSC::JSValue::encode(createErrorWithCode(globalObject, "IPC channel is already disconnected"_s, "ERR_IPC_DISCONNECTED"_s));
|
||||
return JSC::JSValue::encode(createError(globalObject, NodeErrorCode::ERR_IPC_DISCONNECTED, "IPC channel is already disconnected"_s));
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_SERVER_NOT_RUNNING, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*))
|
||||
{
|
||||
return JSC::JSValue::encode(createErrorWithCode(globalObject, "Server is not running."_s, "ERR_SERVER_NOT_RUNNING"_s));
|
||||
return JSC::JSValue::encode(createError(globalObject, NodeErrorCode::ERR_SERVER_NOT_RUNNING, "Server is not running."_s));
|
||||
}
|
||||
|
||||
extern "C" JSC::EncodedJSValue Bun__ERR_MISSING_ARGS(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue arg1, JSC::EncodedJSValue arg2, JSC::EncodedJSValue arg3)
|
||||
@@ -170,7 +277,7 @@ extern "C" JSC::EncodedJSValue Bun__ERR_MISSING_ARGS(JSC::JSGlobalObject* global
|
||||
if (arg2 == 0) {
|
||||
// 1 arg name passed
|
||||
auto message = makeString("The \""_s, name1, "\" argument must be specified"_s);
|
||||
return JSC::JSValue::encode(createTypeErrorWithCode(globalObject, message, "ERR_MISSING_ARGS"_s));
|
||||
return JSC::JSValue::encode(createError(globalObject, NodeErrorCode::ERR_MISSING_ARGS, message));
|
||||
}
|
||||
|
||||
auto name2 = JSValue::decode(arg2).toWTFString(globalObject);
|
||||
@@ -179,7 +286,7 @@ extern "C" JSC::EncodedJSValue Bun__ERR_MISSING_ARGS(JSC::JSGlobalObject* global
|
||||
if (arg3 == 0) {
|
||||
// 2 arg names passed
|
||||
auto message = makeString("The \""_s, name1, "\" and \""_s, name2, "\" arguments must be specified"_s);
|
||||
return JSC::JSValue::encode(createTypeErrorWithCode(globalObject, message, "ERR_MISSING_ARGS"_s));
|
||||
return JSC::JSValue::encode(createError(globalObject, NodeErrorCode::ERR_MISSING_ARGS, message));
|
||||
}
|
||||
|
||||
auto name3 = JSValue::decode(arg3).toWTFString(globalObject);
|
||||
@@ -187,7 +294,7 @@ extern "C" JSC::EncodedJSValue Bun__ERR_MISSING_ARGS(JSC::JSGlobalObject* global
|
||||
|
||||
// 3 arg names passed
|
||||
auto message = makeString("The \""_s, name1, "\", \""_s, name2, "\", and \""_s, name3, "\" arguments must be specified"_s);
|
||||
return JSC::JSValue::encode(createTypeErrorWithCode(globalObject, message, "ERR_MISSING_ARGS"_s));
|
||||
return JSC::JSValue::encode(createError(globalObject, NodeErrorCode::ERR_MISSING_ARGS, message));
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_IPC_CHANNEL_CLOSED, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*))
|
||||
@@ -196,12 +303,78 @@ JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_IPC_CHANNEL_CLOSED, (JSC::JSGlobalObject
|
||||
}
|
||||
extern "C" JSC::EncodedJSValue Bun__ERR_IPC_CHANNEL_CLOSED(JSC::JSGlobalObject* globalObject)
|
||||
{
|
||||
return JSC::JSValue::encode(createErrorWithCode(globalObject, "Channel closed."_s, "ERR_IPC_CHANNEL_CLOSED"_s));
|
||||
return JSC::JSValue::encode(createError(globalObject, NodeErrorCode::ERR_IPC_CHANNEL_CLOSED, "Channel closed."_s));
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_SOCKET_BAD_TYPE, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*))
|
||||
{
|
||||
return JSC::JSValue::encode(createTypeErrorWithCode(globalObject, "Bad socket type specified. Valid types are: udp4, udp6"_s, "ERR_SOCKET_BAD_TYPE"_s));
|
||||
return JSC::JSValue::encode(createError(globalObject, NodeErrorCode::ERR_SOCKET_BAD_TYPE, "Bad socket type specified. Valid types are: udp4, udp6"_s));
|
||||
}
|
||||
|
||||
} // namespace Bun
|
||||
|
||||
JSC::JSValue WebCore::toJS(JSC::JSGlobalObject* globalObject, CacheableAbortReason abortReason)
|
||||
{
|
||||
if (auto* zigGlobal = jsDynamicCast<Zig::GlobalObject*>(globalObject)) {
|
||||
auto* cache = Bun::errorCache(zigGlobal);
|
||||
if (abortReason.shouldCache()) {
|
||||
if (cache->m_cacheableAbortReason.m_identifier == abortReason.identifier() && cache->m_cacheableAbortReason.m_reason == abortReason.reason()) {
|
||||
return cache->m_cachedReason.get();
|
||||
}
|
||||
}
|
||||
|
||||
JSValue reason = toJS(globalObject, abortReason.reason());
|
||||
|
||||
if (abortReason.shouldCache()) {
|
||||
auto& vm = globalObject->vm();
|
||||
cache->m_cacheableAbortReason = abortReason;
|
||||
cache->m_cachedReason.set(vm, cache, reason);
|
||||
}
|
||||
|
||||
return reason;
|
||||
}
|
||||
|
||||
return toJS(globalObject, abortReason.reason());
|
||||
}
|
||||
|
||||
JSC::JSValue WebCore::toJS(JSC::JSGlobalObject* globalObject, CommonAbortReason abortReason)
|
||||
{
|
||||
switch (abortReason) {
|
||||
case CommonAbortReason::Timeout: {
|
||||
return createError(globalObject, Bun::NodeErrorCode::ABORT_ERR, "The operation timed out"_s);
|
||||
}
|
||||
case CommonAbortReason::UserAbort: {
|
||||
return createError(globalObject, Bun::NodeErrorCode::ABORT_ERR, "The operation was aborted by the user"_s);
|
||||
}
|
||||
case CommonAbortReason::ConnectionClosed: {
|
||||
return createError(globalObject, Bun::NodeErrorCode::ABORT_ERR, "The connection was closed"_s);
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
RELEASE_ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
extern "C" JSC::EncodedJSValue WebCore__CommonAbortReason__toJS(JSC::JSGlobalObject* globalObject, WebCore::CommonAbortReason abortReason)
|
||||
{
|
||||
return JSC::JSValue::encode(WebCore::toJS(globalObject, abortReason));
|
||||
}
|
||||
extern "C" JSC::EncodedJSValue WebCore__CommonAbortReason__toJSCached(JSC::JSGlobalObject* globalObject, WebCore::CommonAbortReason abortReason, size_t id)
|
||||
{
|
||||
return JSC::JSValue::encode(WebCore::toJS(globalObject, { id, abortReason }));
|
||||
}
|
||||
|
||||
extern "C" void WebCore__CommonAbortReason__bustCached(JSC::JSGlobalObject* globalObject, WebCore::CommonAbortReason abortReason, size_t id)
|
||||
{
|
||||
if (auto* zigGlobal = jsDynamicCast<Zig::GlobalObject*>(globalObject)) {
|
||||
if (zigGlobal->m_nodeErrorCache.isInitialized()) {
|
||||
auto* cache = Bun::errorCache(zigGlobal);
|
||||
if (cache->m_cacheableAbortReason.m_identifier == id) {
|
||||
cache->m_cacheableAbortReason = { 0, WebCore::CommonAbortReason::None };
|
||||
cache->m_cachedReason.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include "AbortSignal.h"
|
||||
#include "JavaScriptCore/WriteBarrier.h"
|
||||
#include "ZigGlobalObject.h"
|
||||
#include "root.h"
|
||||
#include <JavaScriptCore/JSInternalFieldObjectImpl.h>
|
||||
#include <JavaScriptCore/JSInternalFieldObjectImplInlines.h>
|
||||
#include "BunClientData.h"
|
||||
|
||||
namespace Bun {
|
||||
|
||||
// clang-format off
|
||||
#define FOR_EACH_NODE_ERROR_WITH_CODE(macro) \
|
||||
macro(TypeError, TypeError, ERR_INVALID_ARG_TYPE) \
|
||||
macro(RangeError, RangeError, ERR_OUT_OF_RANGE) \
|
||||
macro(Error, Error, ERR_IPC_DISCONNECTED) \
|
||||
macro(Error, Error, ERR_SERVER_NOT_RUNNING) \
|
||||
macro(TypeError, TypeError, ERR_MISSING_ARGS) \
|
||||
macro(Error, Error, ERR_IPC_CHANNEL_CLOSED) \
|
||||
macro(TypeError, TypeError, ERR_SOCKET_BAD_TYPE) \
|
||||
macro(Error, AbortError, ABORT_ERR)
|
||||
|
||||
|
||||
#define COUNT_ERROR_WITH_CODE_ENUM(E, name, code) +1
|
||||
static constexpr size_t NODE_ERROR_COUNT = 0 FOR_EACH_NODE_ERROR_WITH_CODE(COUNT_ERROR_WITH_CODE_ENUM);
|
||||
#undef COUNT_ERROR_WITH_CODE_ENUM
|
||||
using namespace JSC;
|
||||
// clang-format on
|
||||
|
||||
enum NodeErrorCode : uint8_t {
|
||||
#define DECLARE_ERROR_WITH_CODE_ENUM(E, name, code) code,
|
||||
FOR_EACH_NODE_ERROR_WITH_CODE(DECLARE_ERROR_WITH_CODE_ENUM)
|
||||
#undef DECLARE_ERROR_WITH_CODE_ENUM
|
||||
};
|
||||
|
||||
class NodeErrorCache : public JSC::JSInternalFieldObjectImpl<NODE_ERROR_COUNT> {
|
||||
public:
|
||||
using Base = JSInternalFieldObjectImpl<NODE_ERROR_COUNT>;
|
||||
using Field = NodeErrorCode;
|
||||
|
||||
DECLARE_EXPORT_INFO;
|
||||
|
||||
static size_t allocationSize(Checked<size_t> inlineCapacity)
|
||||
{
|
||||
ASSERT_UNUSED(inlineCapacity, inlineCapacity == 0U);
|
||||
return sizeof(NodeErrorCache);
|
||||
}
|
||||
|
||||
template<typename, SubspaceAccess mode>
|
||||
static GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
|
||||
{
|
||||
if constexpr (mode == JSC::SubspaceAccess::Concurrently)
|
||||
return nullptr;
|
||||
return WebCore::subspaceForImpl<NodeErrorCache, WebCore::UseCustomHeapCellType::No>(
|
||||
vm,
|
||||
[](auto& spaces) { return spaces.m_clientSubspaceForNodeErrors.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_clientSubspaceForNodeErrors = std::forward<decltype(space)>(space); },
|
||||
[](auto& spaces) { return spaces.m_subspaceForNodeErrors.get(); },
|
||||
[](auto& spaces, auto&& space) { spaces.m_subspaceForNodeErrors = std::forward<decltype(space)>(space); });
|
||||
}
|
||||
|
||||
static NodeErrorCache* create(VM& vm, Structure* structure);
|
||||
static Structure* createStructure(VM& vm, JSGlobalObject* globalObject);
|
||||
|
||||
JSObject* createError(VM& vm, Zig::GlobalObject* globalObject, NodeErrorCode code, JSValue message, JSValue options);
|
||||
|
||||
CacheableAbortReason m_cacheableAbortReason { 0, CommonAbortReason::None };
|
||||
mutable WriteBarrier<Unknown> m_cachedReason;
|
||||
|
||||
private:
|
||||
JS_EXPORT_PRIVATE NodeErrorCache(VM&, Structure*);
|
||||
DECLARE_VISIT_CHILDREN_WITH_MODIFIER(JS_EXPORT_PRIVATE);
|
||||
void finishCreation(VM&);
|
||||
};
|
||||
|
||||
JSC::JSObject* createError(Zig::GlobalObject* globalObject, NodeErrorCode code, const WTF::String& message);
|
||||
JSC::JSObject* createError(JSC::JSGlobalObject* globalObject, NodeErrorCode code, const WTF::String& message);
|
||||
JSC::JSObject* createError(Zig::GlobalObject* globalObject, NodeErrorCode code, JSC::JSValue message);
|
||||
JSC::JSObject* createError(VM& vm, Zig::GlobalObject* globalObject, NodeErrorCode code, JSValue message, JSValue options = jsUndefined());
|
||||
JSC::JSValue toJS(JSC::JSGlobalObject*, NodeErrorCode);
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_INVALID_ARG_TYPE, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame));
|
||||
JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_OUT_OF_RANGE, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame));
|
||||
JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_IPC_DISCONNECTED, (JSC::JSGlobalObject * globalObject, JSC::CallFrame*));
|
||||
|
||||
@@ -146,6 +146,7 @@
|
||||
#include "UtilInspect.h"
|
||||
#include "Base64Helpers.h"
|
||||
#include "wtf/text/OrdinalNumber.h"
|
||||
#include "NodeError.h"
|
||||
|
||||
#if ENABLE(REMOTE_INSPECTOR)
|
||||
#include "JavaScriptCore/RemoteInspectorServer.h"
|
||||
@@ -2695,6 +2696,15 @@ void GlobalObject::finishCreation(VM& vm)
|
||||
init.set(Bun::createUtilInspectOptionsStructure(init.vm, init.owner));
|
||||
});
|
||||
|
||||
m_nodeErrorCache.initLater(
|
||||
[](const Initializer<JSObject>& init) {
|
||||
auto* structure = NodeErrorCache::createStructure(
|
||||
init.vm,
|
||||
init.owner);
|
||||
|
||||
init.set(NodeErrorCache::create(init.vm, structure));
|
||||
});
|
||||
|
||||
m_utilInspectStylizeColorFunction.initLater(
|
||||
[](const Initializer<JSFunction>& init) {
|
||||
JSC::MarkedArgumentBuffer args;
|
||||
@@ -3553,6 +3563,8 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
|
||||
thisObject->mockModule.mockWithImplementationCleanupDataStructure.visit(visitor);
|
||||
thisObject->mockModule.withImplementationCleanupFunction.visit(visitor);
|
||||
|
||||
thisObject->m_nodeErrorCache.visit(visitor);
|
||||
|
||||
for (auto& barrier : thisObject->m_thenables) {
|
||||
visitor.append(barrier);
|
||||
}
|
||||
|
||||
@@ -402,6 +402,9 @@ public:
|
||||
//
|
||||
LazyProperty<JSGlobalObject, JSC::JSFunction> m_errorConstructorPrepareStackTraceInternalValue;
|
||||
|
||||
LazyProperty<JSGlobalObject, JSObject> m_nodeErrorCache;
|
||||
JSObject* nodeErrorCache() const { return m_nodeErrorCache.getInitializedOnMainThread(this); }
|
||||
|
||||
Structure* memoryFootprintStructure()
|
||||
{
|
||||
return m_memoryFootprintStructure.getInitializedOnMainThread(this);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "AbortSignal.h"
|
||||
#include "root.h"
|
||||
#include "JavaScriptCore/JSCast.h"
|
||||
#include "JavaScriptCore/JSType.h"
|
||||
@@ -5484,11 +5485,14 @@ extern "C" JSC__JSValue WebCore__AbortSignal__toJS(WebCore__AbortSignal* arg0, J
|
||||
return JSValue::encode(toJS<IDLInterface<WebCore__AbortSignal>>(*globalObject, *jsCast<JSDOMGlobalObject*>(globalObject), *abortSignal));
|
||||
}
|
||||
|
||||
extern "C" WebCore__AbortSignal* WebCore__AbortSignal__signal(WebCore__AbortSignal* arg0, JSC__JSValue JSValue1)
|
||||
extern "C" WebCore__AbortSignal* WebCore__AbortSignal__signal(WebCore__AbortSignal* arg0, JSC::JSGlobalObject* globalObject, WebCore::CommonAbortReason reason, uint32_t id)
|
||||
{
|
||||
|
||||
WebCore::AbortSignal* abortSignal = reinterpret_cast<WebCore::AbortSignal*>(arg0);
|
||||
abortSignal->signalAbort(JSC::JSValue::decode(JSValue1));
|
||||
abortSignal->signalAbort(
|
||||
globalObject,
|
||||
{ id, reason });
|
||||
;
|
||||
return arg0;
|
||||
}
|
||||
|
||||
@@ -5547,49 +5551,6 @@ extern "C" WebCore__AbortSignal* WebCore__AbortSignal__fromJS(JSC__JSValue value
|
||||
|
||||
return reinterpret_cast<WebCore__AbortSignal*>(&object->wrapped());
|
||||
}
|
||||
static auto ABORT_ERROR_NAME = MAKE_STATIC_STRING_IMPL("AbortError");
|
||||
extern "C" JSC__JSValue WebCore__AbortSignal__createAbortError(const ZigString* message, const ZigString* arg1,
|
||||
JSC__JSGlobalObject* globalObject)
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
ZigString code = *arg1;
|
||||
JSC::JSObject* error = Zig::getErrorInstance(message, globalObject).asCell()->getObject();
|
||||
|
||||
error->putDirect(
|
||||
vm, vm.propertyNames->name,
|
||||
JSC::JSValue(JSC::jsOwnedString(vm, ABORT_ERROR_NAME)),
|
||||
0);
|
||||
|
||||
if (code.len > 0) {
|
||||
auto clientData = WebCore::clientData(vm);
|
||||
JSC::JSValue codeValue = Zig::toJSStringValue(code, globalObject);
|
||||
error->putDirect(vm, clientData->builtinNames().codePublicName(), codeValue, 0);
|
||||
}
|
||||
|
||||
return JSC::JSValue::encode(error);
|
||||
}
|
||||
|
||||
static auto TIMEOUT_ERROR_NAME = MAKE_STATIC_STRING_IMPL("TimeoutError");
|
||||
extern "C" JSC__JSValue WebCore__AbortSignal__createTimeoutError(const ZigString* message, const ZigString* arg1,
|
||||
JSC__JSGlobalObject* globalObject)
|
||||
{
|
||||
JSC::VM& vm = globalObject->vm();
|
||||
ZigString code = *arg1;
|
||||
JSC::JSObject* error = Zig::getErrorInstance(message, globalObject).asCell()->getObject();
|
||||
|
||||
error->putDirect(
|
||||
vm, vm.propertyNames->name,
|
||||
JSC::JSValue(JSC::jsOwnedString(vm, TIMEOUT_ERROR_NAME)),
|
||||
0);
|
||||
|
||||
if (code.len > 0) {
|
||||
auto clientData = WebCore::clientData(vm);
|
||||
JSC::JSValue codeValue = Zig::toJSStringValue(code, globalObject);
|
||||
error->putDirect(vm, clientData->builtinNames().codePublicName(), codeValue, 0);
|
||||
}
|
||||
|
||||
return JSC::JSValue::encode(error);
|
||||
}
|
||||
|
||||
CPP_DECL double JSC__JSValue__getUnixTimestamp(JSC__JSValue timeValue)
|
||||
{
|
||||
|
||||
@@ -2003,6 +2003,45 @@ pub fn PromiseCallback(comptime Type: type, comptime CallbackFunction: fn (*Type
|
||||
}.callback;
|
||||
}
|
||||
|
||||
// These objects are extremely common.
|
||||
pub const CommonAbortReason = enum(u8) {
|
||||
Timeout = 1,
|
||||
UserAbort = 2,
|
||||
ConnectionClosed = 3,
|
||||
|
||||
pub fn toJS(this: CommonAbortReason, global: *JSGlobalObject) JSValue {
|
||||
return WebCore__CommonAbortReason__toJS(global, this);
|
||||
}
|
||||
|
||||
pub const Cacheable = union(CommonAbortReason) {
|
||||
Timeout: ID,
|
||||
UserAbort: ID,
|
||||
ConnectionClosed: ID,
|
||||
|
||||
pub const ID = usize;
|
||||
|
||||
// Using the u48 size is generally pretty slow and won't work across C ABI.
|
||||
pub const SizedID = u48;
|
||||
pub const max_id = std.math.maxInt(SizedID) - 1;
|
||||
|
||||
pub fn toJS(this: Cacheable, global: *JSGlobalObject) JSValue {
|
||||
return switch (this) {
|
||||
inline else => |x| WebCore__CommonAbortReason__toJSCached(global, this, x),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn bust(this: Cacheable, globalObject: *JSC.JSGlobalObject) void {
|
||||
switch (this) {
|
||||
inline else => |x| WebCore__CommonAbortReason__bustCached(globalObject, this, x),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
extern fn WebCore__CommonAbortReason__toJS(*JSGlobalObject, CommonAbortReason) JSValue;
|
||||
extern fn WebCore__CommonAbortReason__toJSCached(*JSGlobalObject, CommonAbortReason, Cacheable.ID) JSValue;
|
||||
extern fn WebCore__CommonAbortReason__bustCached(*JSGlobalObject, CommonAbortReason, Cacheable.ID) void;
|
||||
};
|
||||
|
||||
pub const AbortSignal = extern opaque {
|
||||
pub const shim = Shimmer("WebCore", "AbortSignal", @This());
|
||||
const cppFn = shim.cppFn;
|
||||
@@ -2042,12 +2081,22 @@ pub const AbortSignal = extern opaque {
|
||||
return cppFn("cleanNativeBindings", .{ this, ctx });
|
||||
}
|
||||
|
||||
extern fn WebCore__AbortSignal__signal(*AbortSignal, *JSC.JSGlobalObject, CommonAbortReason, CommonAbortReason.Cacheable.ID) void;
|
||||
|
||||
pub fn signal(
|
||||
this: *AbortSignal,
|
||||
reason: JSValue,
|
||||
) *AbortSignal {
|
||||
globalObject: *JSC.JSGlobalObject,
|
||||
reason: CommonAbortReason.Cacheable,
|
||||
) void {
|
||||
bun.Analytics.Features.abort_signal += 1;
|
||||
return cppFn("signal", .{ this, reason });
|
||||
return WebCore__AbortSignal__signal(
|
||||
this,
|
||||
globalObject,
|
||||
reason,
|
||||
switch (reason) {
|
||||
inline else => |x| x,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// This function is not threadsafe. aborted is a boolean, not an atomic!
|
||||
@@ -2095,15 +2144,7 @@ pub const AbortSignal = extern opaque {
|
||||
return WebCore__AbortSignal__new(global);
|
||||
}
|
||||
|
||||
pub fn createAbortError(message: *const ZigString, code: *const ZigString, global: *JSGlobalObject) JSValue {
|
||||
return cppFn("createAbortError", .{ message, code, global });
|
||||
}
|
||||
|
||||
pub fn createTimeoutError(message: *const ZigString, code: *const ZigString, global: *JSGlobalObject) JSValue {
|
||||
return cppFn("createTimeoutError", .{ message, code, global });
|
||||
}
|
||||
|
||||
pub const Extern = [_][]const u8{ "createAbortError", "createTimeoutError", "create", "ref", "unref", "signal", "abortReason", "aborted", "addListener", "fromJS", "toJS", "cleanNativeBindings" };
|
||||
pub const Extern = [_][]const u8{ "create", "ref", "unref", "signal", "abortReason", "aborted", "addListener", "fromJS", "toJS", "cleanNativeBindings" };
|
||||
};
|
||||
|
||||
pub const JSPromise = extern struct {
|
||||
|
||||
3
src/bun.js/bindings/headers.h
generated
3
src/bun.js/bindings/headers.h
generated
@@ -231,11 +231,8 @@ CPP_DECL JSC__JSValue WebCore__AbortSignal__abortReason(WebCore__AbortSignal* ar
|
||||
CPP_DECL WebCore__AbortSignal* WebCore__AbortSignal__addListener(WebCore__AbortSignal* arg0, void* arg1, void(* ArgFn2)(void* arg0, JSC__JSValue JSValue1));
|
||||
CPP_DECL void WebCore__AbortSignal__cleanNativeBindings(WebCore__AbortSignal* arg0, void* arg1);
|
||||
CPP_DECL JSC__JSValue WebCore__AbortSignal__create(JSC__JSGlobalObject* arg0);
|
||||
CPP_DECL JSC__JSValue WebCore__AbortSignal__createAbortError(const ZigString* arg0, const ZigString* arg1, JSC__JSGlobalObject* arg2);
|
||||
CPP_DECL JSC__JSValue WebCore__AbortSignal__createTimeoutError(const ZigString* arg0, const ZigString* arg1, JSC__JSGlobalObject* arg2);
|
||||
CPP_DECL WebCore__AbortSignal* WebCore__AbortSignal__fromJS(JSC__JSValue JSValue0);
|
||||
CPP_DECL WebCore__AbortSignal* WebCore__AbortSignal__ref(WebCore__AbortSignal* arg0);
|
||||
CPP_DECL WebCore__AbortSignal* WebCore__AbortSignal__signal(WebCore__AbortSignal* arg0, JSC__JSValue JSValue1);
|
||||
CPP_DECL JSC__JSValue WebCore__AbortSignal__toJS(WebCore__AbortSignal* arg0, JSC__JSGlobalObject* arg1);
|
||||
CPP_DECL void WebCore__AbortSignal__unref(WebCore__AbortSignal* arg0);
|
||||
|
||||
|
||||
3
src/bun.js/bindings/headers.zig
generated
3
src/bun.js/bindings/headers.zig
generated
@@ -143,11 +143,8 @@ pub extern fn WebCore__AbortSignal__abortReason(arg0: ?*bindings.AbortSignal) JS
|
||||
pub extern fn WebCore__AbortSignal__addListener(arg0: ?*bindings.AbortSignal, arg1: ?*anyopaque, ArgFn2: ?*const fn (?*anyopaque, JSC__JSValue) callconv(.C) void) ?*bindings.AbortSignal;
|
||||
pub extern fn WebCore__AbortSignal__cleanNativeBindings(arg0: ?*bindings.AbortSignal, arg1: ?*anyopaque) void;
|
||||
pub extern fn WebCore__AbortSignal__create(arg0: *bindings.JSGlobalObject) JSC__JSValue;
|
||||
pub extern fn WebCore__AbortSignal__createAbortError(arg0: [*c]const ZigString, arg1: [*c]const ZigString, arg2: *bindings.JSGlobalObject) JSC__JSValue;
|
||||
pub extern fn WebCore__AbortSignal__createTimeoutError(arg0: [*c]const ZigString, arg1: [*c]const ZigString, arg2: *bindings.JSGlobalObject) JSC__JSValue;
|
||||
pub extern fn WebCore__AbortSignal__fromJS(JSValue0: JSC__JSValue) ?*bindings.AbortSignal;
|
||||
pub extern fn WebCore__AbortSignal__ref(arg0: ?*bindings.AbortSignal) ?*bindings.AbortSignal;
|
||||
pub extern fn WebCore__AbortSignal__signal(arg0: ?*bindings.AbortSignal, JSValue1: JSC__JSValue) ?*bindings.AbortSignal;
|
||||
pub extern fn WebCore__AbortSignal__toJS(arg0: ?*bindings.AbortSignal, arg1: *bindings.JSGlobalObject) JSC__JSValue;
|
||||
pub extern fn WebCore__AbortSignal__unref(arg0: ?*bindings.AbortSignal) void;
|
||||
pub extern fn JSC__JSPromise__asValue(arg0: ?*bindings.JSPromise, arg1: *bindings.JSGlobalObject) JSC__JSValue;
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "Event.h"
|
||||
#include "EventNames.h"
|
||||
#include "JSDOMException.h"
|
||||
#include "JavaScriptCore/JSCJSValue.h"
|
||||
#include "ScriptExecutionContext.h"
|
||||
#include "WebCoreOpaqueRoot.h"
|
||||
#include "wtf/DebugHeap.h"
|
||||
@@ -112,6 +113,20 @@ AbortSignal::AbortSignal(ScriptExecutionContext* context, Aborted aborted, JSC::
|
||||
|
||||
AbortSignal::~AbortSignal() = default;
|
||||
|
||||
JSValue AbortSignal::jsReason(JSC::JSGlobalObject& globalObject)
|
||||
{
|
||||
JSValue existingValue = m_reason.getValue(jsUndefined());
|
||||
if (existingValue.isUndefined()) {
|
||||
if (m_cacheableReason.reason() != CommonAbortReason::None) {
|
||||
existingValue = toJS(&globalObject, m_cacheableReason);
|
||||
m_cacheableReason = { 0, CommonAbortReason::None };
|
||||
m_reason.setWeakly(existingValue);
|
||||
}
|
||||
}
|
||||
|
||||
return existingValue;
|
||||
}
|
||||
|
||||
void AbortSignal::addSourceSignal(AbortSignal& signal)
|
||||
{
|
||||
if (signal.isDependent()) {
|
||||
@@ -164,6 +179,26 @@ void AbortSignal::signalAbort(JSC::JSValue reason)
|
||||
dependentSignal->signalAbort(reason);
|
||||
}
|
||||
|
||||
void AbortSignal::signalAbort(JSC::JSGlobalObject* globalObject, CommonAbortReason reason)
|
||||
{
|
||||
// 1. If signal's aborted flag is set, then return.
|
||||
if (m_aborted)
|
||||
return;
|
||||
|
||||
m_cacheableReason = { 0, reason };
|
||||
signalAbort(toJS(globalObject, reason));
|
||||
}
|
||||
|
||||
void AbortSignal::signalAbort(JSC::JSGlobalObject* globalObject, CacheableAbortReason reason)
|
||||
{
|
||||
// 1. If signal's aborted flag is set, then return.
|
||||
if (m_aborted)
|
||||
return;
|
||||
|
||||
m_cacheableReason = reason;
|
||||
signalAbort(toJS(globalObject, reason));
|
||||
}
|
||||
|
||||
void AbortSignal::cleanNativeBindings(void* ref)
|
||||
{
|
||||
auto callbacks = std::exchange(m_native_callbacks, {});
|
||||
@@ -183,7 +218,7 @@ void AbortSignal::signalFollow(AbortSignal& signal)
|
||||
return;
|
||||
|
||||
if (signal.aborted()) {
|
||||
signalAbort(signal.reason().getValue());
|
||||
signalAbort(signal.jsReason(*scriptExecutionContext()->jsGlobalObject()));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -203,7 +238,8 @@ void AbortSignal::eventListenersDidChange()
|
||||
uint32_t AbortSignal::addAbortAlgorithmToSignal(AbortSignal& signal, Ref<AbortAlgorithm>&& algorithm)
|
||||
{
|
||||
if (signal.aborted()) {
|
||||
algorithm->handleEvent(signal.m_reason.getValue());
|
||||
// TODO: Null check.
|
||||
algorithm->handleEvent(signal.jsReason(*signal.scriptExecutionContext()->jsGlobalObject()));
|
||||
return 0;
|
||||
}
|
||||
return signal.addAlgorithm([algorithm = WTFMove(algorithm)](JSC::JSValue value) mutable {
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include "ContextDestructionObserver.h"
|
||||
#include "EventTarget.h"
|
||||
#include "JSValueInWrappedObject.h"
|
||||
#include "JavaScriptCore/JSGlobalObject.h"
|
||||
#include "ZigGlobalObject.h"
|
||||
#include "wtf/DebugHeap.h"
|
||||
#include "wtf/FastMalloc.h"
|
||||
#include <wtf/Function.h>
|
||||
@@ -46,6 +48,33 @@ class WebCoreOpaqueRoot;
|
||||
|
||||
DECLARE_ALLOCATOR_WITH_HEAP_IDENTIFIER(AbortSignal);
|
||||
|
||||
enum class CommonAbortReason : uint8_t {
|
||||
None,
|
||||
Timeout,
|
||||
UserAbort,
|
||||
ConnectionClosed,
|
||||
};
|
||||
|
||||
class CacheableAbortReason {
|
||||
public:
|
||||
CacheableAbortReason() = default;
|
||||
CacheableAbortReason(size_t identifier = 0, CommonAbortReason reason = CommonAbortReason::None)
|
||||
: m_reason(reason)
|
||||
, m_identifier(identifier)
|
||||
{
|
||||
}
|
||||
|
||||
size_t identifier() const { return m_identifier; }
|
||||
CommonAbortReason reason() const { return m_reason; }
|
||||
bool shouldCache() const { return m_identifier > 0; }
|
||||
|
||||
size_t m_identifier = 0;
|
||||
CommonAbortReason m_reason { CommonAbortReason::None };
|
||||
};
|
||||
|
||||
JSC::JSValue toJS(JSC::JSGlobalObject*, CommonAbortReason);
|
||||
JSC::JSValue toJS(JSC::JSGlobalObject* globalObject, CacheableAbortReason abortReason);
|
||||
|
||||
class AbortSignal final : public RefCounted<AbortSignal>, public EventTargetWithInlineData, private ContextDestructionObserver {
|
||||
WTF_MAKE_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(AbortSignal);
|
||||
|
||||
@@ -61,11 +90,14 @@ public:
|
||||
static uint32_t addAbortAlgorithmToSignal(AbortSignal&, Ref<AbortAlgorithm>&&);
|
||||
static void removeAbortAlgorithmFromSignal(AbortSignal&, uint32_t algorithmIdentifier);
|
||||
|
||||
void signalAbort(JSC::JSGlobalObject* globalObject, CacheableAbortReason reason);
|
||||
void signalAbort(JSC::JSGlobalObject* globalObject, CommonAbortReason reason);
|
||||
void signalAbort(JSC::JSValue reason);
|
||||
void signalFollow(AbortSignal&);
|
||||
|
||||
bool aborted() const { return m_aborted; }
|
||||
const JSValueInWrappedObject& reason() const { return m_reason; }
|
||||
JSValue jsReason(JSC::JSGlobalObject& globalObject);
|
||||
|
||||
void cleanNativeBindings(void* ref);
|
||||
void addNativeCallback(NativeCallbackTuple callback) { m_native_callbacks.append(callback); }
|
||||
@@ -89,8 +121,10 @@ public:
|
||||
AbortSignalSet& sourceSignals() { return m_sourceSignals; }
|
||||
|
||||
private:
|
||||
enum class Aborted : bool { No,
|
||||
Yes };
|
||||
enum class Aborted : bool {
|
||||
No,
|
||||
Yes
|
||||
};
|
||||
explicit AbortSignal(ScriptExecutionContext*, Aborted = Aborted::No, JSC::JSValue reason = JSC::jsUndefined());
|
||||
|
||||
void setHasActiveTimeoutTimer(bool hasActiveTimeoutTimer) { m_hasActiveTimeoutTimer = hasActiveTimeoutTimer; }
|
||||
@@ -112,12 +146,13 @@ private:
|
||||
AbortSignalSet m_sourceSignals;
|
||||
AbortSignalSet m_dependentSignals;
|
||||
JSValueInWrappedObject m_reason;
|
||||
CacheableAbortReason m_cacheableReason { 0, CommonAbortReason::None };
|
||||
Vector<NativeCallbackTuple, 2> m_native_callbacks;
|
||||
uint32_t m_algorithmIdentifier { 0 };
|
||||
bool m_aborted { false };
|
||||
bool m_hasActiveTimeoutTimer { false };
|
||||
bool m_hasAbortEventListener { false };
|
||||
bool m_isDependent { false };
|
||||
bool m_aborted : 1 { false };
|
||||
bool m_hasActiveTimeoutTimer : 1 { false };
|
||||
bool m_hasAbortEventListener : 1 { false };
|
||||
bool m_isDependent : 1 { false };
|
||||
};
|
||||
|
||||
WebCoreOpaqueRoot root(AbortSignal*);
|
||||
|
||||
@@ -44,6 +44,7 @@ public:
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForMockWithImplementationCleanupData;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForProcessObject;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForInternalModuleRegistry;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForNodeErrors;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForBunInspectorConnection;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSNextTickQueue;
|
||||
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForNAPIFunction;
|
||||
|
||||
@@ -44,6 +44,7 @@ public:
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForMockWithImplementationCleanupData;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForProcessObject;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForInternalModuleRegistry;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForNodeErrors;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForBunInspectorConnection;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForJSNextTickQueue;
|
||||
std::unique_ptr<IsoSubspace> m_subspaceForNAPIFunction;
|
||||
|
||||
@@ -225,7 +225,7 @@ static inline JSValue jsAbortSignal_reasonGetter(JSGlobalObject& lexicalGlobalOb
|
||||
auto& vm = JSC::getVM(&lexicalGlobalObject);
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
auto& impl = thisObject.wrapped();
|
||||
RELEASE_AND_RETURN(throwScope, (toJS<IDLAny>(lexicalGlobalObject, throwScope, impl.reason())));
|
||||
RELEASE_AND_RETURN(throwScope, (toJS<IDLAny>(lexicalGlobalObject, throwScope, impl.jsReason(lexicalGlobalObject))));
|
||||
}
|
||||
|
||||
JSC_DEFINE_CUSTOM_GETTER(jsAbortSignal_reason, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
|
||||
|
||||
@@ -535,7 +535,7 @@ pub const FSWatcher = struct {
|
||||
listener.ensureStillAlive();
|
||||
var args = [_]JSC.JSValue{
|
||||
EventType.@"error".toJS(this.globalThis),
|
||||
if (err.isEmptyOrUndefinedOrNull()) JSC.WebCore.AbortSignal.createAbortError(JSC.ZigString.static("The user aborted a request"), &JSC.ZigString.Empty, this.globalThis) else err,
|
||||
JSC.CommonAbortReason.UserAbort.toJS(this.globalThis),
|
||||
};
|
||||
_ = listener.callWithGlobalThis(
|
||||
this.globalThis,
|
||||
|
||||
@@ -315,16 +315,35 @@ pub const Body = struct {
|
||||
|
||||
pub const heap_breakdown_label = "BodyValue";
|
||||
pub const ValueError = union(enum) {
|
||||
Aborted: void,
|
||||
Timeout: void,
|
||||
Aborted: JSC.CommonAbortReason.Cacheable.ID,
|
||||
ConnectionClosed: JSC.CommonAbortReason.Cacheable.ID,
|
||||
Timeout: JSC.CommonAbortReason.Cacheable.ID,
|
||||
SystemError: JSC.SystemError,
|
||||
Message: bun.String,
|
||||
JSValue: JSC.Strong,
|
||||
|
||||
pub fn toStreamError(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.WebCore.StreamResult.StreamError {
|
||||
return switch (this.*) {
|
||||
.Aborted => |id| .{
|
||||
.AbortReason = .{ .UserAbort = id },
|
||||
},
|
||||
.ConnectionClosed => |id| .{
|
||||
.AbortReason = .{ .ConnectionClosed = id },
|
||||
},
|
||||
.Timeout => |id| .{
|
||||
.AbortReason = .{ .Timeout = id },
|
||||
},
|
||||
else => .{
|
||||
.JSValue = this.toJS(globalObject),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn toJS(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
|
||||
const js_value = switch (this.*) {
|
||||
.Timeout => JSC.WebCore.AbortSignal.createTimeoutError(JSC.ZigString.static("The operation timed out"), &JSC.ZigString.Empty, globalObject),
|
||||
.Aborted => JSC.WebCore.AbortSignal.createAbortError(JSC.ZigString.static("The user aborted a request"), &JSC.ZigString.Empty, globalObject),
|
||||
.Timeout => |id| JSC.CommonAbortReason.Cacheable.toJS(.{ .Timeout = id }, globalObject),
|
||||
.ConnectionClosed => |id| JSC.CommonAbortReason.Cacheable.toJS(.{ .ConnectionClosed = id }, globalObject),
|
||||
.Aborted => |id| JSC.CommonAbortReason.Cacheable.toJS(.{ .UserAbort = id }, globalObject),
|
||||
.SystemError => |system_error| system_error.toErrorInstance(globalObject),
|
||||
.Message => |message| message.toErrorInstance(globalObject),
|
||||
// do a early return in this case we don't need to create a new Strong
|
||||
@@ -345,7 +364,7 @@ pub const Body = struct {
|
||||
}
|
||||
return .{ .JSValue = .{} };
|
||||
},
|
||||
.Aborted, .Timeout => {},
|
||||
.ConnectionClosed, .Aborted, .Timeout => {},
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -355,7 +374,7 @@ pub const Body = struct {
|
||||
.SystemError => |system_error| system_error.deref(),
|
||||
.Message => |message| message.deref(),
|
||||
.JSValue => this.JSValue.deinit(),
|
||||
.Aborted, .Timeout => {},
|
||||
.ConnectionClosed, .Aborted, .Timeout => {},
|
||||
}
|
||||
// safe empty value after deinit
|
||||
this.* = .{ .JSValue = .{} };
|
||||
@@ -927,12 +946,12 @@ pub const Body = struct {
|
||||
}
|
||||
}
|
||||
|
||||
// The Promise version goes before the ReadableStream version incase the Promise version is used too.
|
||||
// Avoid creating unnecessary duplicate JSValue.
|
||||
if (strong_readable.get()) |readable| {
|
||||
if (readable.ptr == .Bytes) {
|
||||
readable.ptr.Bytes.onData(
|
||||
.{
|
||||
.err = .{ .JSValue = this.Error.toJS(global) },
|
||||
},
|
||||
.{ .err = this.Error.toStreamError(global) },
|
||||
bun.default_allocator,
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -1262,7 +1262,7 @@ pub const Fetch = struct {
|
||||
if (signal.aborted()) {
|
||||
this.abort_reason = signal.abortReason();
|
||||
if (this.abort_reason.isEmptyOrUndefinedOrNull()) {
|
||||
return JSC.WebCore.AbortSignal.createAbortError(JSC.ZigString.static("The user aborted a request"), &JSC.ZigString.Empty, this.global_this);
|
||||
return JSC.CommonAbortReason.UserAbort.toJS(this.global_this);
|
||||
}
|
||||
this.abort_reason.protect();
|
||||
return this.abort_reason;
|
||||
@@ -1281,12 +1281,12 @@ pub const Fetch = struct {
|
||||
|
||||
if (this.result.isTimeout()) {
|
||||
// Timeout without reason
|
||||
return .{ .Timeout = {} };
|
||||
return .{ .Timeout = 0 };
|
||||
}
|
||||
|
||||
if (this.result.isAbort()) {
|
||||
// Abort without reason
|
||||
return .{ .Aborted = {} };
|
||||
return .{ .Aborted = 0 };
|
||||
}
|
||||
|
||||
// some times we don't have metadata so we also check http.url
|
||||
|
||||
@@ -660,7 +660,7 @@ pub const DrainResult = union(enum) {
|
||||
|
||||
pub const StreamResult = union(Tag) {
|
||||
pending: *Pending,
|
||||
err: union(Err) { Error: Syscall.Error, JSValue: JSC.JSValue },
|
||||
err: StreamError,
|
||||
done: void,
|
||||
owned: bun.ByteList,
|
||||
owned_and_done: bun.ByteList,
|
||||
@@ -682,9 +682,32 @@ pub const StreamResult = union(Tag) {
|
||||
}
|
||||
}
|
||||
|
||||
pub const Err = enum {
|
||||
Error,
|
||||
JSValue,
|
||||
pub const StreamError = union(enum) {
|
||||
Error: Syscall.Error,
|
||||
AbortReason: JSC.CommonAbortReason.Cacheable,
|
||||
|
||||
// TODO: use an explicit JSC.Strong here.
|
||||
JSValue: JSC.JSValue,
|
||||
WeakJSValue: JSC.JSValue,
|
||||
|
||||
const WasStrong = enum {
|
||||
Strong,
|
||||
Weak,
|
||||
};
|
||||
|
||||
pub fn toJSWeak(this: *const @This(), globalObject: *JSC.JSGlobalObject) struct { JSC.JSValue, WasStrong } {
|
||||
return switch (this.*) {
|
||||
.Error => |err| {
|
||||
return .{ err.toJSC(globalObject), WasStrong.Weak };
|
||||
},
|
||||
.JSValue => .{ this.JSValue, WasStrong.Strong },
|
||||
.WeakJSValue => .{ this.WeakJSValue, WasStrong.Weak },
|
||||
.AbortReason => |reason| {
|
||||
const value = reason.toJS(globalObject);
|
||||
return .{ value, WasStrong.Weak };
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const Tag = enum {
|
||||
@@ -943,13 +966,12 @@ pub const StreamResult = union(Tag) {
|
||||
defer loop.exit();
|
||||
|
||||
switch (result.*) {
|
||||
.err => |err| {
|
||||
.err => |*err| {
|
||||
const value = brk: {
|
||||
if (err == .Error) break :brk err.Error.toJSC(globalThis);
|
||||
|
||||
const js_err = err.JSValue;
|
||||
const js_err, const was_strong = err.toJSWeak(globalThis);
|
||||
js_err.ensureStillAlive();
|
||||
js_err.unprotect();
|
||||
if (was_strong == .Strong)
|
||||
js_err.unprotect();
|
||||
|
||||
break :brk js_err;
|
||||
};
|
||||
@@ -1010,12 +1032,11 @@ pub const StreamResult = union(Tag) {
|
||||
},
|
||||
|
||||
.err => |err| {
|
||||
if (err == .Error) {
|
||||
return JSC.JSPromise.rejectedPromise(globalThis, JSValue.c(err.Error.toJS(globalThis))).asValue(globalThis);
|
||||
const js_err, const was_strong = err.toJSWeak(globalThis);
|
||||
if (was_strong == .Strong) {
|
||||
js_err.unprotect();
|
||||
}
|
||||
const js_err = err.JSValue;
|
||||
js_err.ensureStillAlive();
|
||||
js_err.unprotect();
|
||||
return JSC.JSPromise.rejectedPromise(globalThis, js_err).asValue(globalThis);
|
||||
},
|
||||
|
||||
@@ -4332,13 +4353,9 @@ pub const ByteStream = struct {
|
||||
|
||||
if (to_copy.len == 0) {
|
||||
if (stream == .err) {
|
||||
if (stream.err == .Error) {
|
||||
this.pending.result = .{ .err = .{ .Error = stream.err.Error } };
|
||||
}
|
||||
const js_err = stream.err.JSValue;
|
||||
js_err.ensureStillAlive();
|
||||
js_err.protect();
|
||||
this.pending.result = .{ .err = .{ .JSValue = js_err } };
|
||||
this.pending.result = .{
|
||||
.err = stream.err,
|
||||
};
|
||||
} else {
|
||||
this.pending.result = .{
|
||||
.done = {},
|
||||
|
||||
Reference in New Issue
Block a user