mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
Fixes ENG-21287
Build times, from `bun run build && echo '//' >> src/main.zig && time
bun run build`
|Platform|0.14.1|0.15.2|Speedup|
|-|-|-|-|
|macos debug asan|126.90s|106.27s|1.19x|
|macos debug noasan|60.62s|50.85s|1.19x|
|linux debug asan|292.77s|241.45s|1.21x|
|linux debug noasan|146.58s|130.94s|1.12x|
|linux debug use_llvm=false|n/a|78.27s|1.87x|
|windows debug asan|177.13s|142.55s|1.24x|
Runtime performance:
- next build memory usage may have gone up by 5%. Otherwise seems the
same. Some code with writers may have gotten slower, especially one
instance of a counting writer and a few instances of unbuffered writers
that now have vtable overhead.
- File size reduced by 800kb (from 100.2mb to 99.4mb)
Improvements:
- `@export` hack is no longer needed for watch
- native x86_64 backend for linux builds faster. to use it, set use_llvm
false and no_link_obj false. also set `ASAN_OPTIONS=detect_leaks=0`
otherwise it will spam the output with tens of thousands of lines of
debug info errors. may need to use the zig lldb fork for debugging.
- zig test-obj, which we will be able to use for zig unit tests
Still an issue:
- false 'dependency loop' errors remain in watch mode
- watch mode crashes observed
Follow-up:
- [ ] search `comptime Writer: type` and `comptime W: type` and remove
- [ ] remove format_mode in our zig fork
- [ ] remove deprecated.zig autoFormatLabelFallback
- [ ] remove deprecated.zig autoFormatLabel
- [ ] remove deprecated.BufferedWriter and BufferedReader
- [ ] remove override_no_export_cpp_apis as it is no longer needed
- [ ] css Parser(W) -> Parser, and remove all the comptime writer: type
params
- [ ] remove deprecated writer fully
Files that add lines:
```
649 src/deprecated.zig
167 scripts/pack-codegen-for-zig-team.ts
54 scripts/cleartrace-impl.js
46 scripts/cleartrace.ts
43 src/windows.zig
18 src/fs.zig
17 src/bun.js/ConsoleObject.zig
16 src/output.zig
12 src/bun.js/test/debug.zig
12 src/bun.js/node/node_fs.zig
8 src/env_loader.zig
7 src/css/printer.zig
7 src/cli/init_command.zig
7 src/bun.js/node.zig
6 src/string/escapeRegExp.zig
6 src/install/PnpmMatcher.zig
5 src/bun.js/webcore/Blob.zig
4 src/crash_handler.zig
4 src/bun.zig
3 src/install/lockfile/bun.lock.zig
3 src/cli/update_interactive_command.zig
3 src/cli/pack_command.zig
3 build.zig
2 src/Progress.zig
2 src/install/lockfile/lockfile_json_stringify_for_debugging.zig
2 src/css/small_list.zig
2 src/bun.js/webcore/prompt.zig
1 test/internal/ban-words.test.ts
1 test/internal/ban-limits.json
1 src/watcher/WatcherTrace.zig
1 src/transpiler.zig
1 src/shell/builtin/cp.zig
1 src/js_printer.zig
1 src/io/PipeReader.zig
1 src/install/bin.zig
1 src/css/selectors/selector.zig
1 src/cli/run_command.zig
1 src/bun.js/RuntimeTranspilerStore.zig
1 src/bun.js/bindings/JSRef.zig
1 src/bake/DevServer.zig
```
Files that remove lines:
```
-1 src/test/recover.zig
-1 src/sql/postgres/SocketMonitor.zig
-1 src/sql/mysql/MySQLRequestQueue.zig
-1 src/sourcemap/CodeCoverage.zig
-1 src/css/values/color_js.zig
-1 src/compile_target.zig
-1 src/bundler/linker_context/convertStmtsForChunk.zig
-1 src/bundler/bundle_v2.zig
-1 src/bun.js/webcore/blob/read_file.zig
-1 src/ast/base.zig
-2 src/sql/postgres/protocol/ArrayList.zig
-2 src/shell/builtin/mkdir.zig
-2 src/install/PackageManager/patchPackage.zig
-2 src/install/PackageManager/PackageManagerDirectories.zig
-2 src/fmt.zig
-2 src/css/declaration.zig
-2 src/css/css_parser.zig
-2 src/collections/baby_list.zig
-2 src/bun.js/bindings/ZigStackFrame.zig
-2 src/ast/E.zig
-3 src/StandaloneModuleGraph.zig
-3 src/deps/picohttp.zig
-3 src/deps/libuv.zig
-3 src/btjs.zig
-4 src/threading/Futex.zig
-4 src/shell/builtin/touch.zig
-4 src/meta.zig
-4 src/install/lockfile.zig
-4 src/css/selectors/parser.zig
-5 src/shell/interpreter.zig
-5 src/css/error.zig
-5 src/bun.js/web_worker.zig
-5 src/bun.js.zig
-6 src/cli/test_command.zig
-6 src/bun.js/VirtualMachine.zig
-6 src/bun.js/uuid.zig
-6 src/bun.js/bindings/JSValue.zig
-9 src/bun.js/test/pretty_format.zig
-9 src/bun.js/api/BunObject.zig
-14 src/install/install_binding.zig
-14 src/fd.zig
-14 src/bun.js/node/path.zig
-14 scripts/pack-codegen-for-zig-team.sh
-17 src/bun.js/test/diff_format.zig
```
`git diff --numstat origin/main...HEAD | awk '{ print ($1-$2)"\t"$3 }' |
sort -rn`
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
Co-authored-by: Meghan Denny <meghan@bun.com>
Co-authored-by: tayor.fish <contact@taylor.fish>
472 lines
16 KiB
Zig
472 lines
16 KiB
Zig
pub const AI_V4MAPPED: c_int = if (bun.Environment.isWindows) 2048 else bun.c.AI_V4MAPPED;
|
|
pub const AI_ADDRCONFIG: c_int = if (bun.Environment.isWindows) 1024 else bun.c.AI_ADDRCONFIG;
|
|
pub const AI_ALL: c_int = if (bun.Environment.isWindows) 256 else bun.c.AI_ALL;
|
|
|
|
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)], 0);
|
|
|
|
hints.ai_family = this.options.family.toLibC();
|
|
hints.ai_socktype = this.options.socktype.toLibC();
|
|
hints.ai_protocol = this.options.protocol.toLibC();
|
|
hints.ai_flags = @bitCast(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(u64) {
|
|
family: Family = .unspecified,
|
|
/// Leaving this unset leads to many duplicate addresses returned.
|
|
/// Node hardcodes to `SOCK_STREAM`.
|
|
/// There don't seem to be any issues in Node's repo about this
|
|
/// So I think it's likely that nobody actually needs `SOCK_DGRAM` as a flag
|
|
/// https://github.com/nodejs/node/blob/2eff28fb7a93d3f672f80b582f664a7c701569fb/src/cares_wrap.cc#L1609
|
|
socktype: SocketType = .stream,
|
|
protocol: Protocol = .unspecified,
|
|
backend: Backend = Backend.default,
|
|
flags: std.c.AI = .{},
|
|
_: u24 = 0,
|
|
|
|
pub fn toLibC(this: Options) ?std.c.addrinfo {
|
|
if (this.family == .unspecified and this.socktype == .unspecified and this.protocol == .unspecified and this.flags == std.c.AI{}) {
|
|
return null;
|
|
}
|
|
|
|
var hints: std.c.addrinfo = undefined;
|
|
@memset(std.mem.asBytes(&hints)[0..@sizeOf(std.c.addrinfo)], 0);
|
|
|
|
hints.family = this.family.toLibC();
|
|
hints.socktype = this.socktype.toLibC();
|
|
hints.protocol = this.protocol.toLibC();
|
|
hints.flags = this.flags;
|
|
return hints;
|
|
}
|
|
|
|
const FromJSError = Family.FromJSError ||
|
|
SocketType.FromJSError ||
|
|
Protocol.FromJSError ||
|
|
Backend.FromJSError || error{
|
|
InvalidFlags,
|
|
InvalidOptions,
|
|
};
|
|
|
|
pub fn fromJS(value: jsc.JSValue, globalObject: *jsc.JSGlobalObject) FromJSError!Options {
|
|
if (value.isEmptyOrUndefinedOrNull())
|
|
return Options{};
|
|
|
|
if (value.isObject()) {
|
|
var options = Options{};
|
|
|
|
if (try value.get(globalObject, "family")) |family| {
|
|
options.family = try Family.fromJS(family, globalObject);
|
|
}
|
|
|
|
if (try value.get(globalObject, "socketType") orelse try value.get(globalObject, "socktype")) |socktype| {
|
|
options.socktype = try SocketType.fromJS(socktype, globalObject);
|
|
}
|
|
|
|
if (try value.get(globalObject, "protocol")) |protocol| {
|
|
options.protocol = try Protocol.fromJS(protocol, globalObject);
|
|
}
|
|
|
|
if (try value.get(globalObject, "backend")) |backend| {
|
|
options.backend = try Backend.fromJS(backend, globalObject);
|
|
}
|
|
|
|
if (try value.get(globalObject, "flags")) |flags| {
|
|
if (!flags.isNumber())
|
|
return error.InvalidFlags;
|
|
|
|
options.flags = try flags.coerce(std.c.AI, globalObject);
|
|
|
|
// hints & ~(AI_ADDRCONFIG | AI_ALL | AI_V4MAPPED)) !== 0
|
|
const filter = ~@as(u32, @bitCast(std.c.AI{ .ALL = true, .ADDRCONFIG = true, .V4MAPPED = true }));
|
|
const int = @as(u32, @bitCast(options.flags));
|
|
if (int & filter != 0) return error.InvalidFlags;
|
|
}
|
|
|
|
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 },
|
|
});
|
|
|
|
const FromJSError = JSError || error{
|
|
InvalidFamily,
|
|
};
|
|
|
|
pub fn fromJS(value: jsc.JSValue, globalObject: *jsc.JSGlobalObject) FromJSError!Family {
|
|
if (value.isEmptyOrUndefinedOrNull())
|
|
return .unspecified;
|
|
|
|
if (value.isNumber()) {
|
|
return switch (try value.coerce(i32, globalObject)) {
|
|
0 => .unspecified,
|
|
4 => .inet,
|
|
6 => .inet6,
|
|
else => return error.InvalidFamily,
|
|
};
|
|
}
|
|
|
|
if (value.isString()) {
|
|
return try map.fromJS(globalObject, value) orelse {
|
|
if ((try value.toJSString(globalObject)).length() == 0) {
|
|
return .unspecified;
|
|
}
|
|
|
|
return error.InvalidFamily;
|
|
};
|
|
}
|
|
|
|
return error.InvalidFamily;
|
|
}
|
|
|
|
pub fn toLibC(this: Family) i32 {
|
|
return switch (this) {
|
|
.unspecified => 0,
|
|
.inet => std.posix.AF.INET,
|
|
.inet6 => std.posix.AF.INET6,
|
|
.unix => std.posix.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.posix.SOCK.STREAM,
|
|
.dgram => return std.posix.SOCK.DGRAM,
|
|
}
|
|
}
|
|
|
|
const FromJSError = JSError || error{
|
|
InvalidSocketType,
|
|
};
|
|
|
|
pub fn fromJS(value: jsc.JSValue, globalObject: *jsc.JSGlobalObject) FromJSError!SocketType {
|
|
if (value.isEmptyOrUndefinedOrNull())
|
|
// Default to .stream
|
|
return .stream;
|
|
|
|
if (value.isNumber()) {
|
|
return switch (value.to(i32)) {
|
|
0 => .unspecified,
|
|
1 => .stream,
|
|
2 => .dgram,
|
|
else => return error.InvalidSocketType,
|
|
};
|
|
}
|
|
|
|
if (value.isString()) {
|
|
return try map.fromJS(globalObject, value) orelse {
|
|
if ((try value.toJSString(globalObject)).length() == 0)
|
|
return .unspecified;
|
|
|
|
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 },
|
|
});
|
|
|
|
const FromJSError = JSError || error{
|
|
InvalidProtocol,
|
|
};
|
|
|
|
pub fn fromJS(value: jsc.JSValue, globalObject: *jsc.JSGlobalObject) FromJSError!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()) {
|
|
return try map.fromJS(globalObject, value) orelse {
|
|
const str = try value.toJSString(globalObject);
|
|
if (str.length() == 0)
|
|
return .unspecified;
|
|
|
|
return error.InvalidProtocol;
|
|
};
|
|
}
|
|
|
|
return error.InvalidProtocol;
|
|
}
|
|
|
|
pub fn toLibC(this: Protocol) i32 {
|
|
switch (this) {
|
|
.unspecified => return 0,
|
|
.tcp => return std.posix.IPPROTO.TCP,
|
|
.udp => return std.posix.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 = switch (bun.Environment.os) {
|
|
.mac, .windows => .system,
|
|
else => .c_ares,
|
|
};
|
|
|
|
pub const FromJSError = JSError || error{
|
|
InvalidBackend,
|
|
};
|
|
|
|
pub fn fromJS(value: jsc.JSValue, globalObject: *jsc.JSGlobalObject) FromJSError!Backend {
|
|
if (value.isEmptyOrUndefinedOrNull())
|
|
return default;
|
|
|
|
if (value.isString()) {
|
|
return try label.fromJS(globalObject, value) orelse {
|
|
if ((try value.toJSString(globalObject)).length() == 0) {
|
|
return default;
|
|
}
|
|
|
|
return error.InvalidBackend;
|
|
};
|
|
}
|
|
|
|
return error.InvalidBackend;
|
|
}
|
|
};
|
|
|
|
pub const Result = struct {
|
|
address: std.net.Address,
|
|
ttl: i32 = 0,
|
|
|
|
pub const List = std.array_list.Managed(Result);
|
|
|
|
pub const Any = union(enum) {
|
|
addrinfo: ?*std.c.addrinfo,
|
|
list: List,
|
|
|
|
pub fn toJS(this: *const Any, globalThis: *jsc.JSGlobalObject) bun.JSError!?jsc.JSValue {
|
|
return switch (this.*) {
|
|
.addrinfo => |addrinfo| try addrInfoToJSArray(addrinfo orelse return null, globalThis),
|
|
.list => |list| brk: {
|
|
const array = try jsc.JSValue.createEmptyArray(globalThis, @as(u32, @truncate(list.items.len)));
|
|
var i: u32 = 0;
|
|
const items: []const Result = list.items;
|
|
for (items) |item| {
|
|
try array.putIndex(globalThis, i, try item.toJS(globalThis));
|
|
i += 1;
|
|
}
|
|
break :brk array;
|
|
},
|
|
};
|
|
}
|
|
|
|
pub fn deinit(this: *const Any) void {
|
|
switch (this.*) {
|
|
.addrinfo => |addrinfo| {
|
|
if (addrinfo) |a| {
|
|
std.c.freeaddrinfo(a);
|
|
}
|
|
},
|
|
.list => |list_| {
|
|
var list = list_;
|
|
list.clearAndFree();
|
|
},
|
|
}
|
|
}
|
|
};
|
|
|
|
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(addrinfo.addr orelse return null)),
|
|
// no TTL in POSIX getaddrinfo()
|
|
.ttl = 0,
|
|
};
|
|
}
|
|
|
|
pub fn toJS(this: *const Result, globalThis: *jsc.JSGlobalObject) bun.JSError!JSValue {
|
|
const obj = jsc.JSValue.createEmptyObject(globalThis, 3);
|
|
obj.put(globalThis, jsc.ZigString.static("address"), try addressToJS(&this.address, globalThis));
|
|
obj.put(globalThis, jsc.ZigString.static("family"), switch (this.address.any.family) {
|
|
std.posix.AF.INET => JSValue.jsNumber(4),
|
|
std.posix.AF.INET6 => JSValue.jsNumber(6),
|
|
else => JSValue.jsNumber(0),
|
|
});
|
|
obj.put(globalThis, jsc.ZigString.static("ttl"), JSValue.jsNumber(this.ttl));
|
|
return obj;
|
|
}
|
|
};
|
|
};
|
|
pub fn addressToString(address: *const std.net.Address) bun.OOM!bun.String {
|
|
switch (address.any.family) {
|
|
std.posix.AF.INET => {
|
|
var self = address.in;
|
|
const bytes = @as(*const [4]u8, @ptrCast(&self.sa.addr));
|
|
return String.createFormat("{}.{}.{}.{}", .{
|
|
bytes[0],
|
|
bytes[1],
|
|
bytes[2],
|
|
bytes[3],
|
|
});
|
|
},
|
|
std.posix.AF.INET6 => {
|
|
var stack = std.heap.stackFallback(512, default_allocator);
|
|
const allocator = stack.get();
|
|
var out = try std.fmt.allocPrint(allocator, "{f}", .{address.*});
|
|
defer allocator.free(out);
|
|
// TODO: this is a hack, fix it
|
|
// This removes [.*]:port
|
|
// ^ ^^^^^^
|
|
return String.cloneLatin1(out[1 .. out.len - 1 - std.fmt.count("{d}", .{address.in6.getPort()}) - 1]);
|
|
},
|
|
std.posix.AF.UNIX => {
|
|
if (comptime std.net.has_unix_sockets) {
|
|
return String.cloneLatin1(&address.un.path);
|
|
}
|
|
|
|
return String.empty;
|
|
},
|
|
else => return String.empty,
|
|
}
|
|
}
|
|
|
|
pub fn addressToJS(address: *const std.net.Address, globalThis: *jsc.JSGlobalObject) bun.JSError!jsc.JSValue {
|
|
var str = addressToString(address) catch return globalThis.throwOutOfMemory();
|
|
return str.transferToJS(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 += @intFromBool(current.?.addr != null);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
pub fn addrInfoToJSArray(addr_info: *std.c.addrinfo, globalThis: *jsc.JSGlobalObject) bun.JSError!jsc.JSValue {
|
|
const array = try jsc.JSValue.createEmptyArray(
|
|
globalThis,
|
|
addrInfoCount(addr_info),
|
|
);
|
|
|
|
{
|
|
var j: u32 = 0;
|
|
var current: ?*std.c.addrinfo = addr_info;
|
|
while (current) |this_node| : (current = current.?.next) {
|
|
try array.putIndex(
|
|
globalThis,
|
|
j,
|
|
try GetAddrInfo.Result.toJS(
|
|
&(GetAddrInfo.Result.fromAddrInfo(this_node) orelse continue),
|
|
globalThis,
|
|
),
|
|
);
|
|
j += 1;
|
|
}
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
pub const internal = bun.api.dns.internal;
|
|
|
|
const std = @import("std");
|
|
|
|
const bun = @import("bun");
|
|
const JSError = bun.JSError;
|
|
const String = bun.String;
|
|
const default_allocator = bun.default_allocator;
|
|
|
|
const jsc = bun.jsc;
|
|
const JSValue = jsc.JSValue;
|