diff options
author | 2023-01-07 07:09:48 -0800 | |
---|---|---|
committer | 2023-01-07 07:09:48 -0800 | |
commit | 87983464d8a331c1ddd09eced9920269a759f0a9 (patch) | |
tree | b08a5aef5c2d18f25a5ee46c88bec84d5b8ee907 /src/bun.js | |
parent | d5565ab2cdd7099a5852ba5ba6d180ef291af084 (diff) | |
download | bun-87983464d8a331c1ddd09eced9920269a759f0a9.tar.gz bun-87983464d8a331c1ddd09eced9920269a759f0a9.tar.zst bun-87983464d8a331c1ddd09eced9920269a759f0a9.zip |
Implement DNS module (#1691)
* Boilerplate for DNS stuff
* Add c-ares
* lookup
* make
* Implement dns.lookup
* Create c-ares
* wip
* normalize
* repro
* Revert "repro"
This reverts commit 8b93e0c295b335b8882a9601da47720348549beb.
* Implement macOS `getaddrinfo_async_start`
* embiggen
* Update string_immutable.zig
* Update Makefile
* alright
* Update .gitignore
* Add types
* more ccache
* Update Dockerfile
* Update Dockerfile
* Update Dockerfile
* Update bun.d.ts
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Diffstat (limited to 'src/bun.js')
-rw-r--r-- | src/bun.js/api/bun/dns_resolver.zig | 1371 | ||||
-rw-r--r-- | src/bun.js/base.zig | 78 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGeneratedCode.cpp | 32 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGlobalObject.cpp | 10 | ||||
-rw-r--r-- | src/bun.js/bindings/ares_build.h | 42 | ||||
-rw-r--r-- | src/bun.js/bindings/bindings.cpp | 56 | ||||
-rw-r--r-- | src/bun.js/bindings/bindings.zig | 66 | ||||
-rw-r--r-- | src/bun.js/bindings/glibc-versions-hack.cpp | 4 | ||||
-rw-r--r-- | src/bun.js/bindings/headers-cpp.h | 2 | ||||
-rw-r--r-- | src/bun.js/bindings/headers.h | 5 | ||||
-rw-r--r-- | src/bun.js/bindings/headers.zig | 3 | ||||
-rw-r--r-- | src/bun.js/event_loop.zig | 25 | ||||
-rw-r--r-- | src/bun.js/fs_promises.exports.js | 2 | ||||
-rw-r--r-- | src/bun.js/node/syscall.zig | 1 | ||||
-rw-r--r-- | src/bun.js/rare_data.zig | 10 |
15 files changed, 1664 insertions, 43 deletions
diff --git a/src/bun.js/api/bun/dns_resolver.zig b/src/bun.js/api/bun/dns_resolver.zig new file mode 100644 index 000000000..285f09f92 --- /dev/null +++ b/src/bun.js/api/bun/dns_resolver.zig @@ -0,0 +1,1371 @@ +const Bun = @This(); +const default_allocator = @import("bun").default_allocator; +const bun = @import("bun"); +const Environment = bun.Environment; +const NetworkThread = @import("bun").HTTP.NetworkThread; +const Global = bun.Global; +const strings = bun.strings; +const string = bun.string; +const Output = @import("bun").Output; +const MutableString = @import("bun").MutableString; +const std = @import("std"); +const Allocator = std.mem.Allocator; +const JSC = @import("bun").JSC; +const JSValue = JSC.JSValue; +const JSGlobalObject = JSC.JSGlobalObject; +const c_ares = bun.c_ares; + +const GetAddrInfoAsyncCallback = fn (i32, ?*std.c.addrinfo, ?*anyopaque) callconv(.C) void; + +const LibInfo = struct { + // static int32_t (*getaddrinfo_async_start)(mach_port_t*, + // const char*, + // const char*, + // const struct addrinfo*, + // getaddrinfo_async_callback, + // void*); + // static int32_t (*getaddrinfo_async_handle_reply)(void*); + // static void (*getaddrinfo_async_cancel)(mach_port_t); + // typedef void getaddrinfo_async_callback(int32_t, struct addrinfo*, void*) + const GetaddrinfoAsyncStart = fn (*?*anyopaque, noalias node: ?[*:0]const u8, noalias service: ?[*:0]const u8, noalias hints: ?*const std.c.addrinfo, callback: *const GetAddrInfoAsyncCallback, noalias context: ?*anyopaque) callconv(.C) i32; + const GetaddrinfoAsyncHandleReply = fn (?**anyopaque) callconv(.C) i32; + const GetaddrinfoAsyncCancel = fn (?**anyopaque) callconv(.C) void; + + var handle: ?*anyopaque = null; + var loaded = false; + pub fn getHandle() ?*anyopaque { + if (loaded) + return handle; + loaded = true; + const RTLD_LAZY = 1; + const RTLD_LOCAL = 4; + + handle = std.c.dlopen("libinfo.dylib", RTLD_LAZY | RTLD_LOCAL); + if (handle == null) + Output.debug("libinfo.dylib not found", .{}); + return handle; + } + + pub const getaddrinfo_async_start = struct { + pub fn get() ?*const GetaddrinfoAsyncStart { + return bun.C.dlsymWithHandle(*const GetaddrinfoAsyncStart, "getaddrinfo_async_start", getHandle); + } + }.get; + + pub const getaddrinfo_async_handle_reply = struct { + pub fn get() ?*const GetaddrinfoAsyncHandleReply { + return bun.C.dlsymWithHandle(*const GetaddrinfoAsyncHandleReply, "getaddrinfo_async_handle_reply", getHandle); + } + }.get; + + pub fn get() ?*const GetaddrinfoAsyncCancel { + return bun.C.dlsymWithHandle(*const GetaddrinfoAsyncCancel, "getaddrinfo_async_cancel", getHandle); + } + + pub fn lookup(this: *DNSResolver, query: GetAddrInfo, globalThis: *JSC.JSGlobalObject) JSC.JSValue { + const getaddrinfo_async_start_ = LibInfo.getaddrinfo_async_start() orelse return LibC.lookup(this, query, globalThis); + + var key = GetAddrInfoRequest.PendingCacheKey.init(query); + var cache = this.getOrPutIntoPendingCache(key, .pending_host_cache_native); + + if (cache == .inflight) { + var dns_lookup = DNSLookup.init(globalThis, globalThis.allocator()) catch unreachable; + + cache.inflight.append(dns_lookup); + + return dns_lookup.promise.value(); + } + + var name_buf: [1024]u8 = undefined; + _ = strings.copy(name_buf[0..], query.name); + + name_buf[query.name.len] = 0; + var name_z = name_buf[0..query.name.len :0]; + + var request = GetAddrInfoRequest.init( + cache, + .{ .libinfo = undefined }, + this, + query, + globalThis, + "pending_host_cache_native", + ) catch unreachable; + const promise_value = request.head.promise.value(); + + const errno = getaddrinfo_async_start_( + &request.backend.libinfo.machport, + name_z.ptr, + null, + null, + GetAddrInfoRequest.getAddrInfoAsyncCallback, + request, + ); + + if (errno != 0) { + request.head.promise.reject(globalThis, globalThis.createErrorInstance("getaddrinfo_async_start error: {s}", .{@tagName(std.c.getErrno(errno))})); + if (request.cache.pending_cache) this.pending_host_cache_native.available.set(request.cache.pos_in_pending); + this.vm.allocator.destroy(request); + + return promise_value; + } + std.debug.assert(request.backend.libinfo.machport != null); + request.backend.libinfo.file_poll = bun.JSC.FilePoll.init(this.vm, std.math.maxInt(i32) - 1, .{}, GetAddrInfoRequest, request); + std.debug.assert( + request.backend.libinfo.file_poll.?.registerWithFd( + this.vm.uws_event_loop.?, + .machport, + true, + @ptrToInt(request.backend.libinfo.machport), + ) == .result, + ); + + return promise_value; + } +}; + +const LibC = struct { + pub fn lookup(this: *DNSResolver, query_init: GetAddrInfo, globalThis: *JSC.JSGlobalObject) JSC.JSValue { + const key = GetAddrInfoRequest.PendingCacheKey.init(query_init); + + var cache = this.getOrPutIntoPendingCache(key, .pending_host_cache_native); + if (cache == .inflight) { + var dns_lookup = DNSLookup.init(globalThis, globalThis.allocator()) catch unreachable; + + cache.inflight.append(dns_lookup); + + return dns_lookup.promise.value(); + } + + var query = query_init.clone(); + + var request = GetAddrInfoRequest.init( + cache, + .{ + .libc = .{ + .query = query, + }, + }, + this, + query, + globalThis, + "pending_host_cache_native", + ) catch unreachable; + const promise_value = request.head.promise.value(); + + var io = GetAddrInfoRequest.Task.createOnJSThread(this.vm.allocator, globalThis, request) catch unreachable; + + io.schedule(); + + return promise_value; + } +}; + +pub fn addressToString( + allocator: std.mem.Allocator, + address: std.net.Address, +) JSC.ZigString { + const str: []const u8 = brk: { + switch (address.any.family) { + std.os.AF.INET => { + var self = address.in; + const bytes = @ptrCast(*const [4]u8, &self.sa.addr); + break :brk std.fmt.allocPrint(allocator, "{}.{}.{}.{}", .{ + bytes[0], + bytes[1], + bytes[2], + bytes[3], + }) catch unreachable; + }, + std.os.AF.INET6 => { + var out = std.fmt.allocPrint(allocator, "{any}", .{address}) catch unreachable; + // TODO: this is a hack, fix it + // This removes [.*]:port + // ^ ^^^^^^ + break :brk out[1 .. out.len - 1 - std.fmt.count("{d}", .{address.in6.getPort()}) - 1]; + }, + std.os.AF.UNIX => { + break :brk std.mem.sliceTo(&address.un.path, 0); + }, + else => break :brk "", + } + }; + + return JSC.ZigString.init(str); +} + +pub fn normalizeDNSName(name: []const u8, backend: *GetAddrInfo.Backend) []const u8 { + if (backend.* == .c_ares) { + // https://github.com/c-ares/c-ares/issues/477 + if (strings.endsWithComptime(name, ".localhost")) { + backend.* = .system; + return "localhost"; + } else if (strings.endsWithComptime(name, ".local")) { + backend.* = .system; + // https://github.com/c-ares/c-ares/pull/463 + } else if (strings.isIPV6Address(name)) { + backend.* = .system; + } + } + + return name; +} + +pub fn addressToJS( + allocator: std.mem.Allocator, + address: std.net.Address, + globalThis: *JSC.JSGlobalObject, +) JSC.JSValue { + return addressToString(allocator, address).toValueGC(globalThis); +} + +fn addrInfoCount(addrinfo: *std.c.addrinfo) u32 { + var count: u32 = 1; + var current: ?*std.c.addrinfo = addrinfo.next; + while (current != null) : (current = current.?.next) { + count += @boolToInt(current.?.addr != null); + } + return count; +} + +pub fn addrInfoToJSArray( + parent_allocator: std.mem.Allocator, + addr_info: *std.c.addrinfo, + globalThis: *JSC.JSGlobalObject, +) JSC.JSValue { + var stack = std.heap.stackFallback(2048, parent_allocator); + var arena = std.heap.ArenaAllocator.init(stack.get()); + const array = JSC.JSValue.createEmptyArray( + globalThis, + addrInfoCount(addr_info), + ); + + { + defer arena.deinit(); + + var allocator = arena.allocator(); + var j: u32 = 0; + var current: ?*std.c.addrinfo = addr_info; + while (current) |this_node| : (current = current.?.next) { + array.putIndex( + globalThis, + j, + bun.JSC.DNS.GetAddrInfo.Result.toJS( + &(bun.JSC.DNS.GetAddrInfo.Result.fromAddrInfo(this_node) orelse continue), + globalThis, + allocator, + ), + ); + j += 1; + } + } + + return array; +} + +pub const GetAddrInfo = struct { + name: []const u8 = "", + port: u16 = 0, + options: Options = Options{}, + + pub fn clone(this: GetAddrInfo) GetAddrInfo { + return GetAddrInfo{ + .name = bun.default_allocator.dupe(u8, this.name) catch unreachable, + .port = this.port, + .options = this.options, + }; + } + + pub fn toCAres(this: GetAddrInfo) bun.c_ares.AddrInfo_hints { + var hints: bun.c_ares.AddrInfo_hints = undefined; + @memset(std.mem.asBytes(&hints), 0, @sizeOf(bun.c_ares.AddrInfo_hints)); + + hints.ai_family = this.options.family.toLibC(); + hints.ai_socktype = this.options.socktype.toLibC(); + hints.ai_protocol = this.options.protocol.toLibC(); + hints.ai_flags = this.options.flags; + + return hints; + } + + pub fn hash(self: GetAddrInfo) u64 { + var hasher = std.hash.Wyhash.init(0); + const bytes = + std.mem.asBytes(&self.port) ++ + std.mem.asBytes(&self.options); + + hasher.update(bytes); + hasher.update(self.name); + + return hasher.final(); + } + + pub const Options = packed struct { + family: Family = .unspecified, + socktype: SocketType = .unspecified, + protocol: Protocol = .unspecified, + backend: Backend = Backend.default, + flags: i32 = 0, + + pub fn toLibC(this: Options) ?std.c.addrinfo { + if (this.family == .unspecified and this.socktype == .unspecified and this.protocol == .unspecified and this.flags == 0) { + return null; + } + + var hints: std.c.addrinfo = undefined; + @memset(std.mem.asBytes(&hints), 0, @sizeOf(std.c.addrinfo)); + + hints.family = this.family.toLibC(); + hints.socktype = this.socktype.toLibC(); + hints.protocol = this.protocol.toLibC(); + hints.flags = this.flags; + return hints; + } + + pub fn fromJS(value: JSC.JSValue, globalObject: *JSC.JSGlobalObject) !Options { + if (value.isEmptyOrUndefinedOrNull()) + return Options{}; + + if (value.isObject()) { + var options = Options{}; + + if (value.get(globalObject, "family")) |family| { + options.family = try Family.fromJS(family, globalObject); + } + + if (value.get(globalObject, "socketType") orelse value.get(globalObject, "socktype")) |socktype| { + options.socktype = try SocketType.fromJS(socktype, globalObject); + } + + if (value.get(globalObject, "protocol")) |protocol| { + options.protocol = try Protocol.fromJS(protocol, globalObject); + } + + if (value.get(globalObject, "backend")) |backend| { + options.backend = try Backend.fromJS(backend, globalObject); + } + + if (value.get(globalObject, "flags")) |flags| { + if (!flags.isNumber()) + return error.InvalidFlags; + + options.flags = flags.coerce(i32, globalObject); + } + + return options; + } + + return error.InvalidOptions; + } + }; + + pub const Family = enum(u2) { + unspecified, + inet, + inet6, + unix, + + pub const map = bun.ComptimeStringMap(Family, .{ + .{ "IPv4", Family.inet }, + .{ "IPv6", Family.inet6 }, + .{ "ipv4", Family.inet }, + .{ "ipv6", Family.inet6 }, + .{ "any", Family.unspecified }, + }); + + pub fn fromJS(value: JSC.JSValue, globalObject: *JSC.JSGlobalObject) !Family { + if (value.isEmptyOrUndefinedOrNull()) + return .unspecified; + + if (value.isNumber()) { + return switch (value.to(i32)) { + 0 => .unspecified, + 4 => .inet, + 6 => .inet6, + else => return error.InvalidFamily, + }; + } + + if (value.isString()) { + const str = value.getZigString(globalObject); + if (str.len == 0) + return .unspecified; + + return map.getWithEql(str, JSC.ZigString.eqlComptime) orelse return error.InvalidFamily; + } + + return error.InvalidFamily; + } + + pub fn toLibC(this: Family) i32 { + return switch (this) { + .unspecified => 0, + .inet => std.os.AF.INET, + .inet6 => std.os.AF.INET6, + .unix => std.os.AF.UNIX, + }; + } + }; + + pub const SocketType = enum(u2) { + unspecified, + stream, + dgram, + + const map = bun.ComptimeStringMap(SocketType, .{ + .{ "stream", SocketType.stream }, + .{ "dgram", SocketType.dgram }, + .{ "tcp", SocketType.stream }, + .{ "udp", SocketType.dgram }, + }); + + pub fn toLibC(this: SocketType) i32 { + switch (this) { + .unspecified => return 0, + .stream => return std.os.SOCK.STREAM, + .dgram => return std.os.SOCK.DGRAM, + } + } + + pub fn fromJS(value: JSC.JSValue, globalObject: *JSC.JSGlobalObject) !SocketType { + if (value.isEmptyOrUndefinedOrNull()) + return .unspecified; + + if (value.isNumber()) { + return switch (value.to(i32)) { + 0 => .unspecified, + 1 => .stream, + 2 => .dgram, + else => return error.InvalidSocketType, + }; + } + + if (value.isString()) { + const str = value.getZigString(globalObject); + if (str.len == 0) + return .unspecified; + + return map.getWithEql(str, JSC.ZigString.eqlComptime) orelse return error.InvalidSocketType; + } + + return error.InvalidSocketType; + } + }; + + pub const Protocol = enum(u2) { + unspecified, + tcp, + udp, + + const map = bun.ComptimeStringMap(Protocol, .{ + .{ "tcp", Protocol.tcp }, + .{ "udp", Protocol.udp }, + }); + + pub fn fromJS(value: JSC.JSValue, globalObject: *JSC.JSGlobalObject) !Protocol { + if (value.isEmptyOrUndefinedOrNull()) + return .unspecified; + + if (value.isNumber()) { + return switch (value.to(i32)) { + 0 => .unspecified, + 6 => .tcp, + 17 => .udp, + else => return error.InvalidProtocol, + }; + } + + if (value.isString()) { + const str = value.getZigString(globalObject); + if (str.len == 0) + return .unspecified; + + return map.getWithEql(str, JSC.ZigString.eqlComptime) orelse return error.InvalidProtocol; + } + + return error.InvalidProtocol; + } + + pub fn toLibC(this: Protocol) i32 { + switch (this) { + .unspecified => return 0, + .tcp => return std.os.IPPROTO.TCP, + .udp => return std.os.IPPROTO.UDP, + } + } + }; + + pub const Backend = enum(u2) { + c_ares, + system, + libc, + + pub const label = bun.ComptimeStringMap(GetAddrInfo.Backend, .{ + .{ "c-ares", .c_ares }, + .{ "c_ares", .c_ares }, + .{ "cares", .c_ares }, + .{ "async", .c_ares }, + .{ "libc", .libc }, + .{ "system", .system }, + .{ "getaddrinfo", .libc }, + }); + + pub const default: GetAddrInfo.Backend = if (Environment.isMac) + GetAddrInfo.Backend.system + else + GetAddrInfo.Backend.c_ares; + + pub fn fromJS(value: JSC.JSValue, globalObject: *JSC.JSGlobalObject) !Backend { + if (value.isEmptyOrUndefinedOrNull()) + return default; + + if (value.isString()) { + const str = value.getZigString(globalObject); + if (str.len == 0) + return default; + + return label.getWithEql(str, JSC.ZigString.eqlComptime) orelse return error.InvalidBackend; + } + + return error.InvalidBackend; + } + }; + + pub const Result = struct { + address: std.net.Address, + ttl: i32 = 0, + + pub const List = std.ArrayList(Result); + + pub const Any = union(enum) { + addrinfo: ?*std.c.addrinfo, + list: List, + + pub fn toJS(this: Any, globalThis: *JSC.JSGlobalObject) ?JSC.JSValue { + return switch (this) { + .addrinfo => |addrinfo| addrInfoToJSArray(globalThis.allocator(), addrinfo orelse return null, globalThis), + .list => |list| brk: { + var stack = std.heap.stackFallback(2048, globalThis.allocator()); + var arena = std.heap.ArenaAllocator.init(stack.get()); + const array = JSC.JSValue.createEmptyArray(globalThis, @truncate(u32, list.items.len)); + var i: u32 = 0; + const items: []const Result = list.items; + for (items) |item| { + array.putIndex(globalThis, i, item.toJS(globalThis, arena.allocator())); + i += 1; + } + break :brk array; + }, + }; + } + + pub fn deinit(this: Any) void { + switch (this) { + .addrinfo => |addrinfo| { + if (addrinfo) |a| { + std.c.freeaddrinfo(a); + } + }, + .list => |list| { + var list_ = list; + list_.deinit(); + }, + } + } + }; + + pub fn toList(allocator: std.mem.Allocator, addrinfo: *std.c.addrinfo) !List { + var list = try List.initCapacity(allocator, addrInfoCount(addrinfo)); + + var addr: ?*std.c.addrinfo = addrinfo; + while (addr) |a| : (addr = a.next) { + list.appendAssumeCapacity(fromAddrInfo(a) orelse continue); + } + + return list; + } + + pub fn fromAddrInfo(addrinfo: *std.c.addrinfo) ?Result { + return Result{ + .address = std.net.Address.initPosix(@alignCast(4, addrinfo.addr orelse return null)), + // no TTL in POSIX getaddrinfo() + .ttl = 0, + }; + } + + pub fn toJS(this: *const Result, globalThis: *JSC.JSGlobalObject, allocator: std.mem.Allocator) JSValue { + const obj = JSC.JSValue.createEmptyObject(globalThis, 3); + obj.put(globalThis, JSC.ZigString.static("address"), addressToJS(allocator, this.address, globalThis)); + obj.put(globalThis, JSC.ZigString.static("family"), switch (this.address.any.family) { + std.os.AF.INET => JSValue.jsNumber(4), + std.os.AF.INET6 => JSValue.jsNumber(6), + else => JSValue.jsNumber(0), + }); + obj.put(globalThis, JSC.ZigString.static("ttl"), JSValue.jsNumber(this.ttl)); + return obj; + } + }; +}; + +pub const GetAddrInfoRequest = struct { + const log = Output.scoped(.GetAddrInfoRequest, false); + + backend: Backend = undefined, + resolver_for_caching: ?*DNSResolver = null, + hash: u64 = 0, + cache: CacheConfig = CacheConfig{}, + head: DNSLookup, + tail: *DNSLookup = undefined, + task: bun.ThreadPool.Task = undefined, + + pub fn init( + cache: DNSResolver.CacheHit, + backend: Backend, + resolver: ?*DNSResolver, + query: GetAddrInfo, + globalThis: *JSC.JSGlobalObject, + comptime cache_field: []const u8, + ) !*GetAddrInfoRequest { + var request = try globalThis.allocator().create(GetAddrInfoRequest); + request.* = .{ + .backend = backend, + .resolver_for_caching = resolver, + .hash = query.hash(), + .head = .{ + .globalThis = globalThis, + .promise = JSC.JSPromise.Strong.init(globalThis), + .allocated = false, + }, + }; + request.tail = &request.head; + if (cache == .new) { + request.resolver_for_caching = resolver; + request.cache = CacheConfig{ + .pending_cache = true, + .entry_cache = false, + .pos_in_pending = @truncate(u5, @field(resolver.?, cache_field).indexOf(cache.new).?), + .name_len = @truncate(u9, query.name.len), + }; + cache.new.lookup = request; + } + return request; + } + + pub const Task = bun.JSC.WorkTask(GetAddrInfoRequest, false); + + pub const CacheConfig = packed struct(u16) { + pending_cache: bool = false, + entry_cache: bool = false, + pos_in_pending: u5 = 0, + name_len: u9 = 0, + }; + + pub const PendingCacheKey = struct { + hash: u64, + len: u16, + lookup: *GetAddrInfoRequest = undefined, + + pub fn append(this: *PendingCacheKey, dns_lookup: *DNSLookup) void { + var tail = this.lookup.tail; + tail.next = dns_lookup; + this.lookup.tail = dns_lookup; + } + + pub fn init(query: GetAddrInfo) PendingCacheKey { + return PendingCacheKey{ + .hash = query.hash(), + .len = @truncate(u16, query.name.len), + .lookup = undefined, + }; + } + }; + + pub fn getAddrInfoAsyncCallback( + status: i32, + addr_info: ?*std.c.addrinfo, + arg: ?*anyopaque, + ) callconv(.C) void { + const this = @intToPtr(*GetAddrInfoRequest, @ptrToInt(arg)); + log("getAddrInfoAsyncCallback: status={d}", .{status}); + + if (this.backend == .libinfo) { + if (this.backend.libinfo.file_poll) |poll| poll.deinit(); + } + + if (this.resolver_for_caching) |resolver| { + if (this.cache.pending_cache) { + resolver.drainPendingHostNative(this.cache.pos_in_pending, this.head.globalThis, status, .{ .addrinfo = addr_info }); + return; + } + } + + var head = this.head; + bun.default_allocator.destroy(this); + head.processGetAddrInfoNative(status, addr_info); + } + + pub const Backend = union(enum) { + c_ares: void, + libinfo: GetAddrInfoRequest.Backend.LibInfo, + libc: union(enum) { + success: GetAddrInfo.Result.List, + err: i32, + query: GetAddrInfo, + + pub fn run(this: *@This()) void { + const query = this.query; + defer bun.default_allocator.free(bun.constStrToU8(query.name)); + var hints = query.options.toLibC(); + var port_buf: [128]u8 = undefined; + var port = std.fmt.bufPrintIntToSlice(&port_buf, query.port, 10, .lower, .{}); + port_buf[port.len] = 0; + var portZ = port_buf[0..port.len :0]; + var hostname: [bun.MAX_PATH_BYTES]u8 = undefined; + _ = strings.copy(hostname[0..], query.name); + hostname[query.name.len] = 0; + var addrinfo: *std.c.addrinfo = undefined; + var host = hostname[0..query.name.len :0]; + const debug_timer = bun.Output.DebugTimer.start(); + const err = std.c.getaddrinfo( + host.ptr, + if (port.len > 0) portZ.ptr else null, + if (hints) |*hint| hint else null, + &addrinfo, + ); + defer std.c.freeaddrinfo(addrinfo); + JSC.Node.Syscall.syslog("getaddrinfo({s}, {d}) = {d} ({any})", .{ + query.name, + port, + err, + debug_timer, + }); + if (@enumToInt(err) != 0) { + this.* = .{ .err = @enumToInt(err) }; + return; + } + + this.* = .{ .success = GetAddrInfo.Result.toList(default_allocator, addrinfo) catch unreachable }; + } + }, + + pub const LibInfo = struct { + file_poll: ?*bun.JSC.FilePoll = null, + machport: ?*anyopaque = null, + + extern fn getaddrinfo_send_reply(*anyopaque, *const JSC.DNS.LibInfo.GetaddrinfoAsyncHandleReply) bool; + pub fn onMachportChange(this: *GetAddrInfoRequest) void { + if (!getaddrinfo_send_reply(this.backend.libinfo.machport.?, JSC.DNS.LibInfo.getaddrinfo_async_handle_reply().?)) { + log("onMachportChange: getaddrinfo_send_reply failed", .{}); + getAddrInfoAsyncCallback(-1, null, this); + } + } + }; + }; + + pub const onMachportChange = Backend.LibInfo.onMachportChange; + + pub fn run(this: *GetAddrInfoRequest, task: *Task) void { + this.backend.libc.run(); + task.onFinish(); + } + + pub fn then(this: *GetAddrInfoRequest, _: *JSC.JSGlobalObject) void { + switch (this.backend.libc) { + .success => |result| { + const any = GetAddrInfo.Result.Any{ .list = result }; + defer any.deinit(); + if (this.resolver_for_caching) |resolver| { + // if (this.cache.entry_cache and result != null and result.?.node != null) { + // resolver.putEntryInCache(this.hash, this.cache.name_len, result.?); + // } + + if (this.cache.pending_cache) { + resolver.drainPendingHostNative(this.cache.pos_in_pending, this.head.globalThis, 0, any); + return; + } + } + var head = this.head; + bun.default_allocator.destroy(this); + head.onCompleteNative(any); + }, + .err => |err| { + getAddrInfoAsyncCallback(err, null, this); + }, + else => unreachable, + } + } + + pub fn onCaresComplete(this: *GetAddrInfoRequest, err_: ?c_ares.Error, timeout: i32, result: ?*c_ares.AddrInfo) void { + if (this.resolver_for_caching) |resolver| { + // if (this.cache.entry_cache and result != null and result.?.node != null) { + // resolver.putEntryInCache(this.hash, this.cache.name_len, result.?); + // } + + if (this.cache.pending_cache) { + resolver.drainPendingHostCares( + this.cache.pos_in_pending, + err_, + timeout, + result, + ); + return; + } + } + + var head = this.head; + bun.default_allocator.destroy(this); + + head.processGetAddrInfo(err_, timeout, result); + } +}; + +pub const DNSLookup = struct { + const log = Output.scoped(.DNSLookup, true); + + globalThis: *JSC.JSGlobalObject = undefined, + promise: JSC.JSPromise.Strong, + allocated: bool = false, + next: ?*DNSLookup = null, + + pub fn init(globalThis: *JSC.JSGlobalObject, allocator: std.mem.Allocator) !*DNSLookup { + var this = try allocator.create(DNSLookup); + this.* = .{ + .globalThis = globalThis, + .promise = JSC.JSPromise.Strong.init(globalThis), + .allocated = true, + }; + return this; + } + + pub fn onCompleteNative(this: *DNSLookup, result: GetAddrInfo.Result.Any) void { + const array = result.toJS(this.globalThis).?; + this.onCompleteWithArray(array); + } + + pub fn processGetAddrInfoNative(this: *DNSLookup, status: i32, result: ?*std.c.addrinfo) void { + if (c_ares.Error.initEAI(status)) |err| { + var promise = this.promise; + var globalThis = this.globalThis; + + const error_value = brk: { + if (err == .ESERVFAIL) { + break :brk JSC.Node.Syscall.Error.fromCode(std.c.getErrno(-1), .getaddrinfo).toJSC(globalThis); + } + const error_value = globalThis.createErrorInstance("DNS lookup failed: {s}", .{err.label()}); + error_value.put( + globalThis, + JSC.ZigString.static("code"), + JSC.ZigString.init(err.code()).toValueGC(globalThis), + ); + break :brk error_value; + }; + + this.deinit(); + + promise.reject(globalThis, error_value); + return; + } + + onCompleteNative(this, .{ .addrinfo = result }); + } + + pub fn processGetAddrInfo(this: *DNSLookup, err_: ?c_ares.Error, _: i32, result: ?*c_ares.AddrInfo) void { + if (err_) |err| { + var promise = this.promise; + var globalThis = this.globalThis; + const error_value = globalThis.createErrorInstance("DNS lookup failed: {s}", .{err.label()}); + error_value.put( + globalThis, + JSC.ZigString.static("code"), + JSC.ZigString.init(err.code()).toValueGC(globalThis), + ); + + this.deinit(); + + promise.reject(globalThis, error_value); + return; + } + + if (result == null or result.?.node == null) { + var promise = this.promise; + var globalThis = this.globalThis; + const error_value = globalThis.createErrorInstance("DNS lookup failed: {s}", .{"No results"}); + error_value.put( + globalThis, + JSC.ZigString.static("code"), + JSC.ZigString.init("EUNREACHABLE").toValueGC(globalThis), + ); + + this.deinit(); + promise.reject(globalThis, error_value); + return; + } + + this.onComplete(result.?); + } + + pub fn onComplete(this: *DNSLookup, result: *c_ares.AddrInfo) void { + const array = result.toJSArray(this.globalThis.allocator(), this.globalThis); + this.onCompleteWithArray(array); + } + + pub fn onCompleteWithArray(this: *DNSLookup, result: JSC.JSValue) void { + var promise = this.promise; + var globalThis = this.globalThis; + this.promise = .{}; + + this.deinit(); + promise.resolveOnNextTick(globalThis, result); + } + + pub fn deinit(this: *DNSLookup) void { + if (this.allocated) + this.globalThis.allocator().destroy(this); + } +}; + +pub const GlobalData = struct { + resolver: DNSResolver, + + pub fn init(allocator: std.mem.Allocator, vm: *JSC.VirtualMachine) *GlobalData { + var global = allocator.create(GlobalData) catch unreachable; + global.* = .{ + .resolver = .{ + .vm = vm, + .polls = std.AutoArrayHashMap(i32, ?*JSC.FilePoll).init(allocator), + }, + }; + + return global; + } +}; + +pub const DNSResolver = struct { + const log = Output.scoped(.DNSResolver, true); + + channel: ?*c_ares.Channel = null, + vm: *JSC.VirtualMachine, + polls: std.AutoArrayHashMap(i32, ?*JSC.FilePoll) = undefined, + + pending_host_cache_cares: PendingCache = PendingCache.init(), + pending_host_cache_native: PendingCache = PendingCache.init(), + // entry_host_cache: std.BoundedArray(128) + + const PendingCache = bun.HiveArray(GetAddrInfoRequest.PendingCacheKey, 32); + + pub fn drainPendingHostCares(this: *DNSResolver, index: u8, err: ?c_ares.Error, timeout: i32, result: ?*c_ares.AddrInfo) void { + const key: GetAddrInfoRequest.PendingCacheKey = brk: { + std.debug.assert(!this.pending_host_cache_cares.available.isSet(index)); + const entry = this.pending_host_cache_cares.buffer[index]; + this.pending_host_cache_cares.buffer[index] = undefined; + this.pending_host_cache_cares.available.unset(index); + break :brk entry; + }; + + var addr = result orelse { + var pending: ?*DNSLookup = key.lookup.head.next; + key.lookup.head.processGetAddrInfo(err, timeout, null); + bun.default_allocator.destroy(key.lookup); + + while (pending) |value| { + pending = value.next; + value.processGetAddrInfo(err, timeout, null); + } + return; + }; + + var pending: ?*DNSLookup = key.lookup.head.next; + var prev_global = key.lookup.head.globalThis; + var array = addr.toJSArray(this.vm.allocator, prev_global); + defer addr.deinit(); + array.ensureStillAlive(); + key.lookup.head.onCompleteWithArray(array); + bun.default_allocator.destroy(key.lookup); + + array.ensureStillAlive(); + // std.c.addrinfo + + while (pending) |value| { + var new_global = value.globalThis; + if (prev_global != new_global) { + array = addr.toJSArray(this.vm.allocator, new_global); + prev_global = new_global; + } + array.ensureStillAlive(); + value.onCompleteWithArray(array); + array.ensureStillAlive(); + pending = value.next; + } + } + + pub fn drainPendingHostNative(this: *DNSResolver, index: u8, globalObject: *JSC.JSGlobalObject, err: i32, result: GetAddrInfo.Result.Any) void { + const key: GetAddrInfoRequest.PendingCacheKey = brk: { + std.debug.assert(!this.pending_host_cache_native.available.isSet(index)); + const entry = this.pending_host_cache_native.buffer[index]; + this.pending_host_cache_native.buffer[index] = undefined; + this.pending_host_cache_native.available.unset(index); + break :brk entry; + }; + + var array = result.toJS(globalObject) orelse { + var pending: ?*DNSLookup = key.lookup.head.next; + var head = key.lookup.head; + head.processGetAddrInfoNative(err, null); + bun.default_allocator.destroy(key.lookup); + + while (pending) |value| { + pending = value.next; + value.processGetAddrInfoNative(err, null); + } + + return; + }; + var pending: ?*DNSLookup = key.lookup.head.next; + var prev_global = key.lookup.head.globalThis; + array.ensureStillAlive(); + key.lookup.head.onCompleteWithArray(array); + bun.default_allocator.destroy(key.lookup); + array.ensureStillAlive(); + // std.c.addrinfo + + while (pending) |value| { + var new_global = value.globalThis; + if (prev_global != new_global) { + array = result.toJS(new_global).?; + prev_global = new_global; + } + array.ensureStillAlive(); + value.onCompleteWithArray(array); + array.ensureStillAlive(); + pending = value.next; + } + } + + pub const CacheHit = union(enum) { + inflight: *GetAddrInfoRequest.PendingCacheKey, + new: *GetAddrInfoRequest.PendingCacheKey, + disabled: void, + }; + + pub fn getOrPutIntoPendingCache( + this: *DNSResolver, + key: GetAddrInfoRequest.PendingCacheKey, + comptime field: std.meta.FieldEnum(DNSResolver), + ) CacheHit { + var cache: *PendingCache = &@field(this, @tagName(field)); + var inflight_iter = cache.available.iterator(.{ + .kind = .unset, + }); + + while (inflight_iter.next()) |index| { + var entry: *GetAddrInfoRequest.PendingCacheKey = &cache.buffer[index]; + if (entry.hash == key.hash and entry.len == key.len) { + return .{ .inflight = entry }; + } + } + + if (cache.get()) |new| { + new.hash = key.hash; + new.len = key.len; + return .{ .new = new }; + } + + return .{ .disabled = {} }; + } + + pub const ChannelResult = union(enum) { + err: c_ares.Error, + result: *c_ares.Channel, + }; + pub fn getChannel(this: *DNSResolver) ChannelResult { + if (this.channel == null) { + if (c_ares.Channel.init(DNSResolver, this)) |err| { + return .{ .err = err }; + } + } + + return .{ .result = this.channel.? }; + } + + pub fn onDNSPoll( + this: *DNSResolver, + poll: *JSC.FilePoll, + ) void { + var channel = this.channel orelse { + _ = this.polls.orderedRemove(@intCast(i32, poll.fd)); + poll.deinit(); + return; + }; + + channel.process( + @intCast(i32, poll.fd), + poll.isReadable(), + poll.isWritable(), + ); + } + + pub fn onDNSSocketState( + this: *DNSResolver, + fd: i32, + readable: bool, + writable: bool, + ) void { + var vm = this.vm; + + if (!readable and !writable) { + // read == 0 and write == 0 this is c-ares's way of notifying us that + // the socket is now closed. We must free the data associated with + // socket. + if (this.polls.fetchOrderedRemove(fd)) |entry| { + if (entry.value) |val| { + val.deinitWithVM(vm); + } + } + + return; + } + + var poll_entry = this.polls.getOrPut(fd) catch unreachable; + + if (!poll_entry.found_existing) { + poll_entry.value_ptr.* = JSC.FilePoll.init(vm, fd, .{}, DNSResolver, this); + } + + var poll = poll_entry.value_ptr.*.?; + + if (readable and !poll.flags.contains(.poll_readable)) + _ = poll.register(vm.uws_event_loop.?, .readable, false); + + if (writable and !poll.flags.contains(.poll_writable)) + _ = poll.register(vm.uws_event_loop.?, .writable, false); + } + + const DNSQuery = struct { + name: JSC.ZigString.Slice, + record_type: RecordType, + + ttl: i32 = 0, + }; + + pub const RecordType = enum(u8) { + A = 1, + AAAA = 28, + CNAME = 5, + MX = 15, + NS = 2, + PTR = 12, + SOA = 6, + SRV = 33, + TXT = 16, + + pub const default = RecordType.A; + + pub const map = bun.ComptimeStringMap(RecordType, .{ + .{ "A", .A }, + .{ "AAAA", .AAAA }, + .{ "CNAME", .CNAME }, + .{ "MX", .MX }, + .{ "NS", .NS }, + .{ "PTR", .PTR }, + .{ "SOA", .SOA }, + .{ "SRV", .SRV }, + .{ "TXT", .TXT }, + .{ "a", .A }, + .{ "aaaa", .AAAA }, + .{ "cname", .CNAME }, + .{ "mx", .MX }, + .{ "ns", .NS }, + .{ "ptr", .PTR }, + .{ "soa", .SOA }, + .{ "srv", .SRV }, + .{ "txt", .TXT }, + }); + }; + + pub fn resolve(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const arguments = callframe.arguments(3); + if (arguments.len < 1) { + globalThis.throwNotEnoughArguments("resolve", 2, arguments.len); + return .zero; + } + + const record_type: RecordType = if (arguments.len == 1) + RecordType.default + else brk: { + const record_type_value = arguments.ptr[1]; + if (record_type_value.isEmptyOrUndefinedOrNull() or !record_type_value.isString()) { + break :brk RecordType.default; + } + + const record_type_str = record_type_value.toStringOrNull(globalThis) orelse { + return .zero; + }; + + if (record_type_str.length() == 0) { + break :brk RecordType.default; + } + + break :brk RecordType.map.getWithEql(record_type_str.getZigString(globalThis), JSC.ZigString.eqlComptime) orelse { + globalThis.throwInvalidArgumentType("resolve", "record", "one of: A, AAAA, CNAME, MX, NS, PTR, SOA, SRV, TXT"); + return .zero; + }; + }; + _ = record_type; + + const name_value = arguments.ptr[0]; + + if (name_value.isEmptyOrUndefinedOrNull() or !name_value.isString()) { + globalThis.throwInvalidArgumentType("resolve", "name", "string"); + return .zero; + } + + const name_str = name_value.toStringOrNull(globalThis) orelse { + return .zero; + }; + + if (name_str.length() == 0) { + globalThis.throwInvalidArgumentType("resolve", "name", "non-empty string"); + return .zero; + } + + // const name = name_str.toSliceZ(globalThis).cloneZ(bun.default_allocator) catch unreachable; + // TODO: + return JSC.JSValue.jsUndefined(); + } + // pub fn reverse(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + // const arguments = callframe.arguments(3); + + // } + + pub fn lookup(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + const arguments = callframe.arguments(2); + if (arguments.len < 1) { + globalThis.throwNotEnoughArguments("lookup", 2, arguments.len); + return .zero; + } + + const name_value = arguments.ptr[0]; + + if (name_value.isEmptyOrUndefinedOrNull() or !name_value.isString()) { + globalThis.throwInvalidArgumentType("lookup", "hostname", "string"); + return .zero; + } + + const name_str = name_value.toStringOrNull(globalThis) orelse { + return .zero; + }; + + if (name_str.length() == 0) { + globalThis.throwInvalidArgumentType("lookup", "hostname", "non-empty string"); + return .zero; + } + + var options = GetAddrInfo.Options{}; + var port: u16 = 0; + + if (arguments.len > 1 and arguments.ptr[1].isCell()) { + if (arguments.ptr[1].get(globalThis, "port")) |port_value| { + if (port_value.isNumber()) { + port = port_value.to(u16); + } + } + + options = GetAddrInfo.Options.fromJS(arguments.ptr[1], globalThis) catch |err| { + globalThis.throw("Invalid options passed to lookup(): {s}", .{@errorName(err)}); + return .zero; + }; + } + + const name = name_str.toSlice(globalThis, bun.default_allocator); + defer name.deinit(); + var vm = globalThis.bunVM(); + var resolver = vm.rareData().globalDNSResolver(vm); + + return resolver.doLookup(name.slice(), port, options, globalThis); + } + + pub fn doLookup(this: *DNSResolver, name: []const u8, port: u16, options: GetAddrInfo.Options, globalThis: *JSC.JSGlobalObject) JSC.JSValue { + var opts = options; + var backend = opts.backend; + const normalized = normalizeDNSName(name, &backend); + opts.backend = backend; + const query = GetAddrInfo{ + .options = opts, + .port = port, + .name = normalized, + }; + return switch (opts.backend) { + .c_ares => this.c_aresLookupWithNormalizedName(query, globalThis), + .libc => LibC.lookup(this, query, globalThis), + .system => if (comptime Environment.isMac) + LibInfo.lookup(this, query, globalThis) + else + LibC.lookup(this, query, globalThis), + }; + } + + pub fn c_aresLookupWithNormalizedName(this: *DNSResolver, query: GetAddrInfo, globalThis: *JSC.JSGlobalObject) JSC.JSValue { + var channel: *c_ares.Channel = switch (this.getChannel()) { + .result => |res| res, + .err => |err| { + const system_error = JSC.SystemError{ + .errno = -1, + .code = JSC.ZigString.init(err.code()), + .message = JSC.ZigString.init(err.label()), + }; + + globalThis.throwValue(system_error.toErrorInstance(globalThis)); + return .zero; + }, + }; + + const key = GetAddrInfoRequest.PendingCacheKey.init(query); + + var cache = this.getOrPutIntoPendingCache(key, .pending_host_cache_cares); + if (cache == .inflight) { + var dns_lookup = DNSLookup.init(globalThis, globalThis.allocator()) catch unreachable; + cache.inflight.append(dns_lookup); + return dns_lookup.promise.value(); + } + + // var hints_buf = &[_]c_ares.AddrInfo_hints{query.toCAres()}; + var request = GetAddrInfoRequest.init( + cache, + .{ + .c_ares = {}, + }, + this, + query, + globalThis, + "pending_host_cache_cares", + ) catch unreachable; + const promise = request.tail.promise.value(); + + channel.getAddrInfo( + query.name, + query.port, + &.{}, + GetAddrInfoRequest, + request, + GetAddrInfoRequest.onCaresComplete, + ); + + return promise; + } + + comptime { + @export( + lookup, + .{ + .name = "Bun__DNSResolver__lookup", + }, + ); + } + // pub fn lookupService(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + // const arguments = callframe.arguments(3); + + // } + // pub fn cancel(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + // const arguments = callframe.arguments(3); + + // } +}; diff --git a/src/bun.js/base.zig b/src/bun.js/base.zig index 671a4c197..333ea36c9 100644 --- a/src/bun.js/base.zig +++ b/src/bun.js/base.zig @@ -3260,6 +3260,8 @@ pub const FilePoll = struct { const Subprocess = JSC.Subprocess; const BufferedInput = Subprocess.BufferedInput; const BufferedOutput = Subprocess.BufferedOutput; + const DNSResolver = JSC.DNS.DNSResolver; + const GetAddrInfoRequest = JSC.DNS.GetAddrInfoRequest; const Deactivated = opaque { pub var owner: Owner = Owner.init(@intToPtr(*Deactivated, @as(usize, 0xDEADBEEF))); }; @@ -3271,6 +3273,8 @@ pub const FilePoll = struct { BufferedInput, FIFO, Deactivated, + DNSResolver, + GetAddrInfoRequest, }); fn updateFlags(poll: *FilePoll, updated: Flags.Set) void { @@ -3278,6 +3282,7 @@ pub const FilePoll = struct { flags.remove(.readable); flags.remove(.writable); flags.remove(.process); + flags.remove(.machport); flags.remove(.eof); flags.remove(.hup); @@ -3331,19 +3336,24 @@ pub const FilePoll = struct { this.deinitWithVM(vm); } - pub fn deinitWithVM(this: *FilePoll, vm: *JSC.VirtualMachine) void { + pub fn deinitWithoutVM(this: *FilePoll, loop: *uws.Loop, polls: *JSC.FilePoll.HiveArray) void { if (this.isRegistered()) { - _ = this.unregister(vm.uws_event_loop.?); + _ = this.unregister(loop); } this.owner = Deactivated.owner; this.flags = Flags.Set{}; this.fd = invalid_fd; - vm.rareData().filePolls(vm).put(this); + polls.put(this); + } + + pub fn deinitWithVM(this: *FilePoll, vm: *JSC.VirtualMachine) void { + var loop = vm.uws_event_loop.?; + this.deinitWithoutVM(loop, vm.rareData().filePolls(vm)); } pub fn isRegistered(this: *const FilePoll) bool { - return this.flags.contains(.poll_writable) or this.flags.contains(.poll_readable) or this.flags.contains(.poll_process); + return this.flags.contains(.poll_writable) or this.flags.contains(.poll_readable) or this.flags.contains(.poll_process) or this.flags.contains(.poll_machport); } const kqueue_or_epoll = if (Environment.isMac) "kevent" else "epoll"; @@ -3371,6 +3381,18 @@ pub const FilePoll = struct { loader.onPoll(size_or_offset, 0); }, + @field(Owner.Tag, "DNSResolver") => { + log("onUpdate " ++ kqueue_or_epoll ++ " (fd: {d}) DNSResolver", .{poll.fd}); + var loader: *DNSResolver = ptr.as(DNSResolver); + loader.onDNSPoll(poll); + }, + + @field(Owner.Tag, "GetAddrInfoRequest") => { + log("onUpdate " ++ kqueue_or_epoll ++ " (fd: {d}) GetAddrInfoRequest", .{poll.fd}); + var loader: *GetAddrInfoRequest = ptr.as(GetAddrInfoRequest); + loader.onMachportChange(); + }, + else => { log("onUpdate " ++ kqueue_or_epoll ++ " (fd: {d}) disconnected?", .{poll.fd}); }, @@ -3389,12 +3411,16 @@ pub const FilePoll = struct { /// Poll for process-related events poll_process, + /// Poll for machport events + poll_machport, + // What did the event loop tell us? readable, writable, process, eof, hup, + machport, // What is the type of file descriptor? fifo, @@ -3414,6 +3440,7 @@ pub const FilePoll = struct { .readable => .poll_readable, .writable => .poll_writable, .process => .poll_process, + .machport => .poll_machport, else => this, }; } @@ -3440,6 +3467,9 @@ pub const FilePoll = struct { } else if (kqueue_event.filter == std.os.system.EVFILT_PROC) { log("proc", .{}); flags.insert(Flags.process); + } else if (kqueue_event.filter == std.os.system.EVFILT_MACHPORT) { + log("machport", .{}); + flags.insert(Flags.machport); } return flags; } @@ -3590,18 +3620,21 @@ pub const FilePoll = struct { const timeout = std.mem.zeroes(std.os.timespec); const kevent = std.c.kevent; const linux = std.os.linux; + pub fn register(this: *FilePoll, loop: *uws.Loop, flag: Flags, one_shot: bool) JSC.Maybe(void) { + return registerWithFd(this, loop, flag, one_shot, this.fd); + } + pub fn registerWithFd(this: *FilePoll, loop: *uws.Loop, flag: Flags, one_shot: bool, fd: u64) JSC.Maybe(void) { const watcher_fd = loop.fd; - const fd = this.fd; log("register: {s} ({d})", .{ @tagName(flag), fd }); + std.debug.assert(fd != invalid_fd); + if (one_shot) { this.flags.insert(.one_shot); } - std.debug.assert(this.fd != invalid_fd); - if (comptime Environment.isLinux) { const one_shot_flag: u32 = if (!this.flags.contains(.one_shot)) 0 else linux.EPOLL.ONESHOT; @@ -3656,6 +3689,15 @@ pub const FilePoll = struct { .flags = std.c.EV_ADD | one_shot_flag, .ext = .{ this.generation_number, 0 }, }, + .machport => .{ + .ident = @intCast(u64, fd), + .filter = std.os.system.EVFILT_MACHPORT, + .data = 0, + .fflags = 0, + .udata = @ptrToInt(Pollable.init(this).ptr()), + .flags = std.c.EV_ADD | one_shot_flag, + .ext = .{ this.generation_number, 0 }, + }, else => unreachable, }; @@ -3711,6 +3753,7 @@ pub const FilePoll = struct { .readable => .poll_readable, .process => if (comptime Environment.isLinux) .poll_readable else .poll_process, .writable => .poll_writable, + .machport => .poll_machport, else => unreachable, }); this.flags.remove(.needs_rearm); @@ -3721,12 +3764,15 @@ pub const FilePoll = struct { const invalid_fd = bun.invalid_fd; pub fn unregister(this: *FilePoll, loop: *uws.Loop) JSC.Maybe(void) { - if (!(this.flags.contains(.poll_readable) or this.flags.contains(.poll_writable) or this.flags.contains(.poll_process))) { + return this.unregisterWithFd(loop, this.fd); + } + + pub fn unregisterWithFd(this: *FilePoll, loop: *uws.Loop, fd: u64) JSC.Maybe(void) { + if (!(this.flags.contains(.poll_readable) or this.flags.contains(.poll_writable) or this.flags.contains(.poll_process) or this.flags.contains(.poll_machport))) { // no-op return JSC.Maybe(void).success; } - const fd = this.fd; std.debug.assert(fd != invalid_fd); const watcher_fd = loop.fd; const flag: Flags = brk: { @@ -3736,6 +3782,9 @@ pub const FilePoll = struct { break :brk .writable; if (this.flags.contains(.poll_process)) break :brk .process; + + if (this.flags.contains(.poll_machport)) + break :brk .machport; return JSC.Maybe(void).success; }; @@ -3744,6 +3793,7 @@ pub const FilePoll = struct { this.flags.remove(.poll_process); this.flags.remove(.poll_readable); this.flags.remove(.poll_process); + this.flags.remove(.poll_machport); return JSC.Maybe(void).success; } @@ -3773,6 +3823,15 @@ pub const FilePoll = struct { .flags = std.c.EV_DELETE, .ext = .{ 0, 0 }, }, + .machport => .{ + .ident = @intCast(u64, fd), + .filter = std.os.system.EVFILT_MACHPORT, + .data = 0, + .fflags = 0, + .udata = @ptrToInt(Pollable.init(this).ptr()), + .flags = std.c.EV_DELETE, + .ext = .{ 0, 0 }, + }, .writable => .{ .ident = @intCast(u64, fd), .filter = std.os.system.EVFILT_WRITE, @@ -3836,6 +3895,7 @@ pub const FilePoll = struct { this.flags.remove(.poll_readable); this.flags.remove(.poll_writable); this.flags.remove(.poll_process); + this.flags.remove(.poll_machport); if (this.isActive()) this.deactivate(loop); diff --git a/src/bun.js/bindings/ZigGeneratedCode.cpp b/src/bun.js/bindings/ZigGeneratedCode.cpp index 7b8807255..d7b48aa04 100644 --- a/src/bun.js/bindings/ZigGeneratedCode.cpp +++ b/src/bun.js/bindings/ZigGeneratedCode.cpp @@ -36,7 +36,7 @@ extern "C" void FFI__ptr__put(JSC::JSGlobalObject *globalObject, JSC::EncodedJSV static const JSC::DOMJIT::Signature DOMJIT_ptr_signature( FFI__ptr__fastpathWrapper, thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), +JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), JSC::SpecDoubleReal, JSC::SpecUint8Array ); @@ -78,7 +78,7 @@ extern "C" void Reader__u8__put(JSC::JSGlobalObject *globalObject, JSC::EncodedJ static const JSC::DOMJIT::Signature DOMJIT_u8_signature( Reader__u8__fastpathWrapper, thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), +JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), JSC::SpecInt32Only, JSC::SpecInt52Any, JSC::SpecInt32Only @@ -121,7 +121,7 @@ extern "C" void Reader__u16__put(JSC::JSGlobalObject *globalObject, JSC::Encoded static const JSC::DOMJIT::Signature DOMJIT_u16_signature( Reader__u16__fastpathWrapper, thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), +JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), JSC::SpecInt32Only, JSC::SpecInt52Any, JSC::SpecInt32Only @@ -164,7 +164,7 @@ extern "C" void Reader__u32__put(JSC::JSGlobalObject *globalObject, JSC::Encoded static const JSC::DOMJIT::Signature DOMJIT_u32_signature( Reader__u32__fastpathWrapper, thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), +JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), JSC::SpecInt32Only, JSC::SpecInt52Any, JSC::SpecInt32Only @@ -207,7 +207,7 @@ extern "C" void Reader__ptr__put(JSC::JSGlobalObject *globalObject, JSC::Encoded static const JSC::DOMJIT::Signature DOMJIT_ptr_signature( Reader__ptr__fastpathWrapper, thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), +JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), JSC::SpecInt52Any, JSC::SpecInt52Any, JSC::SpecInt32Only @@ -250,7 +250,7 @@ extern "C" void Reader__i8__put(JSC::JSGlobalObject *globalObject, JSC::EncodedJ static const JSC::DOMJIT::Signature DOMJIT_i8_signature( Reader__i8__fastpathWrapper, thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), +JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), JSC::SpecInt32Only, JSC::SpecInt52Any, JSC::SpecInt32Only @@ -293,7 +293,7 @@ extern "C" void Reader__i16__put(JSC::JSGlobalObject *globalObject, JSC::Encoded static const JSC::DOMJIT::Signature DOMJIT_i16_signature( Reader__i16__fastpathWrapper, thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), +JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), JSC::SpecInt32Only, JSC::SpecInt52Any, JSC::SpecInt32Only @@ -336,7 +336,7 @@ extern "C" void Reader__i32__put(JSC::JSGlobalObject *globalObject, JSC::Encoded static const JSC::DOMJIT::Signature DOMJIT_i32_signature( Reader__i32__fastpathWrapper, thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), +JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), JSC::SpecInt32Only, JSC::SpecInt52Any, JSC::SpecInt32Only @@ -379,7 +379,7 @@ extern "C" void Reader__i64__put(JSC::JSGlobalObject *globalObject, JSC::Encoded static const JSC::DOMJIT::Signature DOMJIT_i64_signature( Reader__i64__fastpathWrapper, thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), +JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), JSC::SpecHeapTop, JSC::SpecInt52Any, JSC::SpecInt32Only @@ -422,7 +422,7 @@ extern "C" void Reader__u64__put(JSC::JSGlobalObject *globalObject, JSC::Encoded static const JSC::DOMJIT::Signature DOMJIT_u64_signature( Reader__u64__fastpathWrapper, thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), +JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), JSC::SpecHeapTop, JSC::SpecInt52Any, JSC::SpecInt32Only @@ -465,7 +465,7 @@ extern "C" void Reader__intptr__put(JSC::JSGlobalObject *globalObject, JSC::Enco static const JSC::DOMJIT::Signature DOMJIT_intptr_signature( Reader__intptr__fastpathWrapper, thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), +JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), JSC::SpecInt52Any, JSC::SpecInt52Any, JSC::SpecInt32Only @@ -508,7 +508,7 @@ extern "C" void Reader__f32__put(JSC::JSGlobalObject *globalObject, JSC::Encoded static const JSC::DOMJIT::Signature DOMJIT_f32_signature( Reader__f32__fastpathWrapper, thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), +JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), JSC::SpecDoubleReal, JSC::SpecInt52Any, JSC::SpecInt32Only @@ -551,7 +551,7 @@ extern "C" void Reader__f64__put(JSC::JSGlobalObject *globalObject, JSC::Encoded static const JSC::DOMJIT::Signature DOMJIT_f64_signature( Reader__f64__fastpathWrapper, thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), +JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), JSC::SpecDoubleReal, JSC::SpecInt52Any, JSC::SpecInt32Only @@ -594,7 +594,7 @@ extern "C" void Crypto__getRandomValues__put(JSC::JSGlobalObject *globalObject, static const JSC::DOMJIT::Signature DOMJIT_getRandomValues_signature( Crypto__getRandomValues__fastpathWrapper, thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), +JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), JSC::SpecHeapTop, JSC::SpecUint8Array ); @@ -636,7 +636,7 @@ extern "C" void Crypto__randomUUID__put(JSC::JSGlobalObject *globalObject, JSC:: static const JSC::DOMJIT::Signature DOMJIT_randomUUID_signature( Crypto__randomUUID__fastpathWrapper, thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), +JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), JSC::SpecString); JSFunction* function = JSFunction::create( globalObject->vm(), @@ -676,7 +676,7 @@ extern "C" void Crypto__timingSafeEqual__put(JSC::JSGlobalObject *globalObject, static const JSC::DOMJIT::Signature DOMJIT_timingSafeEqual_signature( Crypto__timingSafeEqual__fastpathWrapper, thisObject->classInfo(), - JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), +JSC::DOMJIT::Effect::forReadWrite(JSC::DOMJIT::HeapRange::top(), JSC::DOMJIT::HeapRange::top()), JSC::SpecHeapTop, JSC::SpecUint8Array, JSC::SpecUint8Array diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 1a9a1dc1b..12401d8d1 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -2168,6 +2168,8 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionPerformMicrotask, (JSGlobalObject * globalObj return JSValue::encode(jsUndefined()); } +extern "C" EncodedJSValue Bun__DNSResolver__lookup(JSGlobalObject*, JSC::CallFrame*); + JSC_DEFINE_HOST_FUNCTION(jsFunctionPerformMicrotaskVariadic, (JSGlobalObject * globalObject, CallFrame* callframe)) { auto& vm = globalObject->vm(); @@ -3304,6 +3306,14 @@ void GlobalObject::installAPIGlobals(JSClassRef* globals, int count, JSC::VM& vm } { + JSC::Identifier identifier = JSC::Identifier::fromString(vm, "dns"_s); + JSC::JSObject* dnsObject = JSC::constructEmptyObject(this); + dnsObject->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "lookup"_s), 2, Bun__DNSResolver__lookup, ImplementationVisibility::Public, NoIntrinsic, + JSC::PropertyAttribute::Function | JSC::PropertyAttribute::DontDelete | 0); + object->putDirect(vm, PropertyName(identifier), dnsObject, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontDelete | 0); + } + + { JSC::Identifier identifier = JSC::Identifier::fromString(vm, "plugin"_s); JSFunction* pluginFunction = JSFunction::create(vm, this, 1, String("plugin"_s), jsFunctionBunPlugin, ImplementationVisibility::Public, NoIntrinsic); pluginFunction->putDirectNativeFunction(vm, this, JSC::Identifier::fromString(vm, "clearAll"_s), 1, jsFunctionBunPluginClear, ImplementationVisibility::Public, NoIntrinsic, diff --git a/src/bun.js/bindings/ares_build.h b/src/bun.js/bindings/ares_build.h new file mode 100644 index 000000000..2c4927ba5 --- /dev/null +++ b/src/bun.js/bindings/ares_build.h @@ -0,0 +1,42 @@ +#ifndef __CARES_BUILD_H +#define __CARES_BUILD_H + +#define CARES_TYPEOF_ARES_SOCKLEN_T socklen_t +#define CARES_TYPEOF_ARES_SSIZE_T ssize_t + +/* Prefix names with CARES_ to make sure they don't conflict with other config.h + * files. We need to include some dependent headers that may be system specific + * for C-Ares */ +#define CARES_HAVE_SYS_TYPES_H +#define CARES_HAVE_SYS_SOCKET_H +/* #undef CARES_HAVE_WINDOWS_H */ +/* #undef CARES_HAVE_WS2TCPIP_H */ +/* #undef CARES_HAVE_WINSOCK2_H */ +/* #undef CARES_HAVE_WINDOWS_H */ +#define CARES_HAVE_ARPA_NAMESER_H +#define CARES_HAVE_ARPA_NAMESER_COMPAT_H + +#ifdef CARES_HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#ifdef CARES_HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#ifdef CARES_HAVE_WINSOCK2_H +#include <winsock2.h> +#endif + +#ifdef CARES_HAVE_WS2TCPIP_H +#include <ws2tcpip.h> +#endif + +#ifdef CARES_HAVE_WINDOWS_H +#include <windows.h> +#endif + +typedef CARES_TYPEOF_ARES_SOCKLEN_T ares_socklen_t; +typedef CARES_TYPEOF_ARES_SSIZE_T ares_ssize_t; + +#endif /* __CARES_BUILD_H */ diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index f73a1b6e0..5f3dc3dac 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -1982,6 +1982,50 @@ void JSC__JSPromise__resolve(JSC__JSPromise* arg0, JSC__JSGlobalObject* arg1, { arg0->resolve(arg1, JSC::JSValue::decode(JSValue2)); } + +// This implementation closely mimicks the one in JSC::JSPromise::resolve +void JSC__JSPromise__resolveOnNextTick(JSC__JSPromise* promise, JSC__JSGlobalObject* lexicalGlobalObject, + JSC__JSValue encoedValue) +{ + JSC::JSValue value = JSC::JSValue::decode(encoedValue); + VM& vm = lexicalGlobalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + uint32_t flags = promise->internalField(JSC::JSPromise::Field::Flags).get().asUInt32(); + if (!(flags & JSC::JSPromise::isFirstResolvingFunctionCalledFlag)) { + promise->internalField(JSC::JSPromise::Field::Flags).set(vm, promise, jsNumber(flags | JSC::JSPromise::isFirstResolvingFunctionCalledFlag)); + auto* globalObject = jsCast<Zig::GlobalObject*>(promise->globalObject()); + + globalObject->queueMicrotask( + globalObject->performMicrotaskFunction(), + globalObject->resolvePromiseFunction(), + promise, + value, + JSValue {}); + RETURN_IF_EXCEPTION(scope, void()); + } +} + +// This implementation closely mimicks the one in JSC::JSPromise::reject +void JSC__JSPromise__rejectOnNextTick(JSC__JSPromise* promise, JSC__JSGlobalObject* lexicalGlobalObject, + JSC__JSValue encoedValue) +{ + JSC::JSValue value = JSC::JSValue::decode(encoedValue); + VM& vm = lexicalGlobalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + uint32_t flags = promise->internalField(JSC::JSPromise::Field::Flags).get().asUInt32(); + if (!(flags & JSC::JSPromise::isFirstResolvingFunctionCalledFlag)) { + promise->internalField(JSC::JSPromise::Field::Flags).set(vm, promise, jsNumber(flags | JSC::JSPromise::isFirstResolvingFunctionCalledFlag)); + auto* globalObject = jsCast<Zig::GlobalObject*>(promise->globalObject()); + + globalObject->queueMicrotask( + globalObject->performMicrotaskFunction(), + globalObject->rejectPromiseFunction(), + promise, + value, + JSValue {}); + RETURN_IF_EXCEPTION(scope, void()); + } +} JSC__JSPromise* JSC__JSPromise__resolvedPromise(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1) { Zig::GlobalObject* global = reinterpret_cast<Zig::GlobalObject*>(arg0); @@ -3574,4 +3618,16 @@ extern "C" size_t JSC__VM__externalMemorySize(JSC__VM* vm) #else return 0; #endif +} + +extern "C" void JSC__JSGlobalObject__queueMicrotaskJob(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue JSValue2, JSC__JSValue JSValue3, JSC__JSValue JSValue4) +{ + Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(arg0); + JSC::VM& vm = globalObject->vm(); + globalObject->queueMicrotask( + JSValue(globalObject->performMicrotaskFunction()), + JSC::JSValue::decode(JSValue1), + JSC::JSValue::decode(JSValue2), + JSC::JSValue::decode(JSValue3), + JSC::JSValue::decode(JSValue4)); }
\ No newline at end of file diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index 4da47ec70..f86faf7fd 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -1464,10 +1464,18 @@ pub const JSPromise = extern struct { this.swap().reject(globalThis, val); } + pub fn rejectOnNextTick(this: *Strong, globalThis: *JSC.JSGlobalObject, val: JSC.JSValue) void { + this.swap().rejectOnNextTick(globalThis, val); + } + pub fn resolve(this: *Strong, globalThis: *JSC.JSGlobalObject, val: JSC.JSValue) void { this.swap().resolve(globalThis, val); } + pub fn resolveOnNextTick(this: *Strong, globalThis: *JSC.JSGlobalObject, val: JSC.JSValue) void { + this.swap().resolveOnNextTick(globalThis, val); + } + pub fn init(globalThis: *JSC.JSGlobalObject) Strong { return Strong{ .strong = JSC.Strong.create( @@ -1530,6 +1538,14 @@ pub const JSPromise = extern struct { return cppFn("resolvedPromise", .{ globalThis, value }); } + pub fn resolveOnNextTick(promise: *JSC.JSPromise, globalThis: *JSGlobalObject, value: JSC.JSValue) void { + return cppFn("resolveOnNextTick", .{ promise, globalThis, value }); + } + + pub fn rejectOnNextTick(promise: *JSC.JSPromise, globalThis: *JSGlobalObject, value: JSC.JSValue) void { + return cppFn("rejectOnNextTick", .{ promise, globalThis, value }); + } + /// Create a new promise with an already fulfilled value /// This is the faster function for doing that. pub fn resolvedPromiseValue(globalThis: *JSGlobalObject, value: JSValue) JSValue { @@ -1572,21 +1588,23 @@ pub const JSPromise = extern struct { } pub const Extern = [_][]const u8{ - "rejectWithCaughtException", - "status", - "result", + "asValue", + "create", "isHandled", - "resolvedPromise", - "rejectedPromise", - "resolve", "reject", "rejectAsHandled", - // "rejectException", "rejectAsHandledException", + "rejectOnNextTick", + "rejectWithCaughtException", + "rejectedPromise", "rejectedPromiseValue", + "resolve", + "resolveOnNextTick", + "resolvedPromise", "resolvedPromiseValue", - "asValue", - "create", + "result", + "status", + // "rejectException", }; }; @@ -2065,6 +2083,35 @@ pub const JSGlobalObject = extern struct { this.vm().throwError(this, this.createErrorInstance(fmt, args)); } + pub fn queueMicrotask( + this: *JSGlobalObject, + function: JSValue, + args: []JSC.JSValue, + ) void { + this.queueMicrotaskJob( + function, + if (args.len > 0) args[0] else .zero, + if (args.len > 1) args[1] else .zero, + if (args.len > 2) args[2] else .zero, + ); + } + + pub fn queueMicrotaskJob( + this: *JSGlobalObject, + function: JSValue, + first: JSValue, + second: JSValue, + third: JSValue, + ) void { + shim.cppFn("queueMicrotaskJob", .{ + this, + function, + first, + second, + third, + }); + } + pub fn throwValue( this: *JSGlobalObject, value: JSC.JSValue, @@ -2211,6 +2258,7 @@ pub const JSGlobalObject = extern struct { "startRemoteInspector", "handleRejectedPromises", "createSyntheticModule_", + "queueMicrotaskJob", // "createError", // "throwError", }; diff --git a/src/bun.js/bindings/glibc-versions-hack.cpp b/src/bun.js/bindings/glibc-versions-hack.cpp index 1be279202..33d94c809 100644 --- a/src/bun.js/bindings/glibc-versions-hack.cpp +++ b/src/bun.js/bindings/glibc-versions-hack.cpp @@ -2,7 +2,7 @@ #if defined(__linux__) #include <fcntl.h> -//#include <sys/stat.h> +// #include <sys/stat.h> #include <stdarg.h> #include <math.h> #include <errno.h> @@ -181,4 +181,4 @@ extern "C" int __wrap_mknodat(int dirfd, const char* path, __mode_t mode, __dev_ return __xmknodat(_MKNOD_VER, dirfd, path, mode, dev); } -#endif
\ No newline at end of file +#endif diff --git a/src/bun.js/bindings/headers-cpp.h b/src/bun.js/bindings/headers-cpp.h index 4ed3115af..0079b0ab7 100644 --- a/src/bun.js/bindings/headers-cpp.h +++ b/src/bun.js/bindings/headers-cpp.h @@ -1,4 +1,4 @@ -//-- AUTOGENERATED FILE -- 1672499809 +//-- AUTOGENERATED FILE -- 1673090883 // clang-format off #pragma once diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h index 0a1968637..728c81afb 100644 --- a/src/bun.js/bindings/headers.h +++ b/src/bun.js/bindings/headers.h @@ -1,5 +1,5 @@ // clang-format off -//-- AUTOGENERATED FILE -- 1672499809 +//-- AUTOGENERATED FILE -- 1673090883 #pragma once #include <stddef.h> @@ -200,10 +200,12 @@ CPP_DECL void JSC__JSPromise__rejectAsHandled(JSC__JSPromise* arg0, JSC__JSGloba CPP_DECL void JSC__JSPromise__rejectAsHandledException(JSC__JSPromise* arg0, JSC__JSGlobalObject* arg1, JSC__Exception* arg2); CPP_DECL JSC__JSPromise* JSC__JSPromise__rejectedPromise(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); CPP_DECL JSC__JSValue JSC__JSPromise__rejectedPromiseValue(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); +CPP_DECL void JSC__JSPromise__rejectOnNextTick(JSC__JSPromise* arg0, JSC__JSGlobalObject* arg1, JSC__JSValue JSValue2); CPP_DECL void JSC__JSPromise__rejectWithCaughtException(JSC__JSPromise* arg0, JSC__JSGlobalObject* arg1, bJSC__ThrowScope arg2); CPP_DECL void JSC__JSPromise__resolve(JSC__JSPromise* arg0, JSC__JSGlobalObject* arg1, JSC__JSValue JSValue2); CPP_DECL JSC__JSPromise* JSC__JSPromise__resolvedPromise(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); CPP_DECL JSC__JSValue JSC__JSPromise__resolvedPromiseValue(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1); +CPP_DECL void JSC__JSPromise__resolveOnNextTick(JSC__JSPromise* arg0, JSC__JSGlobalObject* arg1, JSC__JSValue JSValue2); CPP_DECL JSC__JSValue JSC__JSPromise__result(JSC__JSPromise* arg0, JSC__VM* arg1); CPP_DECL uint32_t JSC__JSPromise__status(const JSC__JSPromise* arg0, JSC__VM* arg1); @@ -235,6 +237,7 @@ CPP_DECL JSC__JSValue JSC__JSGlobalObject__generateHeapSnapshot(JSC__JSGlobalObj CPP_DECL JSC__JSValue JSC__JSGlobalObject__getCachedObject(JSC__JSGlobalObject* arg0, const ZigString* arg1); CPP_DECL void JSC__JSGlobalObject__handleRejectedPromises(JSC__JSGlobalObject* arg0); CPP_DECL JSC__JSValue JSC__JSGlobalObject__putCachedObject(JSC__JSGlobalObject* arg0, const ZigString* arg1, JSC__JSValue JSValue2); +CPP_DECL void JSC__JSGlobalObject__queueMicrotaskJob(JSC__JSGlobalObject* arg0, JSC__JSValue JSValue1, JSC__JSValue JSValue2, JSC__JSValue JSValue3, JSC__JSValue JSValue4); CPP_DECL void JSC__JSGlobalObject__reload(JSC__JSGlobalObject* arg0); CPP_DECL bool JSC__JSGlobalObject__startRemoteInspector(JSC__JSGlobalObject* arg0, unsigned char* arg1, uint16_t arg2); CPP_DECL JSC__VM* JSC__JSGlobalObject__vm(JSC__JSGlobalObject* arg0); diff --git a/src/bun.js/bindings/headers.zig b/src/bun.js/bindings/headers.zig index 408c90212..27967eb13 100644 --- a/src/bun.js/bindings/headers.zig +++ b/src/bun.js/bindings/headers.zig @@ -137,10 +137,12 @@ pub extern fn JSC__JSPromise__rejectAsHandled(arg0: ?*bindings.JSPromise, arg1: pub extern fn JSC__JSPromise__rejectAsHandledException(arg0: ?*bindings.JSPromise, arg1: *bindings.JSGlobalObject, arg2: [*c]bindings.Exception) void; pub extern fn JSC__JSPromise__rejectedPromise(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue) ?*bindings.JSPromise; pub extern fn JSC__JSPromise__rejectedPromiseValue(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue) JSC__JSValue; +pub extern fn JSC__JSPromise__rejectOnNextTick(arg0: ?*bindings.JSPromise, arg1: *bindings.JSGlobalObject, JSValue2: JSC__JSValue) void; pub extern fn JSC__JSPromise__rejectWithCaughtException(arg0: ?*bindings.JSPromise, arg1: *bindings.JSGlobalObject, arg2: bJSC__ThrowScope) void; pub extern fn JSC__JSPromise__resolve(arg0: ?*bindings.JSPromise, arg1: *bindings.JSGlobalObject, JSValue2: JSC__JSValue) void; pub extern fn JSC__JSPromise__resolvedPromise(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue) ?*bindings.JSPromise; pub extern fn JSC__JSPromise__resolvedPromiseValue(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue) JSC__JSValue; +pub extern fn JSC__JSPromise__resolveOnNextTick(arg0: ?*bindings.JSPromise, arg1: *bindings.JSGlobalObject, JSValue2: JSC__JSValue) void; pub extern fn JSC__JSPromise__result(arg0: ?*bindings.JSPromise, arg1: *bindings.VM) JSC__JSValue; pub extern fn JSC__JSPromise__status(arg0: [*c]const JSC__JSPromise, arg1: *bindings.VM) u32; pub extern fn JSC__JSInternalPromise__create(arg0: *bindings.JSGlobalObject) [*c]bindings.JSInternalPromise; @@ -163,6 +165,7 @@ pub extern fn JSC__JSGlobalObject__generateHeapSnapshot(arg0: *bindings.JSGlobal pub extern fn JSC__JSGlobalObject__getCachedObject(arg0: *bindings.JSGlobalObject, arg1: [*c]const ZigString) JSC__JSValue; pub extern fn JSC__JSGlobalObject__handleRejectedPromises(arg0: *bindings.JSGlobalObject) void; pub extern fn JSC__JSGlobalObject__putCachedObject(arg0: *bindings.JSGlobalObject, arg1: [*c]const ZigString, JSValue2: JSC__JSValue) JSC__JSValue; +pub extern fn JSC__JSGlobalObject__queueMicrotaskJob(arg0: *bindings.JSGlobalObject, JSValue1: JSC__JSValue, JSValue2: JSC__JSValue, JSValue3: JSC__JSValue, JSValue4: JSC__JSValue) void; pub extern fn JSC__JSGlobalObject__reload(arg0: *bindings.JSGlobalObject) void; pub extern fn JSC__JSGlobalObject__startRemoteInspector(arg0: *bindings.JSGlobalObject, arg1: [*c]u8, arg2: u16) bool; pub extern fn JSC__JSGlobalObject__vm(arg0: *bindings.JSGlobalObject) *bindings.VM; diff --git a/src/bun.js/event_loop.zig b/src/bun.js/event_loop.zig index 6331480d8..825bd7b1b 100644 --- a/src/bun.js/event_loop.zig +++ b/src/bun.js/event_loop.zig @@ -83,10 +83,16 @@ pub fn ConcurrentPromiseTask(comptime Context: type) type { } pub fn IOTask(comptime Context: type) type { + return WorkTask(Context, false); +} + +pub fn WorkTask(comptime Context: type, comptime async_io: bool) type { return struct { + const TaskType = if (async_io) NetworkThread.Task else WorkPoolTask; + const This = @This(); ctx: *Context, - task: NetworkThread.Task = .{ .callback = &runFromThreadPool }, + task: TaskType = .{ .callback = &runFromThreadPool }, event_loop: *JSC.EventLoop, allocator: std.mem.Allocator, globalThis: *JSGlobalObject, @@ -108,7 +114,7 @@ pub fn IOTask(comptime Context: type) type { return this; } - pub fn runFromThreadPool(task: *NetworkThread.Task) void { + pub fn runFromThreadPool(task: *TaskType) void { var this = @fieldParentPtr(This, "task", task); Context.run(this.ctx, this); } @@ -121,8 +127,12 @@ pub fn IOTask(comptime Context: type) type { pub fn schedule(this: *This) void { this.ref.ref(this.event_loop.virtual_machine); - NetworkThread.init() catch return; - NetworkThread.global.schedule(NetworkThread.Batch.from(&this.task)); + if (comptime async_io) { + NetworkThread.init() catch return; + NetworkThread.global.schedule(NetworkThread.Batch.from(&this.task)); + } else { + WorkPool.schedule(&this.task); + } } pub fn onFinish(this: *This) void { @@ -175,6 +185,7 @@ const MicrotaskForDefaultGlobalObject = JSC.MicrotaskForDefaultGlobalObject; const HotReloadTask = JSC.HotReloader.HotReloadTask; const PollPendingModulesTask = JSC.ModuleLoader.AsyncModule.Queue; // const PromiseTask = JSInternalPromise.Completion.PromiseTask; +const GetAddrInfoRequestTask = JSC.DNS.GetAddrInfoRequest.Task; pub const Task = TaggedPointerUnion(.{ FetchTasklet, Microtask, @@ -189,6 +200,7 @@ pub const Task = TaggedPointerUnion(.{ CppTask, HotReloadTask, PollPendingModulesTask, + GetAddrInfoRequestTask, // PromiseTask, // TimeoutTasklet, }); @@ -416,6 +428,11 @@ pub const EventLoop = struct { @field(Task.Tag, typeBaseName(@typeName(PollPendingModulesTask))) => { this.virtual_machine.modules.onPoll(); }, + @field(Task.Tag, typeBaseName(@typeName(GetAddrInfoRequestTask))) => { + var any: *GetAddrInfoRequestTask = task.get(GetAddrInfoRequestTask).?; + any.runFromJS(); + any.deinit(); + }, else => if (Environment.allow_assert) { bun.Output.prettyln("\nUnexpected tag: {s}\n", .{@tagName(task.tag())}); } else unreachable, diff --git a/src/bun.js/fs_promises.exports.js b/src/bun.js/fs_promises.exports.js index 5ae756347..2ba18f3c6 100644 --- a/src/bun.js/fs_promises.exports.js +++ b/src/bun.js/fs_promises.exports.js @@ -8,7 +8,7 @@ function promisify(fsFunction) { // we can use new Function() here instead // based on fsFucntion.length var obj = { - [fsFunction.name]: function (resolve, reject, args) { + ["::bunternal::"]: function (resolve, reject, args) { var result; try { result = fsFunction.apply(fs, args); diff --git a/src/bun.js/node/syscall.zig b/src/bun.js/node/syscall.zig index 06b007ef1..035a8738f 100644 --- a/src/bun.js/node/syscall.zig +++ b/src/bun.js/node/syscall.zig @@ -105,6 +105,7 @@ pub const Tag = enum(u8) { kill, waitpid, posix_spawn, + getaddrinfo, pub var strings = std.EnumMap(Tag, JSC.C.JSStringRef).initFull(null); }; const PathString = @import("bun").PathString; diff --git a/src/bun.js/rare_data.zig b/src/bun.js/rare_data.zig index 4f760c0d6..a14b3282a 100644 --- a/src/bun.js/rare_data.zig +++ b/src/bun.js/rare_data.zig @@ -25,6 +25,8 @@ cleanup_hook: ?*CleanupHook = null, file_polls_: ?*JSC.FilePoll.HiveArray = null, +global_dns_data: ?*JSC.DNS.GlobalData = null, + pub fn filePolls(this: *RareData, vm: *JSC.VirtualMachine) *JSC.FilePoll.HiveArray { return this.file_polls_ orelse { this.file_polls_ = vm.allocator.create(JSC.FilePoll.HiveArray) catch unreachable; @@ -228,3 +230,11 @@ pub fn stdin(rare: *RareData) *Blob.Store { break :brk store; }; } + +pub fn globalDNSResolver(rare: *RareData, vm: *JSC.VirtualMachine) *JSC.DNS.DNSResolver { + if (rare.global_dns_data == null) { + rare.global_dns_data = JSC.DNS.GlobalData.init(vm.allocator, vm); + } + + return &rare.global_dns_data.?.resolver; +} |