From 3083718e3f0589bc8b4877e26306791e2c693072 Mon Sep 17 00:00:00 2001 From: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Date: Fri, 27 May 2022 02:51:38 -0700 Subject: [PATCH] faster async/await + readablestream optimizations --- Makefile | 2 + src/feature_flags.zig | 4 +- src/global.zig | 16 ++ src/javascript/jsc/api/bun.zig | 12 +- src/javascript/jsc/api/server.zig | 170 ++++++++++++++---- src/javascript/jsc/api/transpiler.zig | 10 +- src/javascript/jsc/base.zig | 7 +- src/javascript/jsc/bindings/BunBuiltinNames.h | 3 +- src/javascript/jsc/bindings/BunClientData.cpp | 1 - .../ReadableByteStreamInternalsBuiltins.cpp | 75 +++++--- .../ReadableByteStreamInternalsBuiltins.h | 8 + .../jsc/bindings/ReadableStreamBuiltins.cpp | 20 ++- .../ReadableStreamDefaultReaderBuiltins.cpp | 44 +++-- .../ReadableStreamInternalsBuiltins.cpp | 5 +- .../jsc/bindings/ZigGlobalObject.cpp | 24 ++- src/javascript/jsc/bindings/ZigGlobalObject.h | 46 ++++- src/javascript/jsc/bindings/bindings.cpp | 20 ++- src/javascript/jsc/bindings/bindings.zig | 96 ++++++++-- .../js/ReadableByteStreamInternals.js | 64 +++++-- .../bindings/builtins/js/ReadableStream.js | 16 +- .../js/ReadableStreamDefaultReader.js | 40 +++-- .../builtins/js/ReadableStreamInternals.js | 3 +- src/javascript/jsc/bindings/headers-cpp.h | 2 +- .../jsc/bindings/headers-handwritten.h | 2 + .../jsc/bindings/headers-replacements.zig | 1 + src/javascript/jsc/bindings/headers.h | 15 +- src/javascript/jsc/bindings/headers.zig | 6 +- .../jsc/bindings/webcore/JSReadableStream.cpp | 2 +- .../webcore/JSReadableStreamSource.cpp | 2 +- .../jsc/bindings/webcore/ReadableStream.cpp | 8 +- .../jsc/bindings/webcore/ReadableStream.h | 2 +- src/javascript/jsc/event_loop.zig | 8 +- src/javascript/jsc/javascript.zig | 14 +- src/javascript/jsc/node/node_fs_binding.zig | 2 +- src/javascript/jsc/node/types.zig | 26 +-- src/javascript/jsc/webcore/encoding.zig | 24 +-- src/javascript/jsc/webcore/response.zig | 54 ++++-- src/napi/napi.zig | 34 ++-- src/string_immutable.zig | 65 ++++++- 39 files changed, 697 insertions(+), 256 deletions(-) diff --git a/Makefile b/Makefile index c2ebaf5b21..bafda81acc 100644 --- a/Makefile +++ b/Makefile @@ -265,6 +265,7 @@ CLANG_FLAGS = $(INCLUDE_DIRS) \ -DSTATICALLY_LINKED_WITH_WTF=1 \ -DSTATICALLY_LINKED_WITH_BMALLOC=1 \ -DBUILDING_WITH_CMAKE=1 \ + -DBUN_SINGLE_THREADED_PER_VM_ENTRY_SCOPE=1 \ -DNDEBUG=1 \ -DNOMINMAX \ -DIS_BUILD \ @@ -940,6 +941,7 @@ jsc-build-mac-compile: cmake \ -DPORT="JSCOnly" \ -DENABLE_STATIC_JSC=ON \ + -DENABLE_SINGLE_THREADED_VM_ENTRY_SCOPE=ON \ -DCMAKE_BUILD_TYPE=Release \ -DUSE_THIN_ARCHIVES=OFF \ -DENABLE_FTL_JIT=ON \ diff --git a/src/feature_flags.zig b/src/feature_flags.zig index a9665ddd07..4a9a7a776e 100644 --- a/src/feature_flags.zig +++ b/src/feature_flags.zig @@ -91,4 +91,6 @@ pub const atomic_file_watcher = env.isLinux; pub const node_streams = env.isDebug or env.isTest; pub const simd = true; -pub const latin1_is_now_ascii = true; + +// This change didn't seem to make a meaningful difference in microbenchmarks +pub const latin1_is_now_ascii = false; diff --git a/src/global.zig b/src/global.zig index 5a5be13c0e..f2d9d97290 100644 --- a/src/global.zig +++ b/src/global.zig @@ -161,3 +161,19 @@ pub const IdentityContext = @import("./identity_context.zig").IdentityContext; pub const ArrayIdentityContext = @import("./identity_context.zig").ArrayIdentityContext; pub const BabyList = @import("./baby_list.zig").BabyList; pub const ByteList = BabyList(u8); + +pub fn DebugOnly(comptime Type: type) type { + if (comptime Environment.isDebug) { + return Type; + } + + return void; +} + +pub fn DebugOnlyDefault(comptime val: anytype) if (Environment.isDebug) @TypeOf(val) else void { + if (comptime Environment.isDebug) { + return val; + } + + return {}; +} diff --git a/src/javascript/jsc/api/bun.zig b/src/javascript/jsc/api/bun.zig index bb0fa218b1..8bbcccfb58 100644 --- a/src/javascript/jsc/api/bun.zig +++ b/src/javascript/jsc/api/bun.zig @@ -623,7 +623,7 @@ pub fn openInEditor( ) js.JSValueRef { var edit = &VirtualMachine.vm.rareData().editor_context; - var arguments = JSC.Node.ArgumentsSlice.from(args); + var arguments = JSC.Node.ArgumentsSlice.from(ctx.bunVM(), args); defer arguments.deinit(); var path: string = ""; var editor_choice: ?Editor = null; @@ -822,7 +822,7 @@ fn doResolve( arguments: []const js.JSValueRef, exception: js.ExceptionRef, ) ?JSC.JSValue { - var args = JSC.Node.ArgumentsSlice.from(arguments); + var args = JSC.Node.ArgumentsSlice.from(ctx.bunVM(), arguments); defer args.deinit(); const specifier = args.protectEatNext() orelse { JSC.throwInvalidArguments("Expected a specifier and a from path", .{}, ctx, exception); @@ -1543,7 +1543,7 @@ pub fn serve( arguments: []const js.JSValueRef, exception: js.ExceptionRef, ) js.JSValueRef { - var args = JSC.Node.ArgumentsSlice.from(arguments); + var args = JSC.Node.ArgumentsSlice.from(ctx.bunVM(), arguments); var config = JSC.API.ServerConfig.fromJS(ctx.ptr(), &args, exception); if (exception.* != null) { return null; @@ -1620,7 +1620,7 @@ pub fn allocUnsafe( arguments: []const js.JSValueRef, exception: js.ExceptionRef, ) js.JSValueRef { - var args = JSC.Node.ArgumentsSlice.from(arguments); + var args = JSC.Node.ArgumentsSlice.from(ctx.bunVM(), arguments); const length = @intCast( usize, @@ -1649,7 +1649,7 @@ pub fn mmapFile( arguments: []const js.JSValueRef, exception: js.ExceptionRef, ) js.JSValueRef { - var args = JSC.Node.ArgumentsSlice.from(arguments); + var args = JSC.Node.ArgumentsSlice.from(ctx.bunVM(), arguments); var buf: [bun.MAX_PATH_BYTES]u8 = undefined; const path = getFilePath(ctx, arguments[0..@minimum(1, arguments.len)], &buf, exception) orelse return null; @@ -1795,7 +1795,7 @@ pub const Hash = struct { arguments: []const js.JSValueRef, exception: js.ExceptionRef, ) js.JSValueRef { - var args = JSC.Node.ArgumentsSlice.from(arguments); + var args = JSC.Node.ArgumentsSlice.from(ctx.bunVM(), arguments); var input: []const u8 = ""; var input_slice = ZigString.Slice.empty; defer input_slice.deinit(); diff --git a/src/javascript/jsc/api/server.zig b/src/javascript/jsc/api/server.zig index 8d00207241..aa4cb91a09 100644 --- a/src/javascript/jsc/api/server.zig +++ b/src/javascript/jsc/api/server.zig @@ -238,7 +238,7 @@ pub const ServerConfig = struct { }; pub fn fromJS(global: *JSC.JSGlobalObject, arguments: *JSC.Node.ArgumentsSlice, exception: JSC.C.ExceptionRef) ServerConfig { - var env = VirtualMachine.vm.bundler.env; + var env = arguments.vm.bundler.env; var args = ServerConfig{ .port = 3000, @@ -250,7 +250,7 @@ pub const ServerConfig = struct { args.development = false; } - if (VirtualMachine.vm.bundler.options.production) { + if (arguments.vm.bundler.options.production) { args.development = false; } @@ -264,11 +264,11 @@ pub const ServerConfig = struct { } } - if (VirtualMachine.vm.bundler.options.transform_options.port) |port| { + if (arguments.vm.bundler.options.transform_options.port) |port| { args.port = port; } - if (VirtualMachine.vm.bundler.options.transform_options.origin) |origin| { + if (arguments.vm.bundler.options.transform_options.origin) |origin| { args.base_uri = origin; } @@ -515,6 +515,11 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp url: string, method: HTTP.Method, aborted: bool = false, + finalized: bun.DebugOnly(bool) = bun.DebugOnlyDefault(false), + + /// We can only safely free once the request body promise is finalized + /// and the response is rejected + pending_promises_for_abort: u8 = 0, has_marked_complete: bool = false, response_jsvalue: JSC.JSValue = JSC.JSValue.zero, @@ -549,7 +554,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp arguments: []const JSC.JSValue, ) void { if (ctx.aborted) { - ctx.finalize(); + ctx.finalizeForAbort(); return; } @@ -579,16 +584,20 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp ctx.render(response); } + pub fn finalizeForAbort(this: *RequestContext) void { + this.pending_promises_for_abort -|= 1; + if (this.pending_promises_for_abort == 0) this.finalize(); + } + pub fn onReject( ctx: *RequestContext, _: *JSC.JSGlobalObject, arguments: []const JSC.JSValue, ) void { if (ctx.aborted) { - ctx.finalize(); + ctx.finalizeForAbort(); return; } - handleReject(ctx, if (arguments.len > 0) arguments[0] else JSC.JSValue.jsUndefined()); } @@ -598,10 +607,9 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp ); if (ctx.aborted) { - ctx.finalize(); + ctx.finalizeForAbort(); return; } - if (!ctx.resp.hasResponded()) { ctx.renderMissing(); } @@ -674,8 +682,9 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } pub fn onWritableResponseBuffer(this: *RequestContext, write_offset: c_ulong, resp: *App.Response) callconv(.C) bool { + std.debug.assert(this.resp == resp); if (this.aborted) { - this.finalize(); + this.finalizeForAbort(); return false; } return this.sendWritableBytes(this.response_buf_owned.items, write_offset, resp); @@ -694,10 +703,69 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp }; } - pub fn onAbort(this: *RequestContext, _: *App.Response) void { + pub fn isDeadRequest(this: *RequestContext) bool { + if (this.pending_promises_for_abort > 0) return false; + + if (this.promise != null) { + return false; + } + + if (this.request_js_object) |obj| { + if (obj.value().as(Request)) |req| { + if (req.body == .Locked) { + return false; + } + } + } + + return true; + } + + pub fn onAbort(this: *RequestContext, resp: *App.Response) void { + std.debug.assert(this.resp == resp); + std.debug.assert(!this.aborted); this.aborted = true; - this.finalizeWithoutDeinit(); - this.markComplete(); + + // if we can, free the request now. + if (this.isDeadRequest()) { + this.finalizeWithoutDeinit(); + this.markComplete(); + this.deinit(); + } else { + this.pending_promises_for_abort = 0; + + // if we cannot, we have to reject pending promises + // first, we reject the request body promise + if (this.request_js_object != null) { + var request_js = this.request_js_object.?.value(); + request_js.ensureStillAlive(); + + this.request_js_object = null; + defer request_js.ensureStillAlive(); + defer JSC.C.JSValueUnprotect(this.server.globalThis.ref(), request_js.asObjectRef()); + // User called .blob(), .json(), text(), or .arrayBuffer() on the Request object + // but we received nothing or the connection was aborted + if (request_js.as(Request)) |req| { + // the promise is pending + if (req.body == .Locked and (req.body.Locked.action != .none or req.body.Locked.promise != null)) { + this.pending_promises_for_abort += 1; + req.body.toErrorInstance(JSC.toTypeError(.ABORT_ERR, "Request aborted", .{}, this.server.globalThis), this.server.globalThis); + } + req.uws_request = null; + } + } + + // then, we reject the response promise + if (this.promise) |promise| { + this.pending_promises_for_abort += 1; + this.promise = null; + promise.asPromise().?.reject(this.server.globalThis, JSC.toTypeError(.ABORT_ERR, "Request aborted", .{}, this.server.globalThis)); + } + + if (this.pending_promises_for_abort > 0) { + this.server.vm.tick(); + } + } } pub fn markComplete(this: *RequestContext) void { @@ -709,7 +777,11 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp // so it's important that we can safely do that pub fn finalizeWithoutDeinit(this: *RequestContext) void { this.blob.detach(); - this.request_body_buf.clearAndFree(this.allocator); + + if (comptime Environment.allow_assert) { + std.debug.assert(!this.finalized); + this.finalized = true; + } if (!this.response_jsvalue.isEmpty()) { this.server.response_objects_pool.push(this.server.globalThis, this.response_jsvalue); @@ -717,38 +789,56 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } if (this.request_js_object != null) { + var request_js = this.request_js_object.?.value(); + request_js.ensureStillAlive(); + + this.request_js_object = null; + defer request_js.ensureStillAlive(); + defer JSC.C.JSValueUnprotect(this.server.globalThis.ref(), request_js.asObjectRef()); // User called .blob(), .json(), text(), or .arrayBuffer() on the Request object - // but we received nothing - if (JSC.JSValue.fromRef(this.request_js_object).as(Request)) |req| { + // but we received nothing or the connection was aborted + if (request_js.as(Request)) |req| { + // the promise is pending if (req.body == .Locked and req.body.Locked.action != .none and req.body.Locked.promise != null) { - var old_body = req.body; - req.body = JSC.WebCore.Body.Value.empty; - old_body.Locked.callback = null; - old_body.resolve(&req.body, this.server.globalThis); - VirtualMachine.vm.tick(); + req.body.toErrorInstance(JSC.toTypeError(.ABORT_ERR, "Request aborted", .{}, this.server.globalThis), this.server.globalThis); } req.uws_request = null; - JSC.C.JSValueUnprotect(this.server.globalThis.ref(), this.request_js_object); - this.request_js_object = null; } } - if (this.promise != null) { - JSC.C.JSValueUnprotect(this.server.globalThis.ref(), this.promise.?.asObjectRef()); + if (this.promise) |promise| { this.promise = null; + + if (promise.asInternalPromise()) |prom| { + prom.rejectAsHandled(this.server.globalThis, (JSC.toTypeError(.ABORT_ERR, "Request aborted", .{}, this.server.globalThis))); + } else if (promise.asPromise()) |prom| { + prom.rejectAsHandled(this.server.globalThis, (JSC.toTypeError(.ABORT_ERR, "Request aborted", .{}, this.server.globalThis))); + } + JSC.C.JSValueUnprotect(this.server.globalThis.ref(), promise.asObjectRef()); } if (this.response_headers != null) { this.response_headers.?.deref(); this.response_headers = null; } - - this.response_buf_owned.clearAndFree(this.allocator); } pub fn finalize(this: *RequestContext) void { - var server = this.server; this.finalizeWithoutDeinit(); this.markComplete(); + this.deinit(); + } + + pub fn deinit(this: *RequestContext) void { + if (comptime Environment.allow_assert) + std.debug.assert(this.finalized); + + if (comptime Environment.allow_assert) + std.debug.assert(this.has_marked_complete); + + var server = this.server; + this.request_body_buf.clearAndFree(this.allocator); + this.response_buf_owned.clearAndFree(this.allocator); + server.request_pool_allocator.destroy(this); } @@ -855,8 +945,9 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } pub fn onWritableBytes(this: *RequestContext, write_offset: c_ulong, resp: *App.Response) callconv(.C) bool { + std.debug.assert(this.resp == resp); if (this.aborted) { - this.finalize(); + this.finalizeForAbort(); return false; } @@ -865,6 +956,8 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } pub fn sendWritableBytes(this: *RequestContext, bytes_: []const u8, write_offset: c_ulong, resp: *App.Response) bool { + std.debug.assert(this.resp == resp); + var bytes = bytes_[@minimum(bytes_.len, @truncate(usize, write_offset))..]; if (resp.tryEnd(bytes, bytes_.len)) { this.finalize(); @@ -976,7 +1069,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp pub fn doSendfile(this: *RequestContext, blob: Blob) void { if (this.aborted) { - this.finalize(); + this.finalizeForAbort(); return; } @@ -994,7 +1087,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp pub fn onReadFile(this: *RequestContext, result: Blob.Store.ReadFile.ResultType) void { if (this.aborted) { - this.finalize(); + this.finalizeForAbort(); return; } @@ -1024,7 +1117,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp const err = value.Error; _ = value.use(); if (this.aborted) { - this.finalize(); + this.finalizeForAbort(); return; } this.runErrorHandler(err); @@ -1034,7 +1127,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp this.blob = value.use(); if (this.aborted) { - this.finalize(); + this.finalizeForAbort(); return; } @@ -1068,7 +1161,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp pub fn doRender(this: *RequestContext) void { if (this.aborted) { - this.finalize(); + this.finalizeForAbort(); return; } var response = this.response_ptr.?; @@ -1232,7 +1325,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp pub fn resolveRequestBody(this: *RequestContext) void { if (this.aborted) { - this.finalize(); + this.finalizeForAbort(); return; } @@ -1251,7 +1344,9 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } } - pub fn onBodyChunk(this: *RequestContext, _: *App.Response, chunk: []const u8, last: bool) void { + pub fn onBodyChunk(this: *RequestContext, resp: *App.Response, chunk: []const u8, last: bool) void { + std.debug.assert(this.resp == resp); + if (this.aborted) return; this.request_body_buf.appendSlice(this.allocator, chunk) catch @panic("Out of memory while allocating request body"); if (last) { @@ -1617,10 +1712,9 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type { const response_value = JSC.C.JSObjectCallAsFunctionReturnValue(this.globalThis.ref(), this.config.onRequest.asObjectRef(), this.thisObject.asObjectRef(), 1, &args); if (ctx.aborted) { - ctx.finalize(); + ctx.finalizeForAbort(); return; } - if (response_value.isEmptyOrUndefinedOrNull() and !ctx.resp.hasResponded()) { ctx.renderMissing(); return; diff --git a/src/javascript/jsc/api/transpiler.zig b/src/javascript/jsc/api/transpiler.zig index b8a8192c6a..ca2261c008 100644 --- a/src/javascript/jsc/api/transpiler.zig +++ b/src/javascript/jsc/api/transpiler.zig @@ -737,7 +737,7 @@ pub fn constructor( exception: js.ExceptionRef, ) js.JSObjectRef { var temp = std.heap.ArenaAllocator.init(getAllocator(ctx)); - var args = JSC.Node.ArgumentsSlice.init(@ptrCast([*]const JSC.JSValue, arguments.ptr)[0..arguments.len]); + var args = JSC.Node.ArgumentsSlice.init(ctx.bunVM(), @ptrCast([*]const JSC.JSValue, arguments.ptr)[0..arguments.len]); defer temp.deinit(); const transpiler_options: TranspilerOptions = if (arguments.len > 0) transformOptionsFromJSC(ctx, temp.allocator(), &args, exception) catch { @@ -874,7 +874,7 @@ pub fn scan( arguments: []const js.JSValueRef, exception: js.ExceptionRef, ) JSC.C.JSObjectRef { - var args = JSC.Node.ArgumentsSlice.init(@ptrCast([*]const JSC.JSValue, arguments.ptr)[0..arguments.len]); + var args = JSC.Node.ArgumentsSlice.init(ctx.bunVM(), @ptrCast([*]const JSC.JSValue, arguments.ptr)[0..arguments.len]); defer args.arena.deinit(); const code_arg = args.next() orelse { JSC.throwInvalidArguments("Expected a string or Uint8Array", .{}, ctx, exception); @@ -965,7 +965,7 @@ pub fn transform( arguments: []const js.JSValueRef, exception: js.ExceptionRef, ) JSC.C.JSObjectRef { - var args = JSC.Node.ArgumentsSlice.init(@ptrCast([*]const JSC.JSValue, arguments.ptr)[0..arguments.len]); + var args = JSC.Node.ArgumentsSlice.init(ctx.bunVM(), @ptrCast([*]const JSC.JSValue, arguments.ptr)[0..arguments.len]); defer args.arena.deinit(); const code_arg = args.next() orelse { JSC.throwInvalidArguments("Expected a string or Uint8Array", .{}, ctx, exception); @@ -1006,7 +1006,7 @@ pub fn transformSync( arguments: []const js.JSValueRef, exception: js.ExceptionRef, ) JSC.C.JSObjectRef { - var args = JSC.Node.ArgumentsSlice.init(@ptrCast([*]const JSC.JSValue, arguments.ptr)[0..arguments.len]); + var args = JSC.Node.ArgumentsSlice.init(ctx.bunVM(), @ptrCast([*]const JSC.JSValue, arguments.ptr)[0..arguments.len]); defer args.arena.deinit(); const code_arg = args.next() orelse { JSC.throwInvalidArguments("Expected a string or Uint8Array", .{}, ctx, exception); @@ -1193,7 +1193,7 @@ pub fn scanImports( arguments: []const js.JSValueRef, exception: js.ExceptionRef, ) JSC.C.JSObjectRef { - var args = JSC.Node.ArgumentsSlice.init(@ptrCast([*]const JSC.JSValue, arguments.ptr)[0..arguments.len]); + var args = JSC.Node.ArgumentsSlice.init(ctx.bunVM(), @ptrCast([*]const JSC.JSValue, arguments.ptr)[0..arguments.len]); const code_arg = args.next() orelse { JSC.throwInvalidArguments("Expected a string or Uint8Array", .{}, ctx, exception); return null; diff --git a/src/javascript/jsc/base.zig b/src/javascript/jsc/base.zig index 998344d1ce..2ecd496439 100644 --- a/src/javascript/jsc/base.zig +++ b/src/javascript/jsc/base.zig @@ -2792,7 +2792,7 @@ pub fn wrapWithHasContainer( arguments: []const js.JSValueRef, exception: js.ExceptionRef, ) js.JSObjectRef { - var iter = JSC.Node.ArgumentsSlice.from(arguments); + var iter = JSC.Node.ArgumentsSlice.from(ctx.bunVM(), arguments); var args: Args = undefined; comptime var i: usize = 0; @@ -2933,14 +2933,15 @@ pub fn wrapWithHasContainer( } if (comptime maybe_async) { - JavaScript.VirtualMachine.vm.tick(); + var vm = ctx.ptr().bunVM(); + vm.tick(); var promise = JSC.JSInternalPromise.resolvedPromise(ctx.ptr(), result); switch (promise.status(ctx.ptr().vm())) { JSC.JSPromise.Status.Pending => { while (promise.status(ctx.ptr().vm()) == .Pending) { - JavaScript.VirtualMachine.vm.tick(); + vm.tick(); } result = promise.result(ctx.ptr().vm()); }, diff --git a/src/javascript/jsc/bindings/BunBuiltinNames.h b/src/javascript/jsc/bindings/BunBuiltinNames.h index 17408972a5..0acfb0bb5b 100644 --- a/src/javascript/jsc/bindings/BunBuiltinNames.h +++ b/src/javascript/jsc/bindings/BunBuiltinNames.h @@ -48,7 +48,8 @@ using namespace JSC; macro(backpressureChangePromise) \ macro(basename) \ macro(body) \ - macro(bunNativeTag) \ + macro(bunNativePtr) \ + macro(bunNativeType) \ macro(byobRequest) \ macro(cancel) \ macro(cancelAlgorithm) \ diff --git a/src/javascript/jsc/bindings/BunClientData.cpp b/src/javascript/jsc/bindings/BunClientData.cpp index 56fc51365f..7f005b42c3 100644 --- a/src/javascript/jsc/bindings/BunClientData.cpp +++ b/src/javascript/jsc/bindings/BunClientData.cpp @@ -77,7 +77,6 @@ void JSVMClientData::create(VM* vm) clientData->m_normalWorld = DOMWrapperWorld::create(*vm, DOMWrapperWorld::Type::Normal); vm->heap.addMarkingConstraint(makeUnique(*vm, clientData->heapData())); - vm->m_typedArrayController = adoptRef(new WebCoreTypedArrayController(true)); } diff --git a/src/javascript/jsc/bindings/ReadableByteStreamInternalsBuiltins.cpp b/src/javascript/jsc/bindings/ReadableByteStreamInternalsBuiltins.cpp index 1e91a4b73a..6ea53fa580 100644 --- a/src/javascript/jsc/bindings/ReadableByteStreamInternalsBuiltins.cpp +++ b/src/javascript/jsc/bindings/ReadableByteStreamInternalsBuiltins.cpp @@ -360,7 +360,7 @@ const char* const s_readableByteStreamInternalsReadableByteStreamControllerPullC const JSC::ConstructAbility s_readableByteStreamInternalsReadableByteStreamControllerShouldCallPullCodeConstructAbility = JSC::ConstructAbility::CannotConstruct; const JSC::ConstructorKind s_readableByteStreamInternalsReadableByteStreamControllerShouldCallPullCodeConstructorKind = JSC::ConstructorKind::None; -const int s_readableByteStreamInternalsReadableByteStreamControllerShouldCallPullCodeLength = 815; +const int s_readableByteStreamInternalsReadableByteStreamControllerShouldCallPullCodeLength = 868; static const JSC::Intrinsic s_readableByteStreamInternalsReadableByteStreamControllerShouldCallPullCodeIntrinsic = JSC::NoIntrinsic; const char* const s_readableByteStreamInternalsReadableByteStreamControllerShouldCallPullCode = "(function (controller)\n" \ @@ -375,7 +375,7 @@ const char* const s_readableByteStreamInternalsReadableByteStreamControllerShoul " return false;\n" \ " if (!@getByIdDirectPrivate(controller, \"started\"))\n" \ " return false;\n" \ - " if (@readableStreamHasDefaultReader(stream) && @getByIdDirectPrivate(@getByIdDirectPrivate(stream, \"reader\"), \"readRequests\").length > 0)\n" \ + " if (@readableStreamHasDefaultReader(stream) && (@getByIdDirectPrivate(@getByIdDirectPrivate(stream, \"reader\"), \"readRequests\").length > 0 || !!@getByIdDirectPrivate(reader, \"bunNativePtr\")))\n" \ " return true;\n" \ " if (@readableStreamHasBYOBReader(stream) && @getByIdDirectPrivate(@getByIdDirectPrivate(stream, \"reader\"), \"readIntoRequests\").length > 0)\n" \ " return true;\n" \ @@ -434,9 +434,28 @@ const char* const s_readableByteStreamInternalsTransferBufferToCurrentRealmCode "})\n" \ ; +const JSC::ConstructAbility s_readableByteStreamInternalsReadableStreamReaderKindCodeConstructAbility = JSC::ConstructAbility::CannotConstruct; +const JSC::ConstructorKind s_readableByteStreamInternalsReadableStreamReaderKindCodeConstructorKind = JSC::ConstructorKind::None; +const int s_readableByteStreamInternalsReadableStreamReaderKindCodeLength = 266; +static const JSC::Intrinsic s_readableByteStreamInternalsReadableStreamReaderKindCodeIntrinsic = JSC::NoIntrinsic; +const char* const s_readableByteStreamInternalsReadableStreamReaderKindCode = + "(function (reader) {\n" \ + " \"use strict\";\n" \ + "\n" \ + "\n" \ + " if (!!@getByIdDirectPrivate(reader, \"readRequests\"))\n" \ + " return @getByIdDirectPrivate(reader, \"bunNativePtr\") ? 3 : 1;\n" \ + "\n" \ + " if (!!@getByIdDirectPrivate(reader, \"readIntoRequests\"))\n" \ + " return 2;\n" \ + "\n" \ + " return 0;\n" \ + "})\n" \ +; + const JSC::ConstructAbility s_readableByteStreamInternalsReadableByteStreamControllerEnqueueCodeConstructAbility = JSC::ConstructAbility::CannotConstruct; const JSC::ConstructorKind s_readableByteStreamInternalsReadableByteStreamControllerEnqueueCodeConstructorKind = JSC::ConstructorKind::None; -const int s_readableByteStreamInternalsReadableByteStreamControllerEnqueueCodeLength = 1440; +const int s_readableByteStreamInternalsReadableByteStreamControllerEnqueueCodeLength = 1629; static const JSC::Intrinsic s_readableByteStreamInternalsReadableByteStreamControllerEnqueueCodeIntrinsic = JSC::NoIntrinsic; const char* const s_readableByteStreamInternalsReadableByteStreamControllerEnqueueCode = "(function (controller, chunk)\n" \ @@ -446,28 +465,42 @@ const char* const s_readableByteStreamInternalsReadableByteStreamControllerEnque " const stream = @getByIdDirectPrivate(controller, \"controlledReadableStream\");\n" \ " @assert(!@getByIdDirectPrivate(controller, \"closeRequested\"));\n" \ " @assert(@getByIdDirectPrivate(stream, \"state\") === @streamReadable);\n" \ - " const byteOffset = chunk.byteOffset;\n" \ - " const byteLength = chunk.byteLength;\n" \ + " var reader = @getByIdDirectPrivate(stream, \"reader\");\n" \ "\n" \ - " if (@readableStreamHasDefaultReader(stream)) {\n" \ - " if (!@getByIdDirectPrivate(@getByIdDirectPrivate(stream, \"reader\"), \"readRequests\").length)\n" \ - " @readableByteStreamControllerEnqueueChunk(controller, @transferBufferToCurrentRealm(chunk.buffer), byteOffset, byteLength);\n" \ - " else {\n" \ - " @assert(!@getByIdDirectPrivate(controller, \"queue\").content.length);\n" \ - " const transferredView = chunk.constructor === @Uint8Array ? chunk : new @Uint8Array(chunk.buffer, byteOffset, byteLength);\n" \ - " @readableStreamFulfillReadRequest(stream, transferredView, false);\n" \ + "\n" \ + " switch (@readableStreamReaderKind(reader)) {\n" \ + " \n" \ + " case 1: {\n" \ + " if (!@getByIdDirectPrivate(reader, \"readRequests\").length)\n" \ + " @readableByteStreamControllerEnqueueChunk(controller, @transferBufferToCurrentRealm(chunk.buffer), chunk.byteOffset, chunk.byteLength);\n" \ + " else {\n" \ + " @assert(!@getByIdDirectPrivate(controller, \"queue\").content.length);\n" \ + " const transferredView = chunk.constructor === @Uint8Array ? chunk : new @Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);\n" \ + " @readableStreamFulfillReadRequest(stream, transferredView, false);\n" \ + " }\n" \ + " break;\n" \ " }\n" \ - " return;\n" \ - " }\n" \ "\n" \ - " if (@readableStreamHasBYOBReader(stream)) {\n" \ - " @readableByteStreamControllerEnqueueChunk(controller, @transferBufferToCurrentRealm(chunk.buffer), byteOffset, byteLength);\n" \ - " @readableByteStreamControllerProcessPullDescriptors(controller);\n" \ - " return;\n" \ - " }\n" \ + " \n" \ + " case 2: {\n" \ + " @readableByteStreamControllerEnqueueChunk(controller, @transferBufferToCurrentRealm(chunk.buffer), chunk.byteOffset, chunk.byteLength);\n" \ + " @readableByteStreamControllerProcessPullDescriptors(controller);\n" \ + " break;\n" \ + " }\n" \ "\n" \ - " @assert(!@isReadableStreamLocked(stream));\n" \ - " @readableByteStreamControllerEnqueueChunk(controller, @transferBufferToCurrentRealm(chunk.buffer), byteOffset, byteLength);\n" \ + " \n" \ + " case 3: {\n" \ + " //\n" \ + "\n" \ + " break;\n" \ + " }\n" \ + "\n" \ + " default: {\n" \ + " @assert(!@isReadableStreamLocked(stream));\n" \ + " @readableByteStreamControllerEnqueueChunk(controller, @transferBufferToCurrentRealm(chunk.buffer), chunk.byteOffset, chunk.byteLength);\n" \ + " break;\n" \ + " }\n" \ + " }\n" \ "})\n" \ ; diff --git a/src/javascript/jsc/bindings/ReadableByteStreamInternalsBuiltins.h b/src/javascript/jsc/bindings/ReadableByteStreamInternalsBuiltins.h index 5389414efa..e805cee428 100644 --- a/src/javascript/jsc/bindings/ReadableByteStreamInternalsBuiltins.h +++ b/src/javascript/jsc/bindings/ReadableByteStreamInternalsBuiltins.h @@ -115,6 +115,10 @@ extern const char* const s_readableByteStreamInternalsTransferBufferToCurrentRea extern const int s_readableByteStreamInternalsTransferBufferToCurrentRealmCodeLength; extern const JSC::ConstructAbility s_readableByteStreamInternalsTransferBufferToCurrentRealmCodeConstructAbility; extern const JSC::ConstructorKind s_readableByteStreamInternalsTransferBufferToCurrentRealmCodeConstructorKind; +extern const char* const s_readableByteStreamInternalsReadableStreamReaderKindCode; +extern const int s_readableByteStreamInternalsReadableStreamReaderKindCodeLength; +extern const JSC::ConstructAbility s_readableByteStreamInternalsReadableStreamReaderKindCodeConstructAbility; +extern const JSC::ConstructorKind s_readableByteStreamInternalsReadableStreamReaderKindCodeConstructorKind; extern const char* const s_readableByteStreamInternalsReadableByteStreamControllerEnqueueCode; extern const int s_readableByteStreamInternalsReadableByteStreamControllerEnqueueCodeLength; extern const JSC::ConstructAbility s_readableByteStreamInternalsReadableByteStreamControllerEnqueueCodeConstructAbility; @@ -202,6 +206,7 @@ extern const JSC::ConstructorKind s_readableByteStreamInternalsReadableStreamAdd macro(readableByteStreamControllerShouldCallPull, readableByteStreamInternalsReadableByteStreamControllerShouldCallPull, 1) \ macro(readableByteStreamControllerCallPullIfNeeded, readableByteStreamInternalsReadableByteStreamControllerCallPullIfNeeded, 1) \ macro(transferBufferToCurrentRealm, readableByteStreamInternalsTransferBufferToCurrentRealm, 1) \ + macro(readableStreamReaderKind, readableByteStreamInternalsReadableStreamReaderKind, 1) \ macro(readableByteStreamControllerEnqueue, readableByteStreamInternalsReadableByteStreamControllerEnqueue, 2) \ macro(readableByteStreamControllerEnqueueChunk, readableByteStreamInternalsReadableByteStreamControllerEnqueueChunk, 4) \ macro(readableByteStreamControllerRespondWithNewView, readableByteStreamInternalsReadableByteStreamControllerRespondWithNewView, 2) \ @@ -237,6 +242,7 @@ extern const JSC::ConstructorKind s_readableByteStreamInternalsReadableStreamAdd #define WEBCORE_BUILTIN_READABLEBYTESTREAMINTERNALS_READABLEBYTESTREAMCONTROLLERSHOULDCALLPULL 1 #define WEBCORE_BUILTIN_READABLEBYTESTREAMINTERNALS_READABLEBYTESTREAMCONTROLLERCALLPULLIFNEEDED 1 #define WEBCORE_BUILTIN_READABLEBYTESTREAMINTERNALS_TRANSFERBUFFERTOCURRENTREALM 1 +#define WEBCORE_BUILTIN_READABLEBYTESTREAMINTERNALS_READABLESTREAMREADERKIND 1 #define WEBCORE_BUILTIN_READABLEBYTESTREAMINTERNALS_READABLEBYTESTREAMCONTROLLERENQUEUE 1 #define WEBCORE_BUILTIN_READABLEBYTESTREAMINTERNALS_READABLEBYTESTREAMCONTROLLERENQUEUECHUNK 1 #define WEBCORE_BUILTIN_READABLEBYTESTREAMINTERNALS_READABLEBYTESTREAMCONTROLLERRESPONDWITHNEWVIEW 1 @@ -273,6 +279,7 @@ extern const JSC::ConstructorKind s_readableByteStreamInternalsReadableStreamAdd macro(readableByteStreamInternalsReadableByteStreamControllerShouldCallPullCode, readableByteStreamControllerShouldCallPull, ASCIILiteral(), s_readableByteStreamInternalsReadableByteStreamControllerShouldCallPullCodeLength) \ macro(readableByteStreamInternalsReadableByteStreamControllerCallPullIfNeededCode, readableByteStreamControllerCallPullIfNeeded, ASCIILiteral(), s_readableByteStreamInternalsReadableByteStreamControllerCallPullIfNeededCodeLength) \ macro(readableByteStreamInternalsTransferBufferToCurrentRealmCode, transferBufferToCurrentRealm, ASCIILiteral(), s_readableByteStreamInternalsTransferBufferToCurrentRealmCodeLength) \ + macro(readableByteStreamInternalsReadableStreamReaderKindCode, readableStreamReaderKind, ASCIILiteral(), s_readableByteStreamInternalsReadableStreamReaderKindCodeLength) \ macro(readableByteStreamInternalsReadableByteStreamControllerEnqueueCode, readableByteStreamControllerEnqueue, ASCIILiteral(), s_readableByteStreamInternalsReadableByteStreamControllerEnqueueCodeLength) \ macro(readableByteStreamInternalsReadableByteStreamControllerEnqueueChunkCode, readableByteStreamControllerEnqueueChunk, ASCIILiteral(), s_readableByteStreamInternalsReadableByteStreamControllerEnqueueChunkCodeLength) \ macro(readableByteStreamInternalsReadableByteStreamControllerRespondWithNewViewCode, readableByteStreamControllerRespondWithNewView, ASCIILiteral(), s_readableByteStreamInternalsReadableByteStreamControllerRespondWithNewViewCodeLength) \ @@ -325,6 +332,7 @@ extern const JSC::ConstructorKind s_readableByteStreamInternalsReadableStreamAdd macro(readableStreamFulfillReadIntoRequest) \ macro(readableStreamHasBYOBReader) \ macro(readableStreamHasDefaultReader) \ + macro(readableStreamReaderKind) \ macro(transferBufferToCurrentRealm) \ #define DECLARE_BUILTIN_GENERATOR(codeName, functionName, overriddenName, argumentCount) \ diff --git a/src/javascript/jsc/bindings/ReadableStreamBuiltins.cpp b/src/javascript/jsc/bindings/ReadableStreamBuiltins.cpp index fb0c2c6fec..25eac86c9a 100644 --- a/src/javascript/jsc/bindings/ReadableStreamBuiltins.cpp +++ b/src/javascript/jsc/bindings/ReadableStreamBuiltins.cpp @@ -119,15 +119,15 @@ const char* const s_readableStreamInitializeReadableStreamCode = const JSC::ConstructAbility s_readableStreamCreateNativeReadableStreamCodeConstructAbility = JSC::ConstructAbility::CannotConstruct; const JSC::ConstructorKind s_readableStreamCreateNativeReadableStreamCodeConstructorKind = JSC::ConstructorKind::None; -const int s_readableStreamCreateNativeReadableStreamCodeLength = 2373; +const int s_readableStreamCreateNativeReadableStreamCodeLength = 2446; static const JSC::Intrinsic s_readableStreamCreateNativeReadableStreamCodeIntrinsic = JSC::NoIntrinsic; const char* const s_readableStreamCreateNativeReadableStreamCode = - "(function (nativeTag, nativeID) {\n" \ + "(function (nativePtr, nativeType) {\n" \ " \"use strict\";\n" \ " var cached = globalThis[Symbol.for(\"Bun.nativeReadableStreamPrototype\")] ||= new @Map;\n" \ - " var Prototype = cached.@get(nativeID);\n" \ + " var Prototype = cached.@get(nativeType);\n" \ " if (Prototype === @undefined) {\n" \ - " var [pull, start, cancel, setClose, deinit] = globalThis[Symbol.for(\"Bun.lazy\")](nativeID);\n" \ + " var [pull, start, cancel, setClose, deinit] = globalThis[Symbol.for(\"Bun.lazy\")](nativeType);\n" \ " var closer = [false];\n" \ "\n" \ " var handleResult = function handleResult(result, controller) {\n" \ @@ -187,13 +187,14 @@ const char* const s_readableStreamCreateNativeReadableStreamCode = "\n" \ " static registry = new FinalizationRegistry(deinit);\n" \ " }\n" \ - " cached.@set(nativeID, Prototype);\n" \ + " cached.@set(nativeType, Prototype);\n" \ " }\n" \ " \n" \ - " var instance = new Prototype(nativeTag);\n" \ - " Prototype.registry.register(instance, nativeTag);\n" \ + " var instance = new Prototype(nativePtr);\n" \ + " Prototype.registry.register(instance, nativePtr);\n" \ " var stream = new @ReadableStream(instance);\n" \ - " @putByIdDirectPrivate(stream, \"bunNativeTag\", nativeID);\n" \ + " @putByIdDirectPrivate(stream, \"bunNativeType\", nativeType);\n" \ + " @putByIdDirectPrivate(stream, \"bunNativePtr\", nativePtr);\n" \ " return stream;\n" \ "})\n" \ ; @@ -219,7 +220,7 @@ const char* const s_readableStreamCancelCode = const JSC::ConstructAbility s_readableStreamGetReaderCodeConstructAbility = JSC::ConstructAbility::CannotConstruct; const JSC::ConstructorKind s_readableStreamGetReaderCodeConstructorKind = JSC::ConstructorKind::None; -const int s_readableStreamGetReaderCodeLength = 476; +const int s_readableStreamGetReaderCodeLength = 481; static const JSC::Intrinsic s_readableStreamGetReaderCodeIntrinsic = JSC::NoIntrinsic; const char* const s_readableStreamGetReaderCode = "(function (options)\n" \ @@ -237,6 +238,7 @@ const char* const s_readableStreamGetReaderCode = " if (mode == 'byob')\n" \ " return new @ReadableStreamBYOBReader(this);\n" \ "\n" \ + " \n" \ " @throwTypeError(\"Invalid mode is specified\");\n" \ "})\n" \ ; diff --git a/src/javascript/jsc/bindings/ReadableStreamDefaultReaderBuiltins.cpp b/src/javascript/jsc/bindings/ReadableStreamDefaultReaderBuiltins.cpp index 74427beb64..e1933d425e 100644 --- a/src/javascript/jsc/bindings/ReadableStreamDefaultReaderBuiltins.cpp +++ b/src/javascript/jsc/bindings/ReadableStreamDefaultReaderBuiltins.cpp @@ -89,7 +89,7 @@ const char* const s_readableStreamDefaultReaderCancelCode = const JSC::ConstructAbility s_readableStreamDefaultReaderReadManyCodeConstructAbility = JSC::ConstructAbility::CannotConstruct; const JSC::ConstructorKind s_readableStreamDefaultReaderReadManyCodeConstructorKind = JSC::ConstructorKind::None; -const int s_readableStreamDefaultReaderReadManyCodeLength = 2999; +const int s_readableStreamDefaultReaderReadManyCodeLength = 3797; static const JSC::Intrinsic s_readableStreamDefaultReaderReadManyCodeIntrinsic = JSC::NoIntrinsic; const char* const s_readableStreamDefaultReaderReadManyCode = "(function ()\n" \ @@ -104,30 +104,37 @@ const char* const s_readableStreamDefaultReaderReadManyCode = " @throwTypeError(\"readMany() called on a reader owned by no readable stream\");\n" \ "\n" \ " const state = @getByIdDirectPrivate(stream, \"state\");\n" \ - " const wasDisturbed = @getByIdDirectPrivate(stream, \"disturbed\");\n" \ " @putByIdDirectPrivate(stream, \"disturbed\", true);\n" \ " if (state === @streamClosed)\n" \ - " return {value: [], done: true};\n" \ + " return {value: [], size: 0, done: true};\n" \ " else if (state === @streamErrored) {\n" \ " throw @getByIdDirectPrivate(stream, \"storedError\");\n" \ " }\n" \ "\n" \ " const controller = @getByIdDirectPrivate(stream, \"readableStreamController\");\n" \ + " \n" \ " const content = @getByIdDirectPrivate(controller, \"queue\").content;\n" \ + " var size = @getByIdDirectPrivate(controller, \"queue\").size;\n" \ "\n" \ " var values = new @Array(content.length);\n" \ "\n" \ " if (content.length > 0) {\n" \ " if (\"buffer\" in content[0]) {\n" \ + " values[0] = new @Uint8Array(content[0].buffer, content[0].byteOffset, content[0].byteLength);\n" \ " for (var i = 0; i < content.length; ++i) {\n" \ - " @putByValDirect(values, i, new @Uint8Array(content[i].buffer, content[i].byteOffset, content[i].byteLength));\n" \ + " @putByValDirect(values, i+1, new @Uint8Array(content[i].buffer, content[i].byteOffset, content[i].byteLength));\n" \ + " }\n" \ + " } else if (typeof content[0] === 'object' && content[0] && \"byteLength\" in content[0]) {\n" \ + " size = 0;\n" \ + " for (var i = 0; i < content.length; ++i) {\n" \ + " @putByValDirect(values, i+1, content[i].value);\n" \ + " size += content[i].value.byteLength;\n" \ " }\n" \ " } else {\n" \ " for (var i = 0; i < content.length; ++i) {\n" \ - " @putByValDirect(values, i, content[i].value);\n" \ + " @putByValDirect(values, i+1, content[i].value);\n" \ " }\n" \ " }\n" \ - "\n" \ " @resetQueue(@getByIdDirectPrivate(controller, \"queue\"));\n" \ "\n" \ " if (@getByIdDirectPrivate(controller, \"closeRequested\"))\n" \ @@ -137,19 +144,28 @@ const char* const s_readableStreamDefaultReaderReadManyCode = " } else {\n" \ " return controller.@pull(controller).@then(({value, done}) => {\n" \ " if (done) {\n" \ - " return {value: [], done: true};\n" \ + " return {value: [], size: 0, done: true};\n" \ " }\n" \ "\n" \ - " const content = @getByIdDirectPrivate(controller, \"queue\").content;\n" \ + " const content = queue.content;\n" \ " var values = new @Array(content.length + 1);\n" \ " \n" \ - "\n" \ + " var size = queue.size;\n" \ "\n" \ " if (\"buffer\" in content[0]) {\n" \ - " values[0] = new @Uint8Array(content[0].buffer, content[0].byteOffset, content[0].byteLength);\n" \ + " values[0] = new @Uint8Array(value.buffer, value.byteOffset, value.byteLength);\n" \ " for (var i = 0; i < content.length; ++i) {\n" \ " @putByValDirect(values, i+1, new @Uint8Array(content[i].buffer, content[i].byteOffset, content[i].byteLength));\n" \ " }\n" \ + " size += value.byteLength;\n" \ + " } else if (typeof value === 'object' && value && \"byteLength\" in value) {\n" \ + " size += value.byteLength;\n" \ + " values[0] = value;\n" \ + " for (var i = 0; i < content.length; ++i) {\n" \ + " @putByValDirect(values, i+1, content[i].value);\n" \ + " size += content[i].value.byteLength;\n" \ + " }\n" \ + "\n" \ " } else {\n" \ " values[0] = value;\n" \ " for (var i = 0; i < content.length; ++i) {\n" \ @@ -157,20 +173,18 @@ const char* const s_readableStreamDefaultReaderReadManyCode = " }\n" \ " }\n" \ "\n" \ - " @resetQueue(@getByIdDirectPrivate(controller, \"queue\"));\n" \ + " @resetQueue(queue);\n" \ "\n" \ " if (@getByIdDirectPrivate(controller, \"closeRequested\"))\n" \ " @readableStreamClose(@getByIdDirectPrivate(controller, \"controlledReadableStream\"));\n" \ " else\n" \ " @readableStreamDefaultControllerCallPullIfNeeded(controller);\n" \ "\n" \ - " return {value: values, done: false};\n" \ + " return {value: values, size: size, done: false};\n" \ " });\n" \ " }\n" \ "\n" \ - " \n" \ - "\n" \ - " return {value: values, done: false};\n" \ + " return {value: values, size, done: false};\n" \ "})\n" \ ; diff --git a/src/javascript/jsc/bindings/ReadableStreamInternalsBuiltins.cpp b/src/javascript/jsc/bindings/ReadableStreamInternalsBuiltins.cpp index 6b01e8e4c8..aa3e65eb0d 100644 --- a/src/javascript/jsc/bindings/ReadableStreamInternalsBuiltins.cpp +++ b/src/javascript/jsc/bindings/ReadableStreamInternalsBuiltins.cpp @@ -950,7 +950,7 @@ const char* const s_readableStreamInternalsReadableStreamFulfillReadRequestCode const JSC::ConstructAbility s_readableStreamInternalsReadableStreamDefaultControllerEnqueueCodeConstructAbility = JSC::ConstructAbility::CannotConstruct; const JSC::ConstructorKind s_readableStreamInternalsReadableStreamDefaultControllerEnqueueCodeConstructorKind = JSC::ConstructorKind::None; -const int s_readableStreamInternalsReadableStreamDefaultControllerEnqueueCodeLength = 979; +const int s_readableStreamInternalsReadableStreamDefaultControllerEnqueueCodeLength = 917; static const JSC::Intrinsic s_readableStreamInternalsReadableStreamDefaultControllerEnqueueCodeIntrinsic = JSC::NoIntrinsic; const char* const s_readableStreamInternalsReadableStreamDefaultControllerEnqueueCode = "(function (controller, chunk)\n" \ @@ -958,7 +958,8 @@ const char* const s_readableStreamInternalsReadableStreamDefaultControllerEnqueu " \"use strict\";\n" \ "\n" \ " const stream = @getByIdDirectPrivate(controller, \"controlledReadableStream\");\n" \ - " @assert(@readableStreamDefaultControllerCanCloseOrEnqueue(controller));\n" \ + " //\n" \ + " //\n" \ "\n" \ " if (@isReadableStreamLocked(stream) && @getByIdDirectPrivate(@getByIdDirectPrivate(stream, \"reader\"), \"readRequests\").length) {\n" \ " @readableStreamFulfillReadRequest(stream, chunk, false);\n" \ diff --git a/src/javascript/jsc/bindings/ZigGlobalObject.cpp b/src/javascript/jsc/bindings/ZigGlobalObject.cpp index 6c119ac2b7..36a49312f5 100644 --- a/src/javascript/jsc/bindings/ZigGlobalObject.cpp +++ b/src/javascript/jsc/bindings/ZigGlobalObject.cpp @@ -165,6 +165,8 @@ extern "C" void JSCInitialize() } } +extern "C" void* Bun__getVM(); + extern "C" JSC__JSGlobalObject* Zig__GlobalObject__create(JSClassRef* globalObjectClass, int count, void* console_client) { @@ -181,7 +183,7 @@ extern "C" JSC__JSGlobalObject* Zig__GlobalObject__create(JSClassRef* globalObje JSC::JSLockHolder locker(vm); Zig::GlobalObject* globalObject = Zig::GlobalObject::create(vm, Zig::GlobalObject::createStructure(vm, JSC::JSGlobalObject::create(vm, JSC::JSGlobalObject::createStructure(vm, JSC::jsNull())), JSC::jsNull())); globalObject->setConsole(globalObject); - + globalObject->isThreadLocalDefaultGlobalObject = true; if (count > 0) { globalObject->installAPIGlobals(globalObjectClass, count, vm); } @@ -325,6 +327,7 @@ GlobalObject::GlobalObject(JSC::VM& vm, JSC::Structure* structure) , m_builtinInternalFunctions(vm) { + m_bunVM = Bun__getVM(); m_scriptExecutionContext = new WebCore::ScriptExecutionContext(&vm, this); } @@ -1205,8 +1208,8 @@ JSC_DEFINE_HOST_FUNCTION(isAbortSignal, (JSGlobalObject*, CallFrame* callFrame)) return JSValue::encode(jsBoolean(callFrame->uncheckedArgument(0).inherits())); } -extern "C" JSC__JSValue ZigGlobalObject__createNativeReadableStream(Zig::GlobalObject* globalObject, JSC__JSValue nativeID, JSC__JSValue nativeTag); -extern "C" JSC__JSValue ZigGlobalObject__createNativeReadableStream(Zig::GlobalObject* globalObject, JSC__JSValue nativeID, JSC__JSValue nativeTag) +extern "C" JSC__JSValue ZigGlobalObject__createNativeReadableStream(Zig::GlobalObject* globalObject, JSC__JSValue nativeType, JSC__JSValue nativePtr); +extern "C" JSC__JSValue ZigGlobalObject__createNativeReadableStream(Zig::GlobalObject* globalObject, JSC__JSValue nativeType, JSC__JSValue nativePtr) { auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); @@ -1216,8 +1219,8 @@ extern "C" JSC__JSValue ZigGlobalObject__createNativeReadableStream(Zig::GlobalO auto function = globalObject->getDirect(vm, builtinNames.createNativeReadableStreamPrivateName()).getObject(); JSC::MarkedArgumentBuffer arguments = JSC::MarkedArgumentBuffer(); - arguments.append(JSValue::decode(nativeID)); - arguments.append(JSValue::decode(nativeTag)); + arguments.append(JSValue::decode(nativeType)); + arguments.append(JSValue::decode(nativePtr)); auto callData = JSC::getCallData(function); return JSC::JSValue::encode(call(globalObject, function, callData, JSC::jsUndefined(), arguments)); @@ -1716,9 +1719,14 @@ JSC::JSValue GlobalObject::moduleLoaderEvaluate(JSGlobalObject* globalObject, void GlobalObject::queueMicrotaskToEventLoop(JSC::JSGlobalObject& global, Ref&& task) { - - Zig__GlobalObject__queueMicrotaskToEventLoop( - &global, &JSMicrotaskCallback::create(global, WTFMove(task)).leakRef()); + auto& globalObject = reinterpret_cast(global); + if (globalObject.isThreadLocalDefaultGlobalObject) { + Zig__GlobalObject__queueMicrotaskToEventLoop( + &global, reinterpret_cast(&JSMicrotaskCallbackDefaultGlobal::create(WTFMove(task)).leakRef())); + } else { + Zig__GlobalObject__queueMicrotaskToEventLoop( + &global, &JSMicrotaskCallback::create(global, WTFMove(task)).leakRef()); + } } } // namespace Zig diff --git a/src/javascript/jsc/bindings/ZigGlobalObject.h b/src/javascript/jsc/bindings/ZigGlobalObject.h index 933764be93..4a5b0715b3 100644 --- a/src/javascript/jsc/bindings/ZigGlobalObject.h +++ b/src/javascript/jsc/bindings/ZigGlobalObject.h @@ -149,6 +149,9 @@ public: JSC::Structure* FFIFunctionStructure() { return m_JSFFIFunctionStructure.getInitializedOnMainThread(this); } JSC::Structure* NapiClassStructure() { return m_NapiClassStructure.getInitializedOnMainThread(this); } + void* bunVM() { return m_bunVM; } + bool isThreadLocalDefaultGlobalObject = false; + private: void addBuiltinGlobals(JSC::VM&); void finishCreation(JSC::VM&); @@ -164,9 +167,36 @@ private: LazyClassStructure m_JSFFIFunctionStructure; LazyClassStructure m_NapiClassStructure; DOMGuardedObjectSet m_guardedObjects WTF_GUARDED_BY_LOCK(m_gcLock); + void* m_bunVM; }; -class JSMicrotaskCallback : public RefCounted { +class JSMicrotaskCallbackDefaultGlobal final : public RefCounted { +public: + static Ref create(Ref&& task) + { + return adoptRef(*new JSMicrotaskCallbackDefaultGlobal(WTFMove(task).leakRef())); + } + + void call(JSC::JSGlobalObject* globalObject) + { + + JSC::VM& vm = globalObject->vm(); + auto task = &m_task.leakRef(); + task->run(globalObject); + + delete this; + } + +private: + JSMicrotaskCallbackDefaultGlobal(Ref&& task) + : m_task { WTFMove(task) } + { + } + + Ref m_task; +}; + +class JSMicrotaskCallback final : public RefCounted { public: static Ref create(JSC::JSGlobalObject& globalObject, Ref&& task) @@ -176,21 +206,27 @@ public: void call() { + auto* globalObject = m_globalObject.get(); + if (UNLIKELY(!globalObject)) { + delete this; + return; + } + JSC::VM& vm = m_globalObject->vm(); auto task = &m_task.leakRef(); - task->run(m_globalObject.get()); + task->run(globalObject); - task->~Microtask(); + delete this; } private: JSMicrotaskCallback(JSC::JSGlobalObject& globalObject, Ref&& task) - : m_globalObject { globalObject.vm(), &globalObject } + : m_globalObject { &globalObject } , m_task { WTFMove(task) } { } - JSC::Strong m_globalObject; + JSC::Weak m_globalObject; Ref m_task; }; diff --git a/src/javascript/jsc/bindings/bindings.cpp b/src/javascript/jsc/bindings/bindings.cpp index 2d21ec9ccd..30848ca76d 100644 --- a/src/javascript/jsc/bindings/bindings.cpp +++ b/src/javascript/jsc/bindings/bindings.cpp @@ -505,7 +505,7 @@ JSC__JSPromise* JSC__JSPromise__create(JSC__JSGlobalObject* arg0) } // TODO: prevent this from allocating so much memory -void JSC__JSValue___then(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObject, void* ctx, void (*ArgFn3)(JSC__JSGlobalObject* arg0, void* arg1, JSC__JSValue arg2, size_t arg3), void (*ArgFn4)(JSC__JSGlobalObject* arg0, void* arg1, JSC__JSValue arg2, size_t arg3)) +void JSC__JSValue___then(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObject, void* ctx, void (*ArgFn3)(JSC__JSGlobalObject* arg0, void* arg1, void** arg2, size_t arg3), void (*ArgFn4)(JSC__JSGlobalObject* arg0, void* arg1, void** arg2, size_t arg3)) { globalObject->vm().drainMicrotasks(); @@ -513,7 +513,7 @@ void JSC__JSValue___then(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObjec JSC::Strong promiseValue = { globalObject->vm(), cell }; JSC::JSNativeStdFunction* resolverFunction = JSC::JSNativeStdFunction::create( - globalObject->vm(), globalObject, 1, String(), [&promiseValue, ctx, ArgFn3](JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -> JSC::EncodedJSValue { + globalObject->vm(), globalObject, 1, String(), [promiseValue, ctx, ArgFn3](JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -> JSC::EncodedJSValue { auto argCount = static_cast(callFrame->argumentCount()); WTF::Vector arguments; @@ -524,13 +524,13 @@ void JSC__JSValue___then(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObjec } } - ArgFn3(globalObject, ctx, reinterpret_cast(arguments.data()), argCount); + ArgFn3(globalObject, ctx, reinterpret_cast(arguments.data()), argCount); return JSC::JSValue::encode(JSC::jsUndefined()); }); JSC::JSNativeStdFunction* rejecterFunction = JSC::JSNativeStdFunction::create( globalObject->vm(), globalObject, 1, String(), - [&promiseValue, ctx, ArgFn4](JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -> JSC::EncodedJSValue { + [promiseValue, ctx, ArgFn4](JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -> JSC::EncodedJSValue { auto argCount = static_cast(callFrame->argumentCount()); WTF::Vector arguments; arguments.reserveInitialCapacity(argCount); @@ -540,7 +540,7 @@ void JSC__JSValue___then(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObjec } } - ArgFn4(globalObject, ctx, reinterpret_cast(arguments.data()), argCount); + ArgFn4(globalObject, ctx, reinterpret_cast(arguments.data()), argCount); return JSC::JSValue::encode(JSC::jsUndefined()); }); @@ -806,6 +806,11 @@ void Microtask__run(void* microtask, void* global) reinterpret_cast(microtask)->call(); } +void Microtask__run_default(void* microtask, void* global) +{ + reinterpret_cast(microtask)->call(reinterpret_cast(global)); +} + bool JSC__JSModuleLoader__checkSyntax(JSC__JSGlobalObject* arg0, const JSC__SourceCode* arg1, bool arg2) { @@ -1098,6 +1103,11 @@ JSC__JSValue ZigString__toExternalValue(const ZigString* arg0, JSC__JSGlobalObje ExternalStringImpl::create(Zig::untag(str.ptr), str.len, nullptr, free_global_string)))); } +VirtualMachine* JSC__JSGlobalObject__bunVM(JSC__JSGlobalObject* arg0) +{ + return reinterpret_cast(reinterpret_cast(arg0)->bunVM()); +} + JSC__JSValue ZigString__toValueGC(const ZigString* arg0, JSC__JSGlobalObject* arg1) { return JSC::JSValue::encode(JSC::JSValue(JSC::jsString(arg1->vm(), Zig::toStringCopy(*arg0)))); diff --git a/src/javascript/jsc/bindings/bindings.zig b/src/javascript/jsc/bindings/bindings.zig index 761d79f24a..bb678d4b5f 100644 --- a/src/javascript/jsc/bindings/bindings.zig +++ b/src/javascript/jsc/bindings/bindings.zig @@ -1904,19 +1904,27 @@ pub const String = extern struct { }; }; -pub const JSValue = enum(u64) { +pub const JSValue = enum(i64) { @"undefined" = 0xa, _, pub const shim = Shimmer("JSC", "JSValue", @This()); pub const is_pointer = false; - pub const Type = u64; + pub const Type = i64; const cppFn = shim.cppFn; + pub const Encoded = extern union { + asInt64: i64, + ptr: *JSCell, + asBits: extern struct { payload: i32, tag: i32 }, + asPtr: *anyopaque, + asDouble: f64, + }; + pub const include = "JavaScriptCore/JSValue.h"; pub const name = "JSC::JSValue"; pub const namespace = "JSC"; - pub const zero = @intToEnum(JSValue, @as(u64, 0)); + pub const zero = @intToEnum(JSValue, @as(i64, 0)); pub const JSType = enum(u8) { // The Cell value must come before any JS that is a JSCell. Cell, @@ -2401,7 +2409,14 @@ pub const JSValue = enum(u64) { } pub fn toInt64(this: JSValue) i64 { - return cppFn("toInt64", .{this}); + // these magic numbers came from zig translate-c on FFI.h + // FFI.h came from reading JSCJSValue source code + return switch (@bitCast(c_ulonglong, @enumToInt(this)) & @as(c_ulonglong, 18446181123756130304)) { + 0 => cppFn("toInt64", .{this}), + // int32 + 18446181123756130304 => toInt32(this), + else => @bitCast(i64, @trunc(this.asDouble())), + }; } pub inline fn isUndefined(this: JSValue) bool { @@ -2438,15 +2453,23 @@ pub const JSValue = enum(u64) { pub fn isUInt32AsAnyInt(this: JSValue) bool { return cppFn("isUInt32AsAnyInt", .{this}); } - pub fn isInt32(this: JSValue) bool { - return cppFn("isInt32", .{this}); + + pub fn asEncoded(this: JSValue) Encoded { + return @bitCast(Encoded, this); } + + pub fn isInt32(this: JSValue) bool { + return (@bitCast(c_ulonglong, @enumToInt(this)) & @as(c_ulonglong, 18446181123756130304)) == @as(c_ulonglong, 18446181123756130304); + } + pub fn isInt32AsAnyInt(this: JSValue) bool { return cppFn("isInt32AsAnyInt", .{this}); } + pub fn isNumber(this: JSValue) bool { - return cppFn("isNumber", .{this}); + return (@bitCast(c_ulonglong, @enumToInt(this)) & @as(c_ulonglong, 18446181123756130304)) != 0; } + pub fn isError(this: JSValue) bool { return cppFn("isError", .{this}); } @@ -2724,13 +2747,31 @@ pub const JSValue = enum(u64) { } pub fn asNumber(this: JSValue) f64 { + if (this.isInt32()) { + return @intToFloat(f64, this.toInt32()); + } + + if (isNumber(this)) { + return asDouble(this); + } + + if (this.isUndefinedOrNull()) { + return 0.0; + } else if (this.isBoolean()) { + return if (asBoolean(this)) 1.0 else 0.0; + } + return cppFn("asNumber", .{ this, }); } + pub fn asDouble(this: JSValue) f64 { + return @bitCast(f64, @enumToInt(this) - comptime (@as(c_longlong, 1) << @intCast(@import("std").math.Log2Int(c_longlong), 49))); + } + pub fn asPtr(this: JSValue, comptime Pointer: type) *Pointer { - return @intToPtr(*Pointer, @bitCast(usize, this.asNumber())); + return @intToPtr(*Pointer, @bitCast(usize, @enumToInt(this))); } pub fn fromPtrAddress(addr: anytype) JSValue { @@ -2742,15 +2783,33 @@ pub const JSValue = enum(u64) { } pub fn toBoolean(this: JSValue) bool { - return cppFn("toBoolean", .{ + if (isUndefinedOrNull(this)) { + return false; + } + + return asBoolean(this); + } + + pub fn asBoolean(this: JSValue) bool { + return @enumToInt(this) == @bitCast(c_longlong, @as(c_longlong, (@as(c_int, 2) | @as(c_int, 4)) | @as(c_int, 1))); + } + + pub fn toInt32(this: JSValue) i32 { + if (this.isInt32()) { + return asInt32(this); + } + + if (this.isNumber()) { + return @truncate(i32, @floatToInt(i64, asDouble(this))); + } + + return cppFn("toInt32", .{ this, }); } - pub fn toInt32(this: JSValue) i32 { - return cppFn("toInt32", .{ - this, - }); + pub fn asInt32(this: JSValue) i32 { + return @bitCast(i32, @truncate(c_int, @enumToInt(this))); } pub inline fn toU16(this: JSValue) u16 { @@ -2831,6 +2890,7 @@ pub const JSValue = enum(u64) { }; extern "c" fn Microtask__run(*Microtask, *JSGlobalObject) void; +extern "c" fn Microtask__run_default(*MicrotaskForDefaultGlobalObject, *JSGlobalObject) void; pub const Microtask = opaque { pub const name = "Zig::JSMicrotaskCallback"; @@ -2845,6 +2905,16 @@ pub const Microtask = opaque { } }; +pub const MicrotaskForDefaultGlobalObject = opaque { + pub fn run(this: *MicrotaskForDefaultGlobalObject, global_object: *JSGlobalObject) void { + if (comptime is_bindgen) { + return; + } + + return Microtask__run_default(this, global_object); + } +}; + pub const PropertyName = extern struct { pub const shim = Shimmer("JSC", "PropertyName", @This()); bytes: shim.Bytes, diff --git a/src/javascript/jsc/bindings/builtins/js/ReadableByteStreamInternals.js b/src/javascript/jsc/bindings/builtins/js/ReadableByteStreamInternals.js index b2a16d3eac..d6259f2158 100644 --- a/src/javascript/jsc/bindings/builtins/js/ReadableByteStreamInternals.js +++ b/src/javascript/jsc/bindings/builtins/js/ReadableByteStreamInternals.js @@ -263,7 +263,7 @@ function readableByteStreamControllerShouldCallPull(controller) return false; if (!@getByIdDirectPrivate(controller, "started")) return false; - if (@readableStreamHasDefaultReader(stream) && @getByIdDirectPrivate(@getByIdDirectPrivate(stream, "reader"), "readRequests").length > 0) + if (@readableStreamHasDefaultReader(stream) && (@getByIdDirectPrivate(@getByIdDirectPrivate(stream, "reader"), "readRequests").length > 0 || !!@getByIdDirectPrivate(reader, "bunNativePtr"))) return true; if (@readableStreamHasBYOBReader(stream) && @getByIdDirectPrivate(@getByIdDirectPrivate(stream, "reader"), "readIntoRequests").length > 0) return true; @@ -309,6 +309,18 @@ function transferBufferToCurrentRealm(buffer) return buffer; } +function readableStreamReaderKind(reader) { + "use strict"; + + + if (!!@getByIdDirectPrivate(reader, "readRequests")) + return @getByIdDirectPrivate(reader, "bunNativePtr") ? 3 : 1; + + if (!!@getByIdDirectPrivate(reader, "readIntoRequests")) + return 2; + + return 0; +} function readableByteStreamControllerEnqueue(controller, chunk) { "use strict"; @@ -316,28 +328,42 @@ function readableByteStreamControllerEnqueue(controller, chunk) const stream = @getByIdDirectPrivate(controller, "controlledReadableStream"); @assert(!@getByIdDirectPrivate(controller, "closeRequested")); @assert(@getByIdDirectPrivate(stream, "state") === @streamReadable); - const byteOffset = chunk.byteOffset; - const byteLength = chunk.byteLength; + var reader = @getByIdDirectPrivate(stream, "reader"); - if (@readableStreamHasDefaultReader(stream)) { - if (!@getByIdDirectPrivate(@getByIdDirectPrivate(stream, "reader"), "readRequests").length) - @readableByteStreamControllerEnqueueChunk(controller, @transferBufferToCurrentRealm(chunk.buffer), byteOffset, byteLength); - else { - @assert(!@getByIdDirectPrivate(controller, "queue").content.length); - const transferredView = chunk.constructor === @Uint8Array ? chunk : new @Uint8Array(chunk.buffer, byteOffset, byteLength); - @readableStreamFulfillReadRequest(stream, transferredView, false); + + switch (@readableStreamReaderKind(reader)) { + /* default reader */ + case 1: { + if (!@getByIdDirectPrivate(reader, "readRequests").length) + @readableByteStreamControllerEnqueueChunk(controller, @transferBufferToCurrentRealm(chunk.buffer), chunk.byteOffset, chunk.byteLength); + else { + @assert(!@getByIdDirectPrivate(controller, "queue").content.length); + const transferredView = chunk.constructor === @Uint8Array ? chunk : new @Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength); + @readableStreamFulfillReadRequest(stream, transferredView, false); + } + break; } - return; - } - if (@readableStreamHasBYOBReader(stream)) { - @readableByteStreamControllerEnqueueChunk(controller, @transferBufferToCurrentRealm(chunk.buffer), byteOffset, byteLength); - @readableByteStreamControllerProcessPullDescriptors(controller); - return; - } + /* BYOB */ + case 2: { + @readableByteStreamControllerEnqueueChunk(controller, @transferBufferToCurrentRealm(chunk.buffer), chunk.byteOffset, chunk.byteLength); + @readableByteStreamControllerProcessPullDescriptors(controller); + break; + } - @assert(!@isReadableStreamLocked(stream)); - @readableByteStreamControllerEnqueueChunk(controller, @transferBufferToCurrentRealm(chunk.buffer), byteOffset, byteLength); + /* NativeReader */ + case 3: { + // reader.@enqueueNative(@getByIdDirectPrivate(reader, "bunNativePtr"), chunk); + + break; + } + + default: { + @assert(!@isReadableStreamLocked(stream)); + @readableByteStreamControllerEnqueueChunk(controller, @transferBufferToCurrentRealm(chunk.buffer), chunk.byteOffset, chunk.byteLength); + break; + } + } } // Spec name: readableByteStreamControllerEnqueueChunkToQueue. diff --git a/src/javascript/jsc/bindings/builtins/js/ReadableStream.js b/src/javascript/jsc/bindings/builtins/js/ReadableStream.js index a838d48f54..5f977cd3ad 100644 --- a/src/javascript/jsc/bindings/builtins/js/ReadableStream.js +++ b/src/javascript/jsc/bindings/builtins/js/ReadableStream.js @@ -89,12 +89,12 @@ function initializeReadableStream(underlyingSource, strategy) } @globalPrivate -function createNativeReadableStream(nativeTag, nativeID) { +function createNativeReadableStream(nativePtr, nativeType) { "use strict"; var cached = globalThis[Symbol.for("Bun.nativeReadableStreamPrototype")] ||= new @Map; - var Prototype = cached.@get(nativeID); + var Prototype = cached.@get(nativeType); if (Prototype === @undefined) { - var [pull, start, cancel, setClose, deinit] = globalThis[Symbol.for("Bun.lazy")](nativeID); + var [pull, start, cancel, setClose, deinit] = globalThis[Symbol.for("Bun.lazy")](nativeType); var closer = [false]; var handleResult = function handleResult(result, controller) { @@ -154,13 +154,14 @@ function createNativeReadableStream(nativeTag, nativeID) { static registry = new FinalizationRegistry(deinit); } - cached.@set(nativeID, Prototype); + cached.@set(nativeType, Prototype); } - var instance = new Prototype(nativeTag); - Prototype.registry.register(instance, nativeTag); + var instance = new Prototype(nativePtr); + Prototype.registry.register(instance, nativePtr); var stream = new @ReadableStream(instance); - @putByIdDirectPrivate(stream, "bunNativeTag", nativeID); + @putByIdDirectPrivate(stream, "bunNativeType", nativeType); + @putByIdDirectPrivate(stream, "bunNativePtr", nativePtr); return stream; } @@ -192,6 +193,7 @@ function getReader(options) if (mode == 'byob') return new @ReadableStreamBYOBReader(this); + @throwTypeError("Invalid mode is specified"); } diff --git a/src/javascript/jsc/bindings/builtins/js/ReadableStreamDefaultReader.js b/src/javascript/jsc/bindings/builtins/js/ReadableStreamDefaultReader.js index b611db8b0e..3e7438a622 100644 --- a/src/javascript/jsc/bindings/builtins/js/ReadableStreamDefaultReader.js +++ b/src/javascript/jsc/bindings/builtins/js/ReadableStreamDefaultReader.js @@ -63,30 +63,37 @@ function readMany() @throwTypeError("readMany() called on a reader owned by no readable stream"); const state = @getByIdDirectPrivate(stream, "state"); - const wasDisturbed = @getByIdDirectPrivate(stream, "disturbed"); @putByIdDirectPrivate(stream, "disturbed", true); if (state === @streamClosed) - return {value: [], done: true}; + return {value: [], size: 0, done: true}; else if (state === @streamErrored) { throw @getByIdDirectPrivate(stream, "storedError"); } const controller = @getByIdDirectPrivate(stream, "readableStreamController"); + const content = @getByIdDirectPrivate(controller, "queue").content; + var size = @getByIdDirectPrivate(controller, "queue").size; var values = new @Array(content.length); if (content.length > 0) { if ("buffer" in content[0]) { + values[0] = new @Uint8Array(content[0].buffer, content[0].byteOffset, content[0].byteLength); for (var i = 0; i < content.length; ++i) { - @putByValDirect(values, i, new @Uint8Array(content[i].buffer, content[i].byteOffset, content[i].byteLength)); + @putByValDirect(values, i+1, new @Uint8Array(content[i].buffer, content[i].byteOffset, content[i].byteLength)); + } + } else if (typeof content[0] === 'object' && content[0] && "byteLength" in content[0]) { + size = 0; + for (var i = 0; i < content.length; ++i) { + @putByValDirect(values, i+1, content[i].value); + size += content[i].value.byteLength; } } else { for (var i = 0; i < content.length; ++i) { - @putByValDirect(values, i, content[i].value); + @putByValDirect(values, i+1, content[i].value); } } - @resetQueue(@getByIdDirectPrivate(controller, "queue")); if (@getByIdDirectPrivate(controller, "closeRequested")) @@ -96,19 +103,28 @@ function readMany() } else { return controller.@pull(controller).@then(({value, done}) => { if (done) { - return {value: [], done: true}; + return {value: [], size: 0, done: true}; } - const content = @getByIdDirectPrivate(controller, "queue").content; + const content = queue.content; var values = new @Array(content.length + 1); - + var size = queue.size; if ("buffer" in content[0]) { - values[0] = new @Uint8Array(content[0].buffer, content[0].byteOffset, content[0].byteLength); + values[0] = new @Uint8Array(value.buffer, value.byteOffset, value.byteLength); for (var i = 0; i < content.length; ++i) { @putByValDirect(values, i+1, new @Uint8Array(content[i].buffer, content[i].byteOffset, content[i].byteLength)); } + size += value.byteLength; + } else if (typeof value === 'object' && value && "byteLength" in value) { + size += value.byteLength; + values[0] = value; + for (var i = 0; i < content.length; ++i) { + @putByValDirect(values, i+1, content[i].value); + size += content[i].value.byteLength; + } + } else { values[0] = value; for (var i = 0; i < content.length; ++i) { @@ -116,18 +132,18 @@ function readMany() } } - @resetQueue(@getByIdDirectPrivate(controller, "queue")); + @resetQueue(queue); if (@getByIdDirectPrivate(controller, "closeRequested")) @readableStreamClose(@getByIdDirectPrivate(controller, "controlledReadableStream")); else @readableStreamDefaultControllerCallPullIfNeeded(controller); - return {value: values, done: false}; + return {value: values, size: size, done: false}; }); } - return {value: values, done: false}; + return {value: values, size, done: false}; } function read() diff --git a/src/javascript/jsc/bindings/builtins/js/ReadableStreamInternals.js b/src/javascript/jsc/bindings/builtins/js/ReadableStreamInternals.js index f102e4e2f8..9284ad2082 100644 --- a/src/javascript/jsc/bindings/builtins/js/ReadableStreamInternals.js +++ b/src/javascript/jsc/bindings/builtins/js/ReadableStreamInternals.js @@ -727,7 +727,8 @@ function readableStreamDefaultControllerEnqueue(controller, chunk) "use strict"; const stream = @getByIdDirectPrivate(controller, "controlledReadableStream"); - @assert(@readableStreamDefaultControllerCanCloseOrEnqueue(controller)); + // this is checked by callers + // @assert(@readableStreamDefaultControllerCanCloseOrEnqueue(controller)); if (@isReadableStreamLocked(stream) && @getByIdDirectPrivate(@getByIdDirectPrivate(stream, "reader"), "readRequests").length) { @readableStreamFulfillReadRequest(stream, chunk, false); diff --git a/src/javascript/jsc/bindings/headers-cpp.h b/src/javascript/jsc/bindings/headers-cpp.h index f3fbbc79b8..95203639e8 100644 --- a/src/javascript/jsc/bindings/headers-cpp.h +++ b/src/javascript/jsc/bindings/headers-cpp.h @@ -1,4 +1,4 @@ -//-- AUTOGENERATED FILE -- 1653447610 +//-- AUTOGENERATED FILE -- 1653638459 // clang-format off #pragma once diff --git a/src/javascript/jsc/bindings/headers-handwritten.h b/src/javascript/jsc/bindings/headers-handwritten.h index 710e9290d5..7409427ac9 100644 --- a/src/javascript/jsc/bindings/headers-handwritten.h +++ b/src/javascript/jsc/bindings/headers-handwritten.h @@ -1,6 +1,7 @@ #pragma once typedef uint16_t ZigErrorCode; +typedef struct VirtualMachine VirtualMachine; typedef struct ZigString { const unsigned char* ptr; @@ -190,6 +191,7 @@ extern "C" ZigErrorCode Zig_ErrorCodeParserError; extern "C" void ZigString__free(const unsigned char* ptr, size_t len, void* allocator); extern "C" void Microtask__run(void* ptr, void* global); +extern "C" void Microtask__run_default(void* ptr, void* global); // Used in process.version extern "C" const char* Bun__version; diff --git a/src/javascript/jsc/bindings/headers-replacements.zig b/src/javascript/jsc/bindings/headers-replacements.zig index c503d32887..fb5cf1496e 100644 --- a/src/javascript/jsc/bindings/headers-replacements.zig +++ b/src/javascript/jsc/bindings/headers-replacements.zig @@ -62,3 +62,4 @@ pub const Bun__ArrayBuffer = bindings.ArrayBuffer; pub const struct_WebCore__DOMURL = bindings.DOMURL; pub const struct_WebCore__FetchHeaders = bindings.FetchHeaders; pub const StringPointer = @import("../../../api/schema.zig").Api.StringPointer; +pub const struct_VirtualMachine = bindings.VirtualMachine; diff --git a/src/javascript/jsc/bindings/headers.h b/src/javascript/jsc/bindings/headers.h index 0313a43837..6afdfcbcfc 100644 --- a/src/javascript/jsc/bindings/headers.h +++ b/src/javascript/jsc/bindings/headers.h @@ -1,5 +1,5 @@ // clang-format: off -//-- AUTOGENERATED FILE -- 1653447610 +//-- AUTOGENERATED FILE -- 1653638459 #pragma once #include @@ -95,13 +95,13 @@ typedef void* JSClassRef; typedef SystemError SystemError; typedef bJSC__JSCell JSC__JSCell; // JSC::JSCell typedef bJSC__SourceOrigin JSC__SourceOrigin; // JSC::SourceOrigin + typedef Bun__Writable Bun__Writable; typedef bJSC__JSModuleRecord JSC__JSModuleRecord; // JSC::JSModuleRecord typedef bWTF__String WTF__String; // WTF::String typedef bWTF__URL WTF__URL; // WTF::URL typedef struct JSC__IteratorPrototype JSC__IteratorPrototype; // JSC::IteratorPrototype - typedef Bun__Readable Bun__Readable; typedef bJSC__JSInternalPromise JSC__JSInternalPromise; // JSC::JSInternalPromise - typedef Bun__Writable Bun__Writable; + typedef Bun__Readable Bun__Readable; typedef struct JSC__RegExpPrototype JSC__RegExpPrototype; // JSC::RegExpPrototype typedef struct JSC__MapIteratorPrototype JSC__MapIteratorPrototype; // JSC::MapIteratorPrototype typedef struct WebCore__FetchHeaders WebCore__FetchHeaders; // WebCore::FetchHeaders @@ -116,11 +116,11 @@ typedef void* JSClassRef; typedef struct JSC__AsyncFunctionPrototype JSC__AsyncFunctionPrototype; // JSC::AsyncFunctionPrototype typedef ZigException ZigException; typedef bJSC__SourceCode JSC__SourceCode; // JSC::SourceCode - typedef uint64_t JSC__JSValue; typedef struct JSC__BigIntPrototype JSC__BigIntPrototype; // JSC::BigIntPrototype typedef struct JSC__GeneratorFunctionPrototype JSC__GeneratorFunctionPrototype; // JSC::GeneratorFunctionPrototype typedef ZigString ZigString; typedef struct WebCore__DOMURL WebCore__DOMURL; // WebCore::DOMURL + typedef int64_t JSC__JSValue; typedef struct JSC__FunctionPrototype JSC__FunctionPrototype; // JSC::FunctionPrototype typedef bInspector__ScriptArguments Inspector__ScriptArguments; // Inspector::ScriptArguments typedef bJSC__Exception JSC__Exception; // JSC::Exception @@ -188,13 +188,13 @@ typedef void* JSClassRef; typedef ErrorableResolvedSource ErrorableResolvedSource; typedef ErrorableZigString ErrorableZigString; typedef SystemError SystemError; - typedef Bun__Readable Bun__Readable; typedef Bun__Writable Bun__Writable; + typedef Bun__Readable Bun__Readable; typedef JSClassRef JSClassRef; typedef Bun__ArrayBuffer Bun__ArrayBuffer; typedef ZigException ZigException; - typedef uint64_t JSC__JSValue; typedef ZigString ZigString; + typedef int64_t JSC__JSValue; using JSC__JSCell = JSC::JSCell; using JSC__Exception = JSC::Exception; using JSC__JSPromisePrototype = JSC::JSPromisePrototype; @@ -376,6 +376,7 @@ CPP_DECL JSC__AsyncGeneratorPrototype* JSC__JSGlobalObject__asyncGeneratorProtot CPP_DECL JSC__AsyncIteratorPrototype* JSC__JSGlobalObject__asyncIteratorPrototype(JSC__JSGlobalObject* arg0); CPP_DECL JSC__BigIntPrototype* JSC__JSGlobalObject__bigIntPrototype(JSC__JSGlobalObject* arg0); CPP_DECL JSC__JSObject* JSC__JSGlobalObject__booleanPrototype(JSC__JSGlobalObject* arg0); +CPP_DECL VirtualMachine* JSC__JSGlobalObject__bunVM(JSC__JSGlobalObject* arg0); CPP_DECL JSC__JSValue JSC__JSGlobalObject__createAggregateError(JSC__JSGlobalObject* arg0, void** arg1, uint16_t arg2, const ZigString* arg3); CPP_DECL JSC__JSObject* JSC__JSGlobalObject__datePrototype(JSC__JSGlobalObject* arg0); CPP_DECL void JSC__JSGlobalObject__deleteModuleRegistryEntry(JSC__JSGlobalObject* arg0, ZigString* arg1); @@ -449,7 +450,7 @@ CPP_DECL size_t WTF__String__length(WTF__String* arg0); #pragma mark - JSC::JSValue -CPP_DECL void JSC__JSValue___then(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, void* arg2, void (* ArgFn3)(JSC__JSGlobalObject* arg0, void* arg1, JSC__JSValue JSValue2, size_t arg3), void (* ArgFn4)(JSC__JSGlobalObject* arg0, void* arg1, JSC__JSValue JSValue2, size_t arg3)); +CPP_DECL void JSC__JSValue___then(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, void* arg2, void (* ArgFn3)(JSC__JSGlobalObject* arg0, void* arg1, void** arg2, size_t arg3), void (* ArgFn4)(JSC__JSGlobalObject* arg0, void* arg1, void** arg2, size_t arg3)); CPP_DECL bool JSC__JSValue__asArrayBuffer_(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, Bun__ArrayBuffer* arg2); CPP_DECL JSC__JSCell* JSC__JSValue__asCell(JSC__JSValue JSValue0); CPP_DECL JSC__JSInternalPromise* JSC__JSValue__asInternalPromise(JSC__JSValue JSValue0); diff --git a/src/javascript/jsc/bindings/headers.zig b/src/javascript/jsc/bindings/headers.zig index 341d641095..b8b3535c8d 100644 --- a/src/javascript/jsc/bindings/headers.zig +++ b/src/javascript/jsc/bindings/headers.zig @@ -62,6 +62,7 @@ pub const Bun__ArrayBuffer = bindings.ArrayBuffer; pub const struct_WebCore__DOMURL = bindings.DOMURL; pub const struct_WebCore__FetchHeaders = bindings.FetchHeaders; pub const StringPointer = @import("../../../api/schema.zig").Api.StringPointer; +pub const struct_VirtualMachine = bindings.VirtualMachine; // GENERATED CODE - DO NOT MODIFY BY HAND pub const ptrdiff_t = c_long; @@ -76,6 +77,8 @@ pub const __mbstate_t = extern union { _mbstateL: c_longlong, }; +pub const VirtualMachine = struct_VirtualMachine; + pub const JSC__GeneratorPrototype = struct_JSC__GeneratorPrototype; pub const JSC__ArrayIteratorPrototype = struct_JSC__ArrayIteratorPrototype; @@ -233,6 +236,7 @@ pub extern fn JSC__JSGlobalObject__asyncGeneratorPrototype(arg0: [*c]JSC__JSGlob pub extern fn JSC__JSGlobalObject__asyncIteratorPrototype(arg0: [*c]JSC__JSGlobalObject) ?*JSC__AsyncIteratorPrototype; pub extern fn JSC__JSGlobalObject__bigIntPrototype(arg0: [*c]JSC__JSGlobalObject) ?*JSC__BigIntPrototype; pub extern fn JSC__JSGlobalObject__booleanPrototype(arg0: [*c]JSC__JSGlobalObject) [*c]JSC__JSObject; +pub extern fn JSC__JSGlobalObject__bunVM(arg0: [*c]JSC__JSGlobalObject) ?*VirtualMachine; pub extern fn JSC__JSGlobalObject__createAggregateError(arg0: [*c]JSC__JSGlobalObject, arg1: [*c]*anyopaque, arg2: u16, arg3: [*c]const ZigString) JSC__JSValue; pub extern fn JSC__JSGlobalObject__datePrototype(arg0: [*c]JSC__JSGlobalObject) [*c]JSC__JSObject; pub extern fn JSC__JSGlobalObject__deleteModuleRegistryEntry(arg0: [*c]JSC__JSGlobalObject, arg1: [*c]ZigString) void; @@ -297,7 +301,7 @@ pub extern fn WTF__String__isEmpty(arg0: [*c]WTF__String) bool; pub extern fn WTF__String__isExternal(arg0: [*c]WTF__String) bool; pub extern fn WTF__String__isStatic(arg0: [*c]WTF__String) bool; pub extern fn WTF__String__length(arg0: [*c]WTF__String) usize; -pub extern fn JSC__JSValue___then(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: ?*anyopaque, ArgFn3: ?fn ([*c]JSC__JSGlobalObject, ?*anyopaque, JSC__JSValue, usize) callconv(.C) void, ArgFn4: ?fn ([*c]JSC__JSGlobalObject, ?*anyopaque, JSC__JSValue, usize) callconv(.C) void) void; +pub extern fn JSC__JSValue___then(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: ?*anyopaque, ArgFn3: ?fn ([*c]JSC__JSGlobalObject, ?*anyopaque, [*c]*anyopaque, usize) callconv(.C) void, ArgFn4: ?fn ([*c]JSC__JSGlobalObject, ?*anyopaque, [*c]*anyopaque, usize) callconv(.C) void) void; pub extern fn JSC__JSValue__asArrayBuffer_(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: [*c]Bun__ArrayBuffer) bool; pub extern fn JSC__JSValue__asCell(JSValue0: JSC__JSValue) [*c]JSC__JSCell; pub extern fn JSC__JSValue__asInternalPromise(JSValue0: JSC__JSValue) [*c]JSC__JSInternalPromise; diff --git a/src/javascript/jsc/bindings/webcore/JSReadableStream.cpp b/src/javascript/jsc/bindings/webcore/JSReadableStream.cpp index b9316d130e..e404322cba 100644 --- a/src/javascript/jsc/bindings/webcore/JSReadableStream.cpp +++ b/src/javascript/jsc/bindings/webcore/JSReadableStream.cpp @@ -123,7 +123,7 @@ void JSReadableStreamPrototype::finishCreation(VM& vm) { Base::finishCreation(vm); auto clientData = WebCore::clientData(vm); - this->putDirect(vm, clientData->builtinNames().bunNativeTagPrivateName(), jsNumber(0), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0); + this->putDirect(vm, clientData->builtinNames().bunNativePtrPrivateName(), jsNumber(0), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0); reifyStaticProperties(vm, JSReadableStream::info(), JSReadableStreamPrototypeTableValues, *this); JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); } diff --git a/src/javascript/jsc/bindings/webcore/JSReadableStreamSource.cpp b/src/javascript/jsc/bindings/webcore/JSReadableStreamSource.cpp index e9a39758e5..c27e0918b4 100644 --- a/src/javascript/jsc/bindings/webcore/JSReadableStreamSource.cpp +++ b/src/javascript/jsc/bindings/webcore/JSReadableStreamSource.cpp @@ -105,7 +105,7 @@ void JSReadableStreamSourcePrototype::finishCreation(VM& vm) Base::finishCreation(vm); // -- BUN ADDITION -- auto clientData = WebCore::clientData(vm); - this->putDirect(vm, clientData->builtinNames().bunNativeTagPrivateName(), JSC::jsUndefined(), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0); + this->putDirect(vm, clientData->builtinNames().bunNativePtrPrivateName(), JSC::jsUndefined(), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | 0); // -- BUN ADDITION -- reifyStaticProperties(vm, JSReadableStreamSource::info(), JSReadableStreamSourcePrototypeTableValues, *this); diff --git a/src/javascript/jsc/bindings/webcore/ReadableStream.cpp b/src/javascript/jsc/bindings/webcore/ReadableStream.cpp index 976d3e8009..d1c7e181e7 100644 --- a/src/javascript/jsc/bindings/webcore/ReadableStream.cpp +++ b/src/javascript/jsc/bindings/webcore/ReadableStream.cpp @@ -77,15 +77,15 @@ ExceptionOr> ReadableStream::create(JSC::JSGlobalObject& lex return create(*JSC::jsCast(&lexicalGlobalObject), *jsCast(objectOrException.releaseReturnValue())); } -ExceptionOr> ReadableStream::create(JSC::JSGlobalObject& lexicalGlobalObject, RefPtr&& source, JSC::JSValue nativeTag) +ExceptionOr> ReadableStream::create(JSC::JSGlobalObject& lexicalGlobalObject, RefPtr&& source, JSC::JSValue nativePtr) { auto& builtinNames = WebCore::builtinNames(lexicalGlobalObject.vm()); RELEASE_ASSERT(source != nullptr); - auto objectOrException = invokeConstructor(lexicalGlobalObject, builtinNames.ReadableStreamPrivateName(), [&source, nativeTag](auto& args, auto& lexicalGlobalObject, auto& globalObject) { + auto objectOrException = invokeConstructor(lexicalGlobalObject, builtinNames.ReadableStreamPrivateName(), [&source, nativePtr](auto& args, auto& lexicalGlobalObject, auto& globalObject) { auto sourceStream = toJSNewlyCreated(&lexicalGlobalObject, &globalObject, source.releaseNonNull()); - auto tag = WebCore::clientData(lexicalGlobalObject.vm())->builtinNames().bunNativeTagPrivateName(); - sourceStream.getObject()->putDirect(lexicalGlobalObject.vm(), tag, nativeTag, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::DontEnum); + auto tag = WebCore::clientData(lexicalGlobalObject.vm())->builtinNames().bunNativePtrPrivateName(); + sourceStream.getObject()->putDirect(lexicalGlobalObject.vm(), tag, nativePtr, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::DontEnum); args.append(sourceStream); }); diff --git a/src/javascript/jsc/bindings/webcore/ReadableStream.h b/src/javascript/jsc/bindings/webcore/ReadableStream.h index f332eb5151..de0bbc9fb9 100644 --- a/src/javascript/jsc/bindings/webcore/ReadableStream.h +++ b/src/javascript/jsc/bindings/webcore/ReadableStream.h @@ -41,7 +41,7 @@ public: static Ref create(JSDOMGlobalObject& globalObject, JSReadableStream& readableStream) { return adoptRef(*new ReadableStream(globalObject, readableStream)); } static ExceptionOr> create(JSC::JSGlobalObject&, RefPtr&&); - static ExceptionOr> create(JSC::JSGlobalObject& lexicalGlobalObject, RefPtr&& source, JSC::JSValue nativeTag); + static ExceptionOr> create(JSC::JSGlobalObject& lexicalGlobalObject, RefPtr&& source, JSC::JSValue nativePtr); WEBCORE_EXPORT static bool isDisturbed(JSC::JSGlobalObject&, JSC::JSValue); diff --git a/src/javascript/jsc/event_loop.zig b/src/javascript/jsc/event_loop.zig index 4e3e598ac8..c0a6eaf7be 100644 --- a/src/javascript/jsc/event_loop.zig +++ b/src/javascript/jsc/event_loop.zig @@ -260,11 +260,12 @@ pub const AnyTask = struct { } }; const ThreadSafeFunction = JSC.napi.ThreadSafeFunction; - +const MicrotaskForDefaultGlobalObject = JSC.MicrotaskForDefaultGlobalObject; // const PromiseTask = JSInternalPromise.Completion.PromiseTask; pub const Task = TaggedPointerUnion(.{ FetchTasklet, Microtask, + MicrotaskForDefaultGlobalObject, AsyncTransformTask, BunTimerTimeoutTask, ReadFileTask, @@ -299,6 +300,11 @@ pub const EventLoop = struct { micro.run(global); finished += 1; }, + .MicrotaskForDefaultGlobalObject => { + var micro: *MicrotaskForDefaultGlobalObject = task.as(MicrotaskForDefaultGlobalObject); + micro.run(global); + finished += 1; + }, .FetchTasklet => { var fetch_task: *Fetch.FetchTasklet = task.get(Fetch.FetchTasklet).?; fetch_task.onDone(); diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig index e65b05b777..dd0ea7f748 100644 --- a/src/javascript/jsc/javascript.zig +++ b/src/javascript/jsc/javascript.zig @@ -249,9 +249,14 @@ pub export fn Bun__getDefaultGlobal() *JSGlobalObject { return JSC.VirtualMachine.vm.global; } +pub export fn Bun__getVM() *JSC.VirtualMachine { + return JSC.VirtualMachine.vm; +} + comptime { if (!JSC.is_bindgen) { _ = Bun__getDefaultGlobal; + _ = Bun__getVM; } } @@ -1101,12 +1106,17 @@ pub const VirtualMachine = struct { ret.path = result_path.text; } pub fn queueMicrotaskToEventLoop( - _: *JSGlobalObject, + globalObject: *JSGlobalObject, microtask: *Microtask, ) void { std.debug.assert(VirtualMachine.vm_loaded); - vm.enqueueTask(Task.init(microtask)); + var vm_ = globalObject.bunVM(); + if (vm_.global == globalObject) { + vm_.enqueueTask(Task.init(@ptrCast(*JSC.MicrotaskForDefaultGlobalObject, microtask))); + } else { + vm_.enqueueTask(Task.init(microtask)); + } } pub fn resolveForAPI(res: *ErrorableZigString, global: *JSGlobalObject, specifier: ZigString, source: ZigString) void { diff --git a/src/javascript/jsc/node/node_fs_binding.zig b/src/javascript/jsc/node/node_fs_binding.zig index 6e89ee3751..9e689b929b 100644 --- a/src/javascript/jsc/node/node_fs_binding.zig +++ b/src/javascript/jsc/node/node_fs_binding.zig @@ -41,7 +41,7 @@ fn callSync(comptime FunctionEnum: NodeFSFunctionEnum) NodeFSFunction { arguments: []const JSC.C.JSValueRef, exception: JSC.C.ExceptionRef, ) JSC.C.JSValueRef { - var slice = ArgumentsSlice.init(@ptrCast([*]const JSC.JSValue, arguments.ptr)[0..arguments.len]); + var slice = ArgumentsSlice.init(ctx.bunVM(), @ptrCast([*]const JSC.JSValue, arguments.ptr)[0..arguments.len]); defer slice.deinit(); const args = if (comptime Arguments != void) diff --git a/src/javascript/jsc/node/types.zig b/src/javascript/jsc/node/types.zig index 8d26c6dbf2..3906da3920 100644 --- a/src/javascript/jsc/node/types.zig +++ b/src/javascript/jsc/node/types.zig @@ -456,6 +456,7 @@ pub const Valid = struct { pub const ArgumentsSlice = struct { remaining: []const JSC.JSValue, + vm: *JSC.VirtualMachine, arena: std.heap.ArenaAllocator = std.heap.ArenaAllocator.init(bun.default_allocator), all: []const JSC.JSValue, threw: bool = false, @@ -463,7 +464,7 @@ pub const ArgumentsSlice = struct { pub fn unprotect(this: *ArgumentsSlice) void { var iter = this.protected.iterator(.{}); - var ctx = JSC.VirtualMachine.vm.global.ref(); + var ctx = this.vm.global.ref(); while (iter.next()) |i| { JSC.C.JSValueUnprotect(ctx, this.all[i].asObjectRef()); } @@ -479,24 +480,22 @@ pub const ArgumentsSlice = struct { if (this.remaining.len == 0) return; const index = this.all.len - this.remaining.len; this.protected.set(index); - JSC.C.JSValueProtect(JSC.VirtualMachine.vm.global.ref(), this.all[index].asObjectRef()); + JSC.C.JSValueProtect(this.vm.global.ref(), this.all[index].asObjectRef()); this.eat(); } pub fn protectEatNext(this: *ArgumentsSlice) ?JSC.JSValue { if (this.remaining.len == 0) return null; - const index = this.all.len - this.remaining.len; - this.protected.set(index); - JSC.C.JSValueProtect(JSC.VirtualMachine.vm.global.ref(), this.all[index].asObjectRef()); return this.nextEat(); } - pub fn from(arguments: []const JSC.JSValueRef) ArgumentsSlice { - return init(@ptrCast([*]const JSC.JSValue, arguments.ptr)[0..arguments.len]); + pub fn from(vm: *JSC.VirtualMachine, arguments: []const JSC.JSValueRef) ArgumentsSlice { + return init(vm, @ptrCast([*]const JSC.JSValue, arguments.ptr)[0..arguments.len]); } - pub fn init(arguments: []const JSC.JSValue) ArgumentsSlice { + pub fn init(vm: *JSC.VirtualMachine, arguments: []const JSC.JSValue) ArgumentsSlice { return ArgumentsSlice{ .remaining = arguments, + .vm = vm, .all = arguments, }; } @@ -2534,7 +2533,8 @@ pub const Path = struct { pub const Process = struct { pub fn getArgv(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue { - if (JSC.VirtualMachine.vm.argv.len == 0) + var vm = globalObject.bunVM(); + if (vm.argv.len == 0) return JSC.JSValue.createStringArray(globalObject, null, 0, false); // Allocate up to 32 strings in stack @@ -2546,12 +2546,12 @@ pub const Process = struct { // If it was launched with bun run or bun test, skip it const skip: usize = @as(usize, @boolToInt( - JSC.VirtualMachine.vm.argv.len > 1 and (strings.eqlComptime(JSC.VirtualMachine.vm.argv[0], "run") or strings.eqlComptime(JSC.VirtualMachine.vm.argv[0], "wiptest")), + vm.argv.len > 1 and (strings.eqlComptime(vm.argv[0], "run") or strings.eqlComptime(vm.argv[0], "wiptest")), )); var args = allocator.alloc( JSC.ZigString, - JSC.VirtualMachine.vm.argv.len + 1, + vm.argv.len + 1, ) catch unreachable; var args_list = std.ArrayListUnmanaged(JSC.ZigString){ .items = args, .capacity = args.len }; args_list.items.len = 0; @@ -2567,8 +2567,8 @@ pub const Process = struct { } } - if (JSC.VirtualMachine.vm.argv.len > skip) { - for (JSC.VirtualMachine.vm.argv[skip..]) |arg| { + if (vm.argv.len > skip) { + for (vm.argv[skip..]) |arg| { var str = JSC.ZigString.init(arg); str.setOutputEncoding(); args_list.appendAssumeCapacity(str); diff --git a/src/javascript/jsc/webcore/encoding.zig b/src/javascript/jsc/webcore/encoding.zig index 6d47596601..b168e97ffa 100644 --- a/src/javascript/jsc/webcore/encoding.zig +++ b/src/javascript/jsc/webcore/encoding.zig @@ -742,51 +742,51 @@ pub const Encoder = struct { export fn Bun__encoding__constructFromLatin1AsHex(globalObject: *JSGlobalObject, input: [*]const u8, len: usize) JSValue { var slice = constructFromU8(input, len, .hex); - return JSC.JSValue.createBuffer(globalObject, slice, VirtualMachine.vm.allocator); + return JSC.JSValue.createBuffer(globalObject, slice, globalObject.bunVM().allocator); } export fn Bun__encoding__constructFromLatin1AsASCII(globalObject: *JSGlobalObject, input: [*]const u8, len: usize) JSValue { var slice = constructFromU8(input, len, .ascii); - return JSC.JSValue.createBuffer(globalObject, slice, VirtualMachine.vm.allocator); + return JSC.JSValue.createBuffer(globalObject, slice, globalObject.bunVM().allocator); } export fn Bun__encoding__constructFromLatin1AsURLSafeBase64(globalObject: *JSGlobalObject, input: [*]const u8, len: usize) JSValue { var slice = constructFromU8(input, len, .base64url); - return JSC.JSValue.createBuffer(globalObject, slice, VirtualMachine.vm.allocator); + return JSC.JSValue.createBuffer(globalObject, slice, globalObject.bunVM().allocator); } export fn Bun__encoding__constructFromLatin1AsUTF16(globalObject: *JSGlobalObject, input: [*]const u8, len: usize) JSValue { var slice = constructFromU8(input, len, .utf16le); - return JSC.JSValue.createBuffer(globalObject, slice, VirtualMachine.vm.allocator); + return JSC.JSValue.createBuffer(globalObject, slice, globalObject.bunVM().allocator); } export fn Bun__encoding__constructFromLatin1AsUTF8(globalObject: *JSGlobalObject, input: [*]const u8, len: usize) JSValue { var slice = constructFromU8(input, len, JSC.Node.Encoding.utf8); - return JSC.JSValue.createBuffer(globalObject, slice, VirtualMachine.vm.allocator); + return JSC.JSValue.createBuffer(globalObject, slice, globalObject.bunVM().allocator); } export fn Bun__encoding__constructFromLatin1AsBase64(globalObject: *JSGlobalObject, input: [*]const u8, len: usize) JSValue { var slice = constructFromU8(input, len, .base64); - return JSC.JSValue.createBuffer(globalObject, slice, VirtualMachine.vm.allocator); + return JSC.JSValue.createBuffer(globalObject, slice, globalObject.bunVM().allocator); } export fn Bun__encoding__constructFromUTF16AsBase64(globalObject: *JSGlobalObject, input: [*]const u16, len: usize) JSValue { var slice = constructFromU16(input, len, .base64); - return JSC.JSValue.createBuffer(globalObject, slice, VirtualMachine.vm.allocator); + return JSC.JSValue.createBuffer(globalObject, slice, globalObject.bunVM().allocator); } export fn Bun__encoding__constructFromUTF16AsHex(globalObject: *JSGlobalObject, input: [*]const u16, len: usize) JSValue { var slice = constructFromU16(input, len, .hex); - return JSC.JSValue.createBuffer(globalObject, slice, VirtualMachine.vm.allocator); + return JSC.JSValue.createBuffer(globalObject, slice, globalObject.bunVM().allocator); } export fn Bun__encoding__constructFromUTF16AsURLSafeBase64(globalObject: *JSGlobalObject, input: [*]const u16, len: usize) JSValue { var slice = constructFromU16(input, len, .base64url); - return JSC.JSValue.createBuffer(globalObject, slice, VirtualMachine.vm.allocator); + return JSC.JSValue.createBuffer(globalObject, slice, globalObject.bunVM().allocator); } export fn Bun__encoding__constructFromUTF16AsUTF16(globalObject: *JSGlobalObject, input: [*]const u16, len: usize) JSValue { var slice = constructFromU16(input, len, JSC.Node.Encoding.utf16le); - return JSC.JSValue.createBuffer(globalObject, slice, VirtualMachine.vm.allocator); + return JSC.JSValue.createBuffer(globalObject, slice, globalObject.bunVM().allocator); } export fn Bun__encoding__constructFromUTF16AsUTF8(globalObject: *JSGlobalObject, input: [*]const u16, len: usize) JSValue { var slice = constructFromU16(input, len, .utf8); - return JSC.JSValue.createBuffer(globalObject, slice, VirtualMachine.vm.allocator); + return JSC.JSValue.createBuffer(globalObject, slice, globalObject.bunVM().allocator); } export fn Bun__encoding__constructFromUTF16AsASCII(globalObject: *JSGlobalObject, input: [*]const u16, len: usize) JSValue { var slice = constructFromU16(input, len, .utf8); - return JSC.JSValue.createBuffer(globalObject, slice, VirtualMachine.vm.allocator); + return JSC.JSValue.createBuffer(globalObject, slice, globalObject.bunVM().allocator); } export fn Bun__encoding__toStringUTF16(input: [*]const u8, len: usize, globalObject: *JSC.JSGlobalObject) JSValue { diff --git a/src/javascript/jsc/webcore/response.zig b/src/javascript/jsc/webcore/response.zig index 5f30d5c681..8dcc807bd3 100644 --- a/src/javascript/jsc/webcore/response.zig +++ b/src/javascript/jsc/webcore/response.zig @@ -319,7 +319,8 @@ pub const Response = struct { pub fn makeMaybePooled(ctx: js.JSContextRef, ptr: *Response) JSC.C.JSObjectRef { if (comptime JSC.is_bindgen) unreachable; - if (JSC.VirtualMachine.vm.response_objects_pool) |pool| { + var vm = ctx.bunVM(); + if (vm.response_objects_pool) |pool| { if (pool.get(ptr)) |object| { JSC.C.JSValueUnprotect(ctx, object); return object; @@ -419,7 +420,7 @@ pub const Response = struct { _: js.ExceptionRef, ) js.JSObjectRef { // https://github.com/remix-run/remix/blob/db2c31f64affb2095e4286b91306b96435967969/packages/remix-server-runtime/responses.ts#L4 - var args = JSC.Node.ArgumentsSlice.from(arguments); + var args = JSC.Node.ArgumentsSlice.from(ctx.bunVM(), arguments); // var response = getAllocator(ctx).create(Response) catch unreachable; var response = Response{ @@ -482,7 +483,7 @@ pub const Response = struct { _: js.ExceptionRef, ) js.JSObjectRef { // https://github.com/remix-run/remix/blob/db2c31f64affb2095e4286b91306b96435967969/packages/remix-server-runtime/responses.ts#L4 - var args = JSC.Node.ArgumentsSlice.from(arguments); + var args = JSC.Node.ArgumentsSlice.from(ctx.bunVM(), arguments); // var response = getAllocator(ctx).create(Response) catch unreachable; var response = Response{ @@ -894,7 +895,7 @@ pub const Fetch = struct { var headers: ?Headers = null; var body: MutableString = MutableString.initEmpty(bun.default_allocator); var method = Method.GET; - var args = JSC.Node.ArgumentsSlice.from(arguments); + var args = JSC.Node.ArgumentsSlice.from(ctx.bunVM(), arguments); var url: ZigURL = undefined; var first_arg = args.nextEat().?; var blob_store: ?*Blob.Store = null; @@ -1025,7 +1026,7 @@ pub const ReadableStream = struct { File = 2, }; - extern fn ZigGlobalObject__createNativeReadableStream(*JSGlobalObject, nativeTag: JSValue, nativeID: JSValue) JSValue; + extern fn ZigGlobalObject__createNativeReadableStream(*JSGlobalObject, nativePtr: JSValue, nativeType: JSValue) JSValue; pub fn fromNative(globalThis: *JSGlobalObject, id: Tag, ptr: *anyopaque) JSC.JSValue { return ZigGlobalObject__createNativeReadableStream(globalThis, JSValue.fromPtr(ptr), JSValue.jsNumber(@enumToInt(id))); @@ -1093,6 +1094,22 @@ pub const ReadableStream = struct { return @intCast(JSC.Node.FileDescriptor, out); } }; + + // pub fn NativeReader( + // comptime Context: type, + // comptime onEnqueue: anytype, + // comptime onClose: anytype, + // comptime onAsJS: anytype, + // ) type { + // return struct { + // pub const tag = Context.tag; + + // pub fn enqueue(globalThis: *JSAGlobalObject, callframe: *const JSC.CallFrame) callconv(.C) JSC.JSValue { + // var this = callframe.argument(0).asPtr(*Context); + // return onEnqueue(this, globalThis, callframe.argument(1)); + // } + // }; + // } }; // https://developer.mozilla.org/en-US/docs/Web/API/Headers @@ -1120,7 +1137,7 @@ pub const Headers = struct { }; headers.entries.ensureTotalCapacity(allocator, header_count) catch unreachable; headers.entries.len = header_count; - headers.buf.ensureTotalCapacity(allocator, buf_len) catch unreachable; + headers.buf.ensureTotalCapacityPrecise(allocator, buf_len) catch unreachable; headers.buf.items.len = buf_len; var sliced = headers.entries.slice(); var names = sliced.items(.name); @@ -1353,7 +1370,7 @@ pub const Blob = struct { arguments: []const js.JSValueRef, exception: js.ExceptionRef, ) js.JSObjectRef { - var args = JSC.Node.ArgumentsSlice.from(arguments); + var args = JSC.Node.ArgumentsSlice.from(ctx.bunVM(), arguments); // accept a path or a blob var path_or_blob = PathOrBlob.fromJS(ctx, &args, exception) orelse { exception.* = JSC.toInvalidArguments("Bun.write expects a path, file descriptor or a blob", .{}, ctx).asObjectRef(); @@ -1476,7 +1493,7 @@ pub const Blob = struct { arguments: []const js.JSValueRef, exception: js.ExceptionRef, ) js.JSObjectRef { - var args = JSC.Node.ArgumentsSlice.from(arguments); + var args = JSC.Node.ArgumentsSlice.from(ctx.bunVM(), arguments); defer args.deinit(); var path = JSC.Node.PathOrFileDescriptor.fromJS(ctx, &args, exception) orelse { @@ -1494,7 +1511,8 @@ pub const Blob = struct { pub fn findOrCreateFileFromPath(path_: JSC.Node.PathOrFileDescriptor, globalThis: *JSGlobalObject) Blob { var path = path_; - if (VirtualMachine.vm.getFileBlob(path)) |blob| { + var vm = globalThis.bunVM(); + if (vm.getFileBlob(path)) |blob| { blob.ref(); return Blob.initWithStore(blob, globalThis); } @@ -1510,15 +1528,15 @@ pub const Blob = struct { .fd => { switch (path.fd) { std.os.STDIN_FILENO => return Blob.initWithStore( - VirtualMachine.vm.rareData().stdin(), + vm.rareData().stdin(), globalThis, ), std.os.STDERR_FILENO => return Blob.initWithStore( - VirtualMachine.vm.rareData().stderr(), + vm.rareData().stderr(), globalThis, ), std.os.STDOUT_FILENO => return Blob.initWithStore( - VirtualMachine.vm.rareData().stdout(), + vm.rareData().stdout(), globalThis, ), else => {}, @@ -1527,7 +1545,7 @@ pub const Blob = struct { } const result = Blob.initWithStore(Blob.Store.initFile(path, null, bun.default_allocator) catch unreachable, globalThis); - VirtualMachine.vm.putFileBlob(path, result.store.?) catch unreachable; + vm.putFileBlob(path, result.store.?) catch unreachable; return result; } @@ -3062,7 +3080,7 @@ pub const Blob = struct { // If the optional end parameter is not used as a parameter when making this call, let relativeEnd be size. var relativeEnd: i64 = @intCast(i64, this.size); - var args_iter = JSC.Node.ArgumentsSlice.from(args); + var args_iter = JSC.Node.ArgumentsSlice.from(ctx.bunVM(), args); if (args_iter.nextEat()) |start_| { const start = start_.toInt64(); if (start < 0) { @@ -3664,7 +3682,7 @@ pub const Blob = struct { }, .ret = undefined, }; - JSC.VirtualMachine.vm.global.vm().deferGC(&ctx, DeferCtx.run); + global.vm().deferGC(&ctx, DeferCtx.run); return ctx.ret; } @@ -4168,6 +4186,12 @@ pub const Body = struct { this.* = .{ .Error = error_instance }; } + pub fn toErrorString(this: *Value, comptime err: string, global: *JSGlobalObject) void { + var error_str = ZigString.init(err); + var error_instance = error_str.toErrorInstance(global); + return this.toErrorInstance(error_instance, global); + } + pub fn toError(this: *Value, err: anyerror, global: *JSGlobalObject) void { var error_str = ZigString.init(std.fmt.allocPrint( bun.default_allocator, diff --git a/src/napi/napi.zig b/src/napi/napi.zig index 103c0a365c..65ef0c45c7 100644 --- a/src/napi/napi.zig +++ b/src/napi/napi.zig @@ -186,7 +186,7 @@ pub export fn napi_create_array_with_length(env: napi_env, length: usize, result return .ok; } - const allocator = JSC.VirtualMachine.vm.allocator; + const allocator = env.bunVM().allocator; var undefined_args = allocator.alloc(JSC.C.JSValueRef, length) catch return .generic_failure; defer allocator.free(undefined_args); for (undefined_args) |_, i| { @@ -748,7 +748,7 @@ pub export fn napi_create_external_arraybuffer(env: napi_env, external_data: ?*a @ptrCast([*]u8, external_data.?)[0..byte_length], env, finalize_cb, - JSC.VirtualMachine.vm.allocator, + env.bunVM().allocator, ) catch { return .generic_failure; }; @@ -945,7 +945,7 @@ pub const napi_async_work = struct { work.* = .{ .global = global, .execute = execute, - .event_loop = JSC.VirtualMachine.vm.eventLoop(), + .event_loop = global.bunVM().eventLoop(), .complete = complete, .ctx = ctx, }; @@ -1054,8 +1054,8 @@ fn napiSpan(ptr: anytype, len: usize) []const u8 { // C++ // pub export fn napi_module_register(mod: *napi_module) void { // const register = mod.nm_register_func orelse return; -// var ref = JSC.C.JSObjectMake(JSC.VirtualMachine.vm.global, null, null); -// register(JSC.VirtualMachine.vm.global, JSC.JSValue.c(ref)); +// var ref = JSC.C.JSObjectMake(env.bunVM().global, null, null); +// register(env.bunVM().global, JSC.JSValue.c(ref)); // } pub export fn napi_fatal_error(location_ptr: ?[*:0]const u8, location_len: usize, message_ptr: ?[*:0]const u8, message_len_: usize) noreturn { var message = napiSpan(message_ptr, message_len_); @@ -1083,7 +1083,7 @@ pub export fn napi_fatal_error(location_ptr: ?[*:0]const u8, location_len: usize // // JSC.C.JSObjectCallAsFunction(env.ref(), func.asObjectRef(), recv.asObjectRef(), argc, @ptrCast([*]const JSC.C.JSValueRef), exception: ExceptionRef) // } pub export fn napi_create_buffer(env: napi_env, length: usize, data: [*]*anyopaque, result: *napi_value) napi_status { - var buf = JSC.ExternalBuffer.create(null, @ptrCast([*]u8, data)[0..length], env, null, JSC.VirtualMachine.vm.allocator) catch { + var buf = JSC.ExternalBuffer.create(null, @ptrCast([*]u8, data)[0..length], env, null, env.bunVM().allocator) catch { return .generic_failure; }; @@ -1091,7 +1091,7 @@ pub export fn napi_create_buffer(env: napi_env, length: usize, data: [*]*anyopaq return .ok; } pub export fn napi_create_external_buffer(env: napi_env, length: usize, data: ?*anyopaque, finalize_cb: napi_finalize, finalize_hint: ?*anyopaque, result: *napi_value) napi_status { - var buf = JSC.ExternalBuffer.create(finalize_hint, @ptrCast([*]u8, data.?)[0..length], env, finalize_cb, JSC.VirtualMachine.vm.allocator) catch { + var buf = JSC.ExternalBuffer.create(finalize_hint, @ptrCast([*]u8, data.?)[0..length], env, finalize_cb, env.bunVM().allocator) catch { return .generic_failure; }; @@ -1099,7 +1099,7 @@ pub export fn napi_create_external_buffer(env: napi_env, length: usize, data: ?* return .ok; } pub export fn napi_create_buffer_copy(env: napi_env, length: usize, data: [*]u8, result_data: ?*?*anyopaque, result: *napi_value) napi_status { - var duped = JSC.VirtualMachine.vm.allocator.alloc(u8, length) catch { + var duped = env.bunVM().allocator.alloc(u8, length) catch { return .generic_failure; }; @memcpy(duped.ptr, data, length); @@ -1107,7 +1107,7 @@ pub export fn napi_create_buffer_copy(env: napi_env, length: usize, data: [*]u8, res.* = duped.ptr; } - result.* = JSC.JSValue.createBuffer(env, duped, JSC.VirtualMachine.vm.allocator); + result.* = JSC.JSValue.createBuffer(env, duped, env.bunVM().allocator); return .ok; } @@ -1161,9 +1161,9 @@ pub export fn napi_get_node_version(_: napi_env, version: **const napi_node_vers version.* = &napi_node_version.global; return .ok; } -pub export fn napi_get_uv_event_loop(_: napi_env, loop: **JSC.EventLoop) napi_status { +pub export fn napi_get_uv_event_loop(env: napi_env, loop: **JSC.EventLoop) napi_status { // lol - loop.* = JSC.VirtualMachine.vm.eventLoop(); + loop.* = env.bunVM().eventLoop(); return .ok; } pub extern fn napi_fatal_exception(env: napi_env, err: napi_value) napi_status; @@ -1174,18 +1174,18 @@ pub export fn napi_add_env_cleanup_hook(env: napi_env, fun: ?fn (?*anyopaque) ca if (fun == null) return .ok; - JSC.VirtualMachine.vm.rareData().pushCleanupHook(env, arg, fun.?); + env.bunVM().rareData().pushCleanupHook(env, arg, fun.?); return .ok; } pub export fn napi_remove_env_cleanup_hook(env: napi_env, fun: ?fn (?*anyopaque) callconv(.C) void, arg: ?*anyopaque) napi_status { - if (JSC.VirtualMachine.vm.rare_data == null or fun == null) + if (env.bunVM().rare_data == null or fun == null) return .ok; - var rare_data = JSC.VirtualMachine.vm.rare_data.?; + var rare_data = env.bunVM().rare_data.?; var hook = rare_data.cleanup_hook orelse return .ok; const cmp = JSC.RareData.CleanupHook.from(env, arg, fun.?); if (hook.eql(cmp)) { - JSC.VirtualMachine.vm.allocator.destroy(hook); + env.bunVM().allocator.destroy(hook); rare_data.cleanup_hook = null; rare_data.tail_cleanup_hook = null; } @@ -1196,7 +1196,7 @@ pub export fn napi_remove_env_cleanup_hook(env: napi_env, fun: ?fn (?*anyopaque) } else { hook.next = null; } - JSC.VirtualMachine.vm.allocator.destroy(current); + env.bunVM().allocator.destroy(current); return .ok; } hook = current; @@ -1398,7 +1398,7 @@ pub export fn napi_create_threadsafe_function( JSC.C.JSValueProtect(env.ref(), func.asObjectRef()); var function = bun.default_allocator.create(ThreadSafeFunction) catch return .generic_failure; function.* = .{ - .event_loop = JSC.VirtualMachine.vm.eventLoop(), + .event_loop = env.bunVM().eventLoop(), .javascript_function = func, .call_js = call_js_cb, .ctx = context, diff --git a/src/string_immutable.zig b/src/string_immutable.zig index 97bbbf4591..6846e39c6b 100644 --- a/src/string_immutable.zig +++ b/src/string_immutable.zig @@ -1036,6 +1036,56 @@ pub fn allocateLatin1IntoUTF8(allocator: std.mem.Allocator, comptime Type: type, return list.toOwnedSlice(); } +pub fn allocateLatin1IntoUTF8ForArrayBuffer(allocator: std.mem.Allocator, globalThis: *JSC.JSGlobalObject, comptime Type: type, latin1_: Type) !JSC.JSValue { + if (comptime bun.FeatureFlags.latin1_is_now_ascii) { + var out = try allocator.alloc(u8, latin1_.len); + @memcpy(out.ptr, latin1_.ptr, latin1_.len); + return out; + } + + var latin1 = latin1_; + + if (firstNonASCII(latin1)) |start_i| { + var list = try std.ArrayList(u8).initCapacity(allocator, latin1_.len + 2); + list.items.len = start_i; + @memcpy(list.items.ptr, latin1.ptr, start_i); + { + var buf = list.items.ptr[list.items.len .. list.items.len + 2][0..2]; + list.items.len += 2; + buf[0..2].* = latin1ToCodepointBytesAssumeNotASCII(latin1[0]); + latin1 = latin1[1..]; + } + + while (latin1.len > 0) { + const read = @as(usize, firstNonASCII(latin1) orelse @intCast(u32, latin1.len)); + try list.ensureTotalCapacityPrecise( + list.items.len + read + if (read != latin1.len) @as(usize, 2) else @as(usize, 0), + ); + const before = list.items.len; + list.items.len += read; + @memcpy(list.items[before..].ptr, latin1.ptr, read); + latin1 = latin1[read..]; + + if (latin1.len > 0) { + try list.ensureUnusedCapacity(2); + var buf = list.items.ptr[list.items.len .. list.items.len + 2][0..2]; + list.items.len += 2; + buf[0..2].* = latin1ToCodepointBytesAssumeNotASCII(latin1[0]); + latin1 = latin1[1..]; + } + } + + return JSC.ArrayBuffer.fromBytes(list.toOwnedSlice(), .Uint8Array).toJS(globalThis, null); + } + + { + const array_buffer = JSC.JSValue.createUninitializedUint8Array(globalThis, latin1.len); + var bytes = array_buffer.asArrayBuffer(globalThis).?.slice(); + @memcpy(bytes.ptr, latin1.ptr, latin1.len); + return array_buffer; + } +} + pub const UTF16Replacement = struct { code_point: u32 = unicode_replacement, len: u3 = 0, @@ -1144,6 +1194,7 @@ pub fn copyLatin1IntoUTF8(buf_: []u8, comptime Type: type, latin1_: Type) Encode @memcpy(buf_.ptr, latin1_.ptr, to_copy); return .{ .written = to_copy, .read = to_copy }; } + var buf = buf_; var latin1 = latin1_; while (buf.len > 0 and latin1.len > 0) { @@ -1164,11 +1215,11 @@ pub fn copyLatin1IntoUTF8(buf_: []u8, comptime Type: type, latin1_: Type) Encode while (read < latin1.len and latin1[read] < 0x80) : (read += 1) {} - const written = @minimum(read, buf.len); - if (written == 0) break; - @memcpy(buf.ptr, latin1.ptr, written); - latin1 = latin1[written..]; - buf = buf[written..]; + const to_copy = @minimum(read, buf.len); + @memcpy(buf.ptr, latin1.ptr, to_copy); + latin1 = latin1[to_copy..]; + buf = buf[to_copy..]; + if (latin1.len > 0 and buf.len >= 2) { buf[0..2].* = latin1ToCodepointBytesAssumeNotASCII(latin1[0]); latin1 = latin1[1..]; @@ -1177,8 +1228,8 @@ pub fn copyLatin1IntoUTF8(buf_: []u8, comptime Type: type, latin1_: Type) Encode } return .{ - .read = @truncate(u32, buf_.len - buf.len), - .written = @truncate(u32, latin1_.len - latin1.len), + .written = @truncate(u32, buf_.len - buf.len), + .read = @truncate(u32, latin1_.len - latin1.len), }; }