mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 10:58:56 +00:00
* alright now just gotta try running it
* fix a gajillion compiler errors
* even more code
* okay i fixed more errors
* wip
* Update launch.json
* Update string_builder.zig
* `fast_debug_build_mode` makes debug build 2x faster
* Update bundle_v2.zig
* more code!
* It bundles!
* Rename `Bun.Transpiler` to `Bun.Bundler`
* `import()` expressions almost work
* wip attempt to get import() expr to work
* Bundle namespace imports
* Attempt to fix the issue with import() unsuccessfully
* consider current working directory when resolving relative paths (#2313)
* consider current working directory when resolving relative paths
fixes #2298
* comment test
---------
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
* support `expect().toThrow(/pattern/)` (#2314)
- fix time-zone-dependent test failure
* fix missing `Blob` error messages on Linux (#2315)
* fix & clean up tests (#2318)
- skip flaky tests when running as `root`
- use `expect().toThrow()`
- clean up temporary files after tests
* feat(tty): add some `tty.WriteStream` methods to `process.{stdout, stderr}` (#2320)
* feat(stdio): add some `tty.WriteStream` methods
* chore(builtins): add process builtin gen'd code
* Fix docker install command
* `bun test` on macOS in GitHub Actions (#2322)
* Fixes #2323
* throw invalid parameter errors in `crypto.scryptSync` (#2331)
* throw invalid parameter errors
* remove comptime, add empty buffer function
* remove error_name comptime
* Add reference documentation for bun:test (#2327)
* Reorganize tests (#2332)
* Fix html-rewriter.test.js
* fix the wrong thing being incremented in hmr example (#2334)
* Add more test harness
* Improve Benchmarking page, small fixes (#2339)
* Improve benchmarking page
* WIP
* Add typescript instructions to hot
* Document preload in Plugins. Fix loader in plugin types.
* Fix typo
* Fix links
* run prettier
* Document openInEditor
* improve `Buffer` compatibility with Node.js (#2341)
* improve `Buffer` compatibility with Node.js
* use `memmove()`
allow `encoding` to be `undefined`
* run `bun test` after macOS builds (#2343)
* "binary" is an alias of "latin1"
Fixes https://github.com/oven-sh/bun/issues/2110
* More spec compliant `Blob.prototype.type` (#2340)
* Make `Blob.prototype. type` more spec compliant
* Add a few more checks for isNumber()
* Fix `make headers`
* Safer JSValue.isString()
* More tests for blob.slice
* Make `Blob.prototype.type` more spec compliant
* Add isASCII check
* Fix types
* Fix failing type test
* Update blob.zig
* Update blob.zig
* Fix .eql check on empty values
---------
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
* Fix bug in test runner
* Support `import()` expressions
* Implement `require()`
* clean up bit_set.zig slightly
* Move some things around
* misc cleanup
* Cleanup some things
* Fix a lot of stuff
* Fix `module.exports.fn = fn;` in ESM entry point
* Fix crash due when printing file
* Fix issue with class names
* Fix issue with `export default identifier`
* Update js_parser.zig
* optimization: inline single-property object acceses and arrays
* Fix undefined memory in renamed symbols list
* Handle call target
* wip
* Inline it
* Fix undefined memory issue when reclaiming blocks in ast
* Halt linking on any parse errors
* alias
* Rename `enable_bundling` to `enable_legacy_bundling`
* Workaround anonymous struct literal zig bug
* Use slower approach (without bitset) because it doesn't break after 8 symbols
* Fix incorrectly-renaming statically defined symbols
* Handle more edgecases in our bit_set fork
* Reduce number of allocations for `define`
* Do not rename unbound symbols
* Clean up dot defines a little more
* Make the generated names prettier
* Workaround runtime symbol missing issue
* Fail the build on errors
* Support export * from
* Support `--outfile`
* partially fix renaming
* fanicer symbol renaming impl
* misc, extremely revertible cleanup
* Fix up some bugs with symbol renaming
* formatting
* Update launch.json
* Parse `__PURE__` comments
* clean up simd code for pure comments
* changes to merge
* workaround runtime issue
* Fix issue with `export * as` not propagating correctly
* Make all top-level declarations `var` when bundling
* Fix missing prefix
* Fix assigning to stack copy
* Fix missing runtime symbol
* Fix bug with namespace exports
* Dramatically reduce allocations
* Update launch.json
* Add missing flags
* Update js_parser.zig
* small cleanup
* Make the export name better
* Fix unnecessary `var foo = foo`
* Implement CommonJS -> ESM conversion
* Implement module redirects
* Port esbuild bundler tests for new bundler (#2380)
* started porting esbuild tests
* clean up test names and api before moving on
* port tests using a program i wrote
* replace todo generated comment
* fix generated tests not including some files
* work on tests
* [github web editor] add define, external, inject, minifySyntax, minifyWhitespace options.
* get most of the todo comments out of the way, but expectBundled does not handle most of the cases
* continue working on esbuild tests
* use test.skip for unsupported tests
* Fixups for test runner
* Hoist imports & exports
* Fix test
* Hoist classes
* bundler test refining, 51/835
* Fix runtime require
* bundler test refining, 81/835
* bundler test refining, 93/835
* Make the test work in any timezone
* feat(expect): update toBeInstanceOf (#2396)
* feat: update instanceof binding
* fix: according to PR comments
* Rename `expectObjectTypeCount` to `expectMaxObjectTypeCount`
* Fix socket tests with connection errors (#2403)
* release pending activity with connection error handler
* unref poll_ref
* remove trailing comma
* Organize Dockerfiles for official status
* Remove test Dockerfile
* Remove old Docker workflow
* Feat(test): add toMatch (#2404)
* Fix various fetch/response/request tests (#2416)
* fix most fetch tests, skip a few
* fastGet, toValueGC, and invalid init
* bigint unreachable, range error, log process as process
* remove extra fetch_headers
* remove js_type parameter, check isObject()
* throw invalid mime type error, use enum literal
* switch back to promise rejection
* RangeError pascal case
* Fix several bugs (#2418)
* utf16 codepoint with replacement character
* Fix test failure with `TextEncoder("ascii')`
* Add missing type
* Fix Response.prototype.bodyUsed and Request.prototype.bodyUsed
* Fix bug with scrypt error not clearing
* Update server.zig
* oopsie
* 💅
* docs: Use correct url in the 'Issues' link in README header (#2420)
* Fix crash when rendering error page and the server or network is slow
* [fetch] Make the default body value `null` when unspecified
This is better aligned with the fetch spec
* Make node-net tests less flaky
* [node:net] Fix issue with `listen` callback firing before it's listening
* Always clear timers in node test harness
* Fix out of bounds access
Repro'd in Buffer tests
* Update UWS
cc @cirospaciari
* Make this test more thorough
* Hanging abort test
* 0 length body is a null stream
* Several bug fixes (#2427)
* Fix test
* Fix segfault when unexpected type is passed in `expect().toThrow`
* Fix issues with request constructor
* Don't bother cloning headers when its empty
* woops
* more tests
* fix incorrect test
* Make the fetch error messages better
* Update response.zig
* Fix test that failed on macOS
* Fix test
* Remove extra hash table lookups
* Support running dummy registry directly
cc @alexlamsl
* Update test
* Update test
* fixup
* Workaround crash in test runner
* Fixup test
* Fixup test
* Update os.test.js
---------
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
* Remove usages of port numbers in tests
* Set -O2 and -fno-rtti
* Remove -g
* Prevent undefined memory access
* [bun test] Implement `--rerun-each` flag to run each test N times
* Reduce number of module scopes created
* add some extra abort checks into streams (#2430)
* add some checks to avoid UAF
* avoid multiple calls to finalize if endFromJS is called more than once
* fix no-op comment
* mark as requested_end on abort
* remove requested_end from abort
* remove unnecessary check (#2432)
* Fix bug with scoped aliased dependencies in bun install on macOS
* remove `addLog`, remove `--prominent-compile-errors`
* Finish the upgrade
* Optional chaining flag
* Implement same_target_becomes_destructuring optimization
* bundler test refining, 109/835
* Reset bindings
* Support multiple entry points
* Implement `--entry-names` flag
* Use a tempdir with a better name
* prettier
* Log file name
* Update js_parser.zig
* Mark all bun builtins as external
* Make resolve errors actually errors
* Update bundler_default.test.ts
* Fix `await import(foo)`
* WIP react server components
* Do more stuff at runtime
* ✂️
* Support automatic JSX imports
* Use a module cache for now
* Update tsconfig.base.json
* Fix ThisOutsideFunctionNotRenamed
* woopsie
* moar cpu
* clamp it
* fixup
* Add a bunch of assertions
* Bun uses automatic runtime by default
* Parse Import Attributes
* Add a note about Valgrind
* Update developing.md
* Fix up code splitting for React Server Components
* Implement client component manifest
* Fix crash with --react-server-components and no client components
* Backport 4d31e3c917
* Update launch.json
* Fix for latest zig
* Workaround bug with ?[]const string
Occasionally saw alignment errors in this code
Workaround https://github.com/ziglang/zig/issues/15085
related: https://github.com/ziglang/zig/pull/15089
* switch to regular slice
* Avoid initializing named_imports and named_exports as undefined
* Reduce usages of `undefined`
* Add more assertions
* --watch wip
* Update javascript.zig
* Possibly fix the race condition
* Faster `do`
* bump allocator
* Reduce the size of `Symbol` slightly
* Alphabetically sort runtime import symbols, for determinism
* Prepare for code splitting
* handle overlapping stdout
* pure
* clean up some things
* Fix bug with `$$typeof`
* Address CommonJS -> ESM hoisting bug
* Support `"use server"` in manifest
* Implement `"use server"`
* Fix importing bun builtins when bundling
* Make `commonjs_to_esm` a feature flag, fix some splitting bugs
* ✂️
* fixme remove this
* Fix crash in longestCommonPath
* Chunking! Just need to do import paths now.
* Import paths work...now trying to figure out how to make runtime symbols work
* add workaround
* Replace `bun bun` with `bun build`
* Fix crash with dual package hazard
* Fix many CommonJS <> ESM interop bugs
* Support package.json `"sideEffects"`
also skip loading unnecessary package.json data in `bun run`
* add a not good --watch implementation
* bundler test refining, 140/831
* remove accidentally committed file
* do not return status code 1 on successful bundles
* bundler test refining, 159/830
* pass exit code to exitOrWatch
* clean up help menu
-remove two spaces to line up bun build
-moved all <r> tags to the end of the text they are colorizing
-moved other colors to the start of the text they colorize
-removed unneeded <r> tags, keeping only one at the start of the block
* importstar is fully ported
* wip
* you can run code in this branch now
* Disable this transform
* organize and document bundler tests
* Fix double import
* Fix sloppy mode function declarations
* Disable our CommonJS transform for now
* add `assertNotPresent` to make splitting cases easier
* Bump!
* Update bun.d.ts
* use import.meta.require in runtime code
* Disable this again
* Fix dirname
* Fix ESM -> CJS wrapper
* 💅
---------
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Co-authored-by: Alex Lam S.L <alexlamsl@gmail.com>
Co-authored-by: Derrick Farris <mr.dcfarris@gmail.com>
Co-authored-by: Ashcon Partovi <ashcon@partovi.net>
Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com>
Co-authored-by: pfg <pfg@pfg.pw>
Co-authored-by: Colin McDonnell <colinmcd94@gmail.com>
Co-authored-by: dave caruso <me@paperdave.net>
Co-authored-by: zhiyuan <32867472+zhiyuang@users.noreply.github.com>
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
Co-authored-by: Kamil Ogórek <kamil.ogorek@gmail.com>
Co-authored-by: Ciro Spaciari <ciro.spaciari@gmail.com>
1230 lines
41 KiB
Zig
1230 lines
41 KiB
Zig
const std = @import("std");
|
|
pub const Environment = @import("env.zig");
|
|
|
|
pub const use_mimalloc = !Environment.isTest;
|
|
|
|
pub const default_allocator: std.mem.Allocator = if (!use_mimalloc)
|
|
std.heap.c_allocator
|
|
else
|
|
@import("./memory_allocator.zig").c_allocator;
|
|
|
|
pub const huge_allocator: std.mem.Allocator = if (!use_mimalloc)
|
|
std.heap.c_allocator
|
|
else
|
|
@import("./memory_allocator.zig").huge_allocator;
|
|
|
|
pub const auto_allocator: std.mem.Allocator = if (!use_mimalloc)
|
|
std.heap.c_allocator
|
|
else
|
|
@import("./memory_allocator.zig").auto_allocator;
|
|
|
|
pub const huge_allocator_threshold: comptime_int = @import("./memory_allocator.zig").huge_threshold;
|
|
|
|
pub const C = @import("c.zig");
|
|
|
|
pub const FeatureFlags = @import("feature_flags.zig");
|
|
pub const meta = @import("./meta.zig");
|
|
pub const ComptimeStringMap = @import("./comptime_string_map.zig").ComptimeStringMap;
|
|
pub const base64 = @import("./base64/base64.zig");
|
|
pub const path = @import("./resolver/resolve_path.zig");
|
|
pub const fmt = struct {
|
|
pub usingnamespace std.fmt;
|
|
|
|
pub fn formatIp(address: std.net.Address, into: []u8) ![]u8 {
|
|
// std.net.Address.format includes `:<port>` and square brackets (IPv6)
|
|
// while Node does neither. This uses format then strips these to bring
|
|
// the result into conformance with Node.
|
|
var result = try std.fmt.bufPrint(into, "{}", .{address});
|
|
|
|
// Strip `:<port>`
|
|
if (std.mem.lastIndexOfScalar(u8, result, ':')) |colon| {
|
|
result = result[0..colon];
|
|
}
|
|
// Strip brackets
|
|
if (result[0] == '[' and result[result.len - 1] == ']') {
|
|
result = result[1 .. result.len - 1];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// https://lemire.me/blog/2021/06/03/computing-the-number-of-digits-of-an-integer-even-faster/
|
|
pub fn fastDigitCount(x: u64) u64 {
|
|
const table = [_]u64{
|
|
4294967296,
|
|
8589934582,
|
|
8589934582,
|
|
8589934582,
|
|
12884901788,
|
|
12884901788,
|
|
12884901788,
|
|
17179868184,
|
|
17179868184,
|
|
17179868184,
|
|
21474826480,
|
|
21474826480,
|
|
21474826480,
|
|
21474826480,
|
|
25769703776,
|
|
25769703776,
|
|
25769703776,
|
|
30063771072,
|
|
30063771072,
|
|
30063771072,
|
|
34349738368,
|
|
34349738368,
|
|
34349738368,
|
|
34349738368,
|
|
38554705664,
|
|
38554705664,
|
|
38554705664,
|
|
41949672960,
|
|
41949672960,
|
|
41949672960,
|
|
42949672960,
|
|
42949672960,
|
|
};
|
|
return x + table[std.math.log2(x)] >> 32;
|
|
}
|
|
|
|
pub const SizeFormatter = struct {
|
|
value: usize = 0,
|
|
pub fn format(self: SizeFormatter, comptime _: []const u8, opts: fmt.FormatOptions, writer: anytype) !void {
|
|
const math = std.math;
|
|
const value = self.value;
|
|
if (value == 0) {
|
|
return writer.writeAll("0 KB");
|
|
}
|
|
|
|
if (value < 512) {
|
|
try fmt.formatInt(self.value, 10, .lower, opts, writer);
|
|
return writer.writeAll(" bytes");
|
|
}
|
|
|
|
const mags_si = " KMGTPEZY";
|
|
const mags_iec = " KMGTPEZY";
|
|
|
|
const log2 = math.log2(value);
|
|
const magnitude = math.min(log2 / comptime math.log2(1000), mags_si.len - 1);
|
|
const new_value = math.lossyCast(f64, value) / math.pow(f64, 1000, math.lossyCast(f64, magnitude));
|
|
const suffix = switch (1000) {
|
|
1000 => mags_si[magnitude],
|
|
1024 => mags_iec[magnitude],
|
|
else => unreachable,
|
|
};
|
|
|
|
if (suffix == ' ') {
|
|
try fmt.formatFloatDecimal(new_value / 1000.0, .{ .precision = 2 }, writer);
|
|
return writer.writeAll(" KB");
|
|
} else {
|
|
try fmt.formatFloatDecimal(new_value, .{ .precision = if (std.math.approxEqAbs(f64, new_value, @trunc(new_value), 0.100)) @as(usize, 0) else @as(usize, 2) }, writer);
|
|
}
|
|
|
|
const buf = switch (1000) {
|
|
1000 => &[_]u8{ ' ', suffix, 'B' },
|
|
1024 => &[_]u8{ ' ', suffix, 'i', 'B' },
|
|
else => unreachable,
|
|
};
|
|
return writer.writeAll(buf);
|
|
}
|
|
};
|
|
|
|
pub fn size(value: anytype) SizeFormatter {
|
|
return switch (@TypeOf(value)) {
|
|
f64, f32, f128 => SizeFormatter{
|
|
.value = @floatToInt(u64, value),
|
|
},
|
|
else => SizeFormatter{ .value = @intCast(u64, value) },
|
|
};
|
|
}
|
|
|
|
const lower_hex_table = [_]u8{
|
|
'0',
|
|
'1',
|
|
'2',
|
|
'3',
|
|
'4',
|
|
'5',
|
|
'6',
|
|
'7',
|
|
'8',
|
|
'9',
|
|
'a',
|
|
'b',
|
|
'c',
|
|
'd',
|
|
'e',
|
|
'f',
|
|
};
|
|
const upper_hex_table = [_]u8{
|
|
'0',
|
|
'1',
|
|
'2',
|
|
'3',
|
|
'4',
|
|
'5',
|
|
'6',
|
|
'7',
|
|
'8',
|
|
'9',
|
|
'A',
|
|
'B',
|
|
'C',
|
|
'D',
|
|
'E',
|
|
'F',
|
|
};
|
|
pub fn HexIntFormatter(comptime Int: type, comptime lower: bool) type {
|
|
return struct {
|
|
value: Int,
|
|
|
|
const table = if (lower) lower_hex_table else upper_hex_table;
|
|
|
|
const BufType = [@bitSizeOf(Int) / 4]u8;
|
|
|
|
fn getOutBuf(value: Int) BufType {
|
|
var buf: BufType = undefined;
|
|
comptime var i: usize = 0;
|
|
inline while (i < buf.len) : (i += 1) {
|
|
// value relative to the current nibble
|
|
buf[i] = table[@as(u8, @truncate(u4, value >> comptime ((buf.len - i - 1) * 4))) & 0xF];
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
pub fn format(self: @This(), comptime _: []const u8, _: fmt.FormatOptions, writer: anytype) !void {
|
|
const value = self.value;
|
|
try writer.writeAll(&getOutBuf(value));
|
|
}
|
|
};
|
|
}
|
|
|
|
pub fn HexInt(comptime Int: type, comptime lower: std.fmt.Case, value: Int) HexIntFormatter(Int, lower == .lower) {
|
|
const Formatter = HexIntFormatter(Int, lower == .lower);
|
|
return Formatter{ .value = value };
|
|
}
|
|
|
|
pub fn hexIntLower(value: anytype) HexIntFormatter(@TypeOf(value), true) {
|
|
const Formatter = HexIntFormatter(@TypeOf(value), true);
|
|
return Formatter{ .value = value };
|
|
}
|
|
|
|
pub fn hexIntUpper(value: anytype) HexIntFormatter(@TypeOf(value), false) {
|
|
const Formatter = HexIntFormatter(@TypeOf(value), false);
|
|
return Formatter{ .value = value };
|
|
}
|
|
};
|
|
|
|
pub const Output = @import("./output.zig");
|
|
pub const Global = @import("./__global.zig");
|
|
|
|
pub const FileDescriptor = if (Environment.isBrowser) u0 else std.os.fd_t;
|
|
|
|
// When we are on a computer with an absurdly high number of max open file handles
|
|
// such is often the case with macOS
|
|
// As a useful optimization, we can store file descriptors and just keep them open...forever
|
|
pub const StoredFileDescriptorType = if (Environment.isWindows or Environment.isBrowser) u0 else std.os.fd_t;
|
|
|
|
pub const StringTypes = @import("string_types.zig");
|
|
pub const stringZ = StringTypes.stringZ;
|
|
pub const string = StringTypes.string;
|
|
pub const CodePoint = StringTypes.CodePoint;
|
|
pub const PathString = StringTypes.PathString;
|
|
pub const HashedString = StringTypes.HashedString;
|
|
pub const strings = @import("string_immutable.zig");
|
|
pub const MutableString = @import("string_mutable.zig").MutableString;
|
|
pub const RefCount = @import("./ref_count.zig").RefCount;
|
|
|
|
pub inline fn constStrToU8(s: []const u8) []u8 {
|
|
return @intToPtr([*]u8, @ptrToInt(s.ptr))[0..s.len];
|
|
}
|
|
|
|
pub const MAX_PATH_BYTES: usize = if (Environment.isWasm) 1024 else std.fs.MAX_PATH_BYTES;
|
|
|
|
pub inline fn cast(comptime To: type, value: anytype) To {
|
|
if (comptime std.meta.trait.isIntegral(@TypeOf(value))) {
|
|
return @intToPtr(To, @bitCast(usize, value));
|
|
}
|
|
|
|
// TODO: file issue about why std.meta.Child only is necessary on Linux aarch64
|
|
// it should be necessary on all targets
|
|
return @ptrCast(To, @alignCast(@alignOf(std.meta.Child(To)), value));
|
|
}
|
|
|
|
extern fn strlen(ptr: [*c]const u8) usize;
|
|
pub fn indexOfSentinel(comptime Elem: type, comptime sentinel: Elem, ptr: [*:sentinel]const Elem) usize {
|
|
if (comptime Elem == u8 and sentinel == 0) {
|
|
return strlen(ptr);
|
|
} else {
|
|
var i: usize = 0;
|
|
while (ptr[i] != sentinel) {
|
|
i += 1;
|
|
}
|
|
return i;
|
|
}
|
|
}
|
|
|
|
pub fn len(value: anytype) usize {
|
|
return switch (@typeInfo(@TypeOf(value))) {
|
|
.Array => |info| info.len,
|
|
.Vector => |info| info.len,
|
|
.Pointer => |info| switch (info.size) {
|
|
.One => switch (@as(@import("builtin").TypeInfo, @typeInfo(info.child))) {
|
|
.Array => |array| brk: {
|
|
if (array.sentinel != null) {
|
|
@compileError("use bun.sliceTo");
|
|
}
|
|
|
|
break :brk array.len;
|
|
},
|
|
else => @compileError("invalid type given to std.mem.len"),
|
|
},
|
|
.Many => {
|
|
const sentinel_ptr = info.sentinel orelse
|
|
@compileError("length of pointer with no sentinel");
|
|
const sentinel = @ptrCast(*align(1) const info.child, sentinel_ptr).*;
|
|
|
|
return indexOfSentinel(info.child, sentinel, value);
|
|
},
|
|
.C => {
|
|
std.debug.assert(value != null);
|
|
return indexOfSentinel(info.child, 0, value);
|
|
},
|
|
.Slice => value.len,
|
|
},
|
|
.Struct => |info| if (info.is_tuple) {
|
|
return info.fields.len;
|
|
} else @compileError("invalid type given to std.mem.len"),
|
|
else => @compileError("invalid type given to std.mem.len"),
|
|
};
|
|
}
|
|
|
|
fn Span(comptime T: type) type {
|
|
switch (@typeInfo(T)) {
|
|
.Optional => |optional_info| {
|
|
return ?Span(optional_info.child);
|
|
},
|
|
.Pointer => |ptr_info| {
|
|
var new_ptr_info = ptr_info;
|
|
switch (ptr_info.size) {
|
|
.One => switch (@typeInfo(ptr_info.child)) {
|
|
.Array => |info| {
|
|
new_ptr_info.child = info.child;
|
|
new_ptr_info.sentinel = info.sentinel;
|
|
},
|
|
else => @compileError("invalid type given to std.mem.Span"),
|
|
},
|
|
.C => {
|
|
new_ptr_info.sentinel = &@as(ptr_info.child, 0);
|
|
new_ptr_info.is_allowzero = false;
|
|
},
|
|
.Many, .Slice => {},
|
|
}
|
|
new_ptr_info.size = .Slice;
|
|
return @Type(.{ .Pointer = new_ptr_info });
|
|
},
|
|
else => @compileError("invalid type given to std.mem.Span"),
|
|
}
|
|
}
|
|
// fn Span(comptime T: type) type {
|
|
// switch (@typeInfo(T)) {
|
|
// .Optional => |optional_info| {
|
|
// return ?Span(optional_info.child);
|
|
// },
|
|
// .Pointer => |ptr_info| {
|
|
// var new_ptr_info = ptr_info;
|
|
// switch (ptr_info.size) {
|
|
// .C => {
|
|
// new_ptr_info.sentinel = &@as(ptr_info.child, 0);
|
|
// new_ptr_info.is_allowzero = false;
|
|
// },
|
|
// .Many => if (ptr_info.sentinel == null) @compileError("invalid type given to bun.span: " ++ @typeName(T)),
|
|
// else => {},
|
|
// }
|
|
// new_ptr_info.size = .Slice;
|
|
// return @Type(.{ .Pointer = new_ptr_info });
|
|
// },
|
|
// else => {},
|
|
// }
|
|
// @compileError("invalid type given to bun.span: " ++ @typeName(T));
|
|
// }
|
|
|
|
pub fn span(ptr: anytype) Span(@TypeOf(ptr)) {
|
|
if (@typeInfo(@TypeOf(ptr)) == .Optional) {
|
|
if (ptr) |non_null| {
|
|
return span(non_null);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
const Result = Span(@TypeOf(ptr));
|
|
const l = len(ptr);
|
|
const ptr_info = @typeInfo(Result).Pointer;
|
|
if (ptr_info.sentinel) |s_ptr| {
|
|
const s = @ptrCast(*align(1) const ptr_info.child, s_ptr).*;
|
|
return ptr[0..l :s];
|
|
} else {
|
|
return ptr[0..l];
|
|
}
|
|
}
|
|
|
|
pub const IdentityContext = @import("./identity_context.zig").IdentityContext;
|
|
pub const ArrayIdentityContext = @import("./identity_context.zig").ArrayIdentityContext;
|
|
pub const StringHashMapUnowned = struct {
|
|
pub const Key = struct {
|
|
hash: u64,
|
|
len: usize,
|
|
|
|
pub fn init(str: []const u8) Key {
|
|
return Key{
|
|
.hash = hash(str),
|
|
.len = str.len,
|
|
};
|
|
}
|
|
};
|
|
|
|
pub const Adapter = struct {
|
|
pub fn eql(_: @This(), a: Key, b: Key) bool {
|
|
return a.hash == b.hash and a.len == b.len;
|
|
}
|
|
|
|
pub fn hash(_: @This(), key: Key) u64 {
|
|
return key.hash;
|
|
}
|
|
};
|
|
};
|
|
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 {};
|
|
}
|
|
|
|
pub inline fn range(comptime min: anytype, comptime max: anytype) [max - min]usize {
|
|
return comptime brk: {
|
|
var slice: [max - min]usize = undefined;
|
|
var i: usize = min;
|
|
while (i < max) {
|
|
slice[i - min] = i;
|
|
i += 1;
|
|
}
|
|
break :brk slice;
|
|
};
|
|
}
|
|
|
|
pub fn copy(comptime Type: type, dest: []Type, src: []const Type) void {
|
|
if (comptime Environment.allow_assert) std.debug.assert(dest.len >= src.len);
|
|
if (@ptrToInt(src.ptr) == @ptrToInt(dest.ptr) or src.len == 0) return;
|
|
|
|
const input: []const u8 = std.mem.sliceAsBytes(src);
|
|
const output: []u8 = std.mem.sliceAsBytes(dest);
|
|
|
|
std.debug.assert(input.len > 0);
|
|
std.debug.assert(output.len > 0);
|
|
|
|
const does_input_or_output_overlap = (@ptrToInt(input.ptr) < @ptrToInt(output.ptr) and
|
|
@ptrToInt(input.ptr) + input.len > @ptrToInt(output.ptr)) or
|
|
(@ptrToInt(output.ptr) < @ptrToInt(input.ptr) and
|
|
@ptrToInt(output.ptr) + output.len > @ptrToInt(input.ptr));
|
|
|
|
if (!does_input_or_output_overlap) {
|
|
@memcpy(output.ptr, input.ptr, input.len);
|
|
} else {
|
|
C.memmove(output.ptr, input.ptr, input.len);
|
|
}
|
|
}
|
|
|
|
pub const hasCloneFn = std.meta.trait.multiTrait(.{ std.meta.trait.isContainer, std.meta.trait.hasFn("clone") });
|
|
pub fn cloneWithType(comptime T: type, item: T, allocator: std.mem.Allocator) !T {
|
|
if (comptime std.meta.trait.isIndexable(T)) {
|
|
const Child = std.meta.Child(T);
|
|
assertDefined(item);
|
|
|
|
if (comptime hasCloneFn(Child)) {
|
|
var slice = try allocator.alloc(Child, item.len);
|
|
for (slice, 0..) |*val, i| {
|
|
val.* = try item[i].clone(allocator);
|
|
}
|
|
return slice;
|
|
}
|
|
|
|
if (comptime std.meta.trait.isContainer(Child)) {
|
|
@compileError("Expected clone() to exist for slice child: " ++ @typeName(Child));
|
|
}
|
|
|
|
return try allocator.dupe(Child, item);
|
|
}
|
|
|
|
if (comptime hasCloneFn(T)) {
|
|
return try item.clone(allocator);
|
|
}
|
|
|
|
@compileError("Expected clone() to exist for " ++ @typeName(T));
|
|
}
|
|
|
|
pub fn clone(val: anytype, allocator: std.mem.Allocator) !@TypeOf(val) {
|
|
return cloneWithType(@TypeOf(val), val, allocator);
|
|
}
|
|
pub const StringBuilder = @import("./string_builder.zig");
|
|
|
|
pub fn assertDefined(val: anytype) void {
|
|
if (comptime !Environment.allow_assert) return;
|
|
const Type = @TypeOf(val);
|
|
|
|
if (comptime @typeInfo(Type) == .Optional) {
|
|
if (val) |res| {
|
|
assertDefined(res);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (comptime std.meta.trait.isSlice(Type)) {
|
|
std.debug.assert(val.len < std.math.maxInt(u32) + 1);
|
|
std.debug.assert(val.len < std.math.maxInt(u32) + 1);
|
|
std.debug.assert(val.len < std.math.maxInt(u32) + 1);
|
|
var slice: []Type = undefined;
|
|
if (val.len > 0) {
|
|
std.debug.assert(@ptrToInt(val.ptr) != @ptrToInt(slice.ptr));
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (comptime @typeInfo(Type) == .Pointer) {
|
|
var slice: *Type = undefined;
|
|
std.debug.assert(@ptrToInt(val) != @ptrToInt(slice));
|
|
return;
|
|
}
|
|
|
|
if (comptime @typeInfo(Type) == .Struct) {
|
|
inline for (comptime std.meta.fieldNames(Type)) |name| {
|
|
assertDefined(@field(val, name));
|
|
}
|
|
}
|
|
}
|
|
|
|
pub const LinearFifo = @import("./linear_fifo.zig").LinearFifo;
|
|
|
|
/// hash a string
|
|
pub fn hash(content: []const u8) u64 {
|
|
return std.hash.Wyhash.hash(0, content);
|
|
}
|
|
|
|
pub fn hash32(content: []const u8) u32 {
|
|
const res = hash(content);
|
|
return @truncate(u32, res);
|
|
}
|
|
|
|
pub const HiveArray = @import("./hive_array.zig").HiveArray;
|
|
|
|
pub fn rand(bytes: []u8) void {
|
|
_ = BoringSSL.RAND_bytes(bytes.ptr, bytes.len);
|
|
}
|
|
|
|
pub const ObjectPool = @import("./pool.zig").ObjectPool;
|
|
|
|
pub fn assertNonBlocking(fd: anytype) void {
|
|
std.debug.assert(
|
|
(std.os.fcntl(fd, std.os.F.GETFL, 0) catch unreachable) & std.os.O.NONBLOCK != 0,
|
|
);
|
|
}
|
|
|
|
pub fn ensureNonBlocking(fd: anytype) void {
|
|
const current = std.os.fcntl(fd, std.os.F.GETFL, 0) catch 0;
|
|
_ = std.os.fcntl(fd, std.os.F.SETFL, current | std.os.O.NONBLOCK) catch 0;
|
|
}
|
|
|
|
const global_scope_log = Output.scoped(.bun, false);
|
|
pub fn isReadable(fd: std.os.fd_t) PollFlag {
|
|
var polls = &[_]std.os.pollfd{
|
|
.{
|
|
.fd = fd,
|
|
.events = std.os.POLL.IN | std.os.POLL.ERR,
|
|
.revents = 0,
|
|
},
|
|
};
|
|
|
|
const result = (std.os.poll(polls, 0) catch 0) != 0;
|
|
global_scope_log("poll({d}) readable: {any} ({d})", .{ fd, result, polls[0].revents });
|
|
return if (result and polls[0].revents & std.os.POLL.HUP != 0)
|
|
PollFlag.hup
|
|
else if (result)
|
|
PollFlag.ready
|
|
else
|
|
PollFlag.not_ready;
|
|
}
|
|
|
|
pub const PollFlag = enum { ready, not_ready, hup };
|
|
pub fn isWritable(fd: std.os.fd_t) PollFlag {
|
|
var polls = &[_]std.os.pollfd{
|
|
.{
|
|
.fd = fd,
|
|
.events = std.os.POLL.OUT,
|
|
.revents = 0,
|
|
},
|
|
};
|
|
|
|
const result = (std.os.poll(polls, 0) catch 0) != 0;
|
|
global_scope_log("poll({d}) writable: {any} ({d})", .{ fd, result, polls[0].revents });
|
|
if (result and polls[0].revents & std.os.POLL.HUP != 0) {
|
|
return PollFlag.hup;
|
|
} else if (result) {
|
|
return PollFlag.ready;
|
|
} else {
|
|
return PollFlag.not_ready;
|
|
}
|
|
}
|
|
|
|
pub inline fn unreachablePanic(comptime fmts: []const u8, args: anytype) noreturn {
|
|
if (comptime !Environment.allow_assert) unreachable;
|
|
std.debug.panic(fmts, args);
|
|
}
|
|
|
|
pub fn StringEnum(comptime Type: type, comptime Map: anytype, value: []const u8) ?Type {
|
|
return ComptimeStringMap(Type, Map).get(value);
|
|
}
|
|
|
|
pub const Bunfig = @import("./bunfig.zig").Bunfig;
|
|
|
|
pub const HTTPThead = @import("./http_client_async.zig").HTTPThread;
|
|
|
|
pub const Analytics = @import("./analytics/analytics_thread.zig");
|
|
|
|
pub usingnamespace @import("./tagged_pointer.zig");
|
|
|
|
pub fn once(comptime function: anytype, comptime ReturnType: type) ReturnType {
|
|
const Result = struct {
|
|
var value: ReturnType = undefined;
|
|
var ran = false;
|
|
|
|
pub fn execute() ReturnType {
|
|
if (ran) return value;
|
|
ran = true;
|
|
value = function();
|
|
return value;
|
|
}
|
|
};
|
|
|
|
return Result.execute();
|
|
}
|
|
|
|
pub fn isHeapMemory(memory: anytype) bool {
|
|
if (comptime use_mimalloc) {
|
|
const Memory = @TypeOf(memory);
|
|
if (comptime std.meta.trait.isSingleItemPtr(Memory)) {
|
|
return Mimalloc.mi_is_in_heap_region(memory);
|
|
}
|
|
return Mimalloc.mi_is_in_heap_region(std.mem.sliceAsBytes(memory).ptr);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
pub const Mimalloc = @import("./allocators/mimalloc.zig");
|
|
|
|
pub inline fn isSliceInBuffer(slice: []const u8, buffer: []const u8) bool {
|
|
return slice.len > 0 and @ptrToInt(buffer.ptr) <= @ptrToInt(slice.ptr) and ((@ptrToInt(slice.ptr) + slice.len) <= (@ptrToInt(buffer.ptr) + buffer.len));
|
|
}
|
|
|
|
pub fn rangeOfSliceInBuffer(slice: []const u8, buffer: []const u8) ?[2]u32 {
|
|
if (!isSliceInBuffer(slice, buffer)) return null;
|
|
const r = [_]u32{
|
|
@truncate(u32, @ptrToInt(slice.ptr) -| @ptrToInt(buffer.ptr)),
|
|
@truncate(u32, slice.len),
|
|
};
|
|
if (comptime Environment.allow_assert)
|
|
std.debug.assert(strings.eqlLong(slice, buffer[r[0]..][0..r[1]], false));
|
|
return r;
|
|
}
|
|
|
|
pub const invalid_fd = std.math.maxInt(FileDescriptor);
|
|
|
|
pub const simdutf = @import("./bun.js/bindings/bun-simdutf.zig");
|
|
|
|
pub const JSC = @import("./jsc.zig");
|
|
pub const AsyncIO = @import("async_io");
|
|
|
|
pub const logger = @import("./logger.zig");
|
|
pub const HTTP = @import("./http_client_async.zig");
|
|
pub const ThreadPool = @import("./thread_pool.zig");
|
|
pub const picohttp = @import("./deps/picohttp.zig");
|
|
pub const uws = @import("./deps/uws.zig");
|
|
pub const BoringSSL = @import("./boringssl.zig");
|
|
pub const LOLHTML = @import("./deps/lol-html.zig");
|
|
pub const clap = @import("./deps/zig-clap/clap.zig");
|
|
pub const analytics = @import("./analytics.zig");
|
|
pub const DateTime = @import("./deps/zig-datetime/src/datetime.zig");
|
|
|
|
pub var start_time: i128 = 0;
|
|
|
|
pub fn openDir(dir: std.fs.Dir, path_: [:0]const u8) !std.fs.IterableDir {
|
|
const fd = try std.os.openatZ(dir.fd, path_, std.os.O.DIRECTORY | std.os.O.CLOEXEC | 0, 0);
|
|
return std.fs.IterableDir{ .dir = .{ .fd = fd } };
|
|
}
|
|
pub const MimallocArena = @import("./mimalloc_arena.zig").Arena;
|
|
|
|
/// This wrapper exists to avoid the call to sliceTo(0)
|
|
/// Zig's sliceTo(0) is scalar
|
|
pub fn getenvZ(path_: [:0]const u8) ?[]const u8 {
|
|
const ptr = std.c.getenv(path_.ptr) orelse return null;
|
|
return sliceTo(ptr, 0);
|
|
}
|
|
|
|
// These wrappers exist to use our strings.eqlLong function
|
|
pub const StringArrayHashMapContext = struct {
|
|
pub fn hash(_: @This(), s: []const u8) u32 {
|
|
return @truncate(u32, std.hash.Wyhash.hash(0, s));
|
|
}
|
|
pub fn eql(_: @This(), a: []const u8, b: []const u8, _: usize) bool {
|
|
return strings.eqlLong(a, b, true);
|
|
}
|
|
|
|
pub fn pre(input: []const u8) Prehashed {
|
|
return Prehashed{
|
|
.value = @This().hash(.{}, input),
|
|
.input = input,
|
|
};
|
|
}
|
|
|
|
pub const Prehashed = struct {
|
|
value: u32,
|
|
input: []const u8,
|
|
pub fn hash(this: @This(), s: []const u8) u32 {
|
|
if (s.ptr == this.input.ptr and s.len == this.input.len)
|
|
return this.value;
|
|
return @truncate(u32, std.hash.Wyhash.hash(0, s));
|
|
}
|
|
|
|
pub fn eql(_: @This(), a: []const u8, b: []const u8) bool {
|
|
return strings.eqlLong(a, b, true);
|
|
}
|
|
};
|
|
};
|
|
|
|
pub const StringHashMapContext = struct {
|
|
pub fn hash(_: @This(), s: []const u8) u64 {
|
|
return std.hash.Wyhash.hash(0, s);
|
|
}
|
|
pub fn eql(_: @This(), a: []const u8, b: []const u8) bool {
|
|
return strings.eqlLong(a, b, true);
|
|
}
|
|
|
|
pub fn pre(input: []const u8) Prehashed {
|
|
return Prehashed{
|
|
.value = @This().hash(.{}, input),
|
|
.input = input,
|
|
};
|
|
}
|
|
|
|
pub const Prehashed = struct {
|
|
value: u64,
|
|
input: []const u8,
|
|
pub fn hash(this: @This(), s: []const u8) u64 {
|
|
if (s.ptr == this.input.ptr and s.len == this.input.len)
|
|
return this.value;
|
|
return StringHashMapContext.hash(.{}, s);
|
|
}
|
|
|
|
pub fn eql(_: @This(), a: []const u8, b: []const u8) bool {
|
|
return strings.eqlLong(a, b, true);
|
|
}
|
|
};
|
|
};
|
|
|
|
pub fn StringArrayHashMap(comptime Type: type) type {
|
|
return std.ArrayHashMap([]const u8, Type, StringArrayHashMapContext, true);
|
|
}
|
|
|
|
pub fn StringArrayHashMapUnmanaged(comptime Type: type) type {
|
|
return std.ArrayHashMapUnmanaged([]const u8, Type, StringArrayHashMapContext, true);
|
|
}
|
|
|
|
pub fn StringHashMap(comptime Type: type) type {
|
|
return std.HashMap([]const u8, Type, StringHashMapContext, std.hash_map.default_max_load_percentage);
|
|
}
|
|
|
|
pub fn StringHashMapUnmanaged(comptime Type: type) type {
|
|
return std.HashMapUnmanaged([]const u8, Type, StringHashMapContext, std.hash_map.default_max_load_percentage);
|
|
}
|
|
|
|
const CopyFile = @import("./copy_file.zig");
|
|
pub const copyFileRange = CopyFile.copyFileRange;
|
|
pub const copyFile = CopyFile.copyFile;
|
|
|
|
pub fn parseDouble(input: []const u8) !f64 {
|
|
return JSC.WTF.parseDouble(input);
|
|
}
|
|
|
|
pub const SignalCode = enum(u8) {
|
|
SIGHUP = 1,
|
|
SIGINT = 2,
|
|
SIGQUIT = 3,
|
|
SIGILL = 4,
|
|
SIGTRAP = 5,
|
|
SIGABRT = 6,
|
|
SIGBUS = 7,
|
|
SIGFPE = 8,
|
|
SIGKILL = 9,
|
|
SIGUSR1 = 10,
|
|
SIGSEGV = 11,
|
|
SIGUSR2 = 12,
|
|
SIGPIPE = 13,
|
|
SIGALRM = 14,
|
|
SIGTERM = 15,
|
|
SIG16 = 16,
|
|
SIGCHLD = 17,
|
|
SIGCONT = 18,
|
|
SIGSTOP = 19,
|
|
SIGTSTP = 20,
|
|
SIGTTIN = 21,
|
|
SIGTTOU = 22,
|
|
SIGURG = 23,
|
|
SIGXCPU = 24,
|
|
SIGXFSZ = 25,
|
|
SIGVTALRM = 26,
|
|
SIGPROF = 27,
|
|
SIGWINCH = 28,
|
|
SIGIO = 29,
|
|
SIGPWR = 30,
|
|
SIGSYS = 31,
|
|
_,
|
|
|
|
pub fn name(value: SignalCode) ?[]const u8 {
|
|
if (@enumToInt(value) <= @enumToInt(SignalCode.SIGSYS)) {
|
|
return asByteSlice(@tagName(value));
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
pub fn from(value: anytype) SignalCode {
|
|
return @intToEnum(SignalCode, @truncate(u7, std.mem.asBytes(&value)[0]));
|
|
}
|
|
|
|
pub fn format(self: SignalCode, comptime _: []const u8, _: fmt.FormatOptions, writer: anytype) !void {
|
|
if (self.name()) |str| {
|
|
try std.fmt.format(writer, "code {d} ({s})", .{ @enumToInt(self), str });
|
|
} else {
|
|
try std.fmt.format(writer, "code {d}", .{@enumToInt(self)});
|
|
}
|
|
}
|
|
};
|
|
|
|
pub fn isMissingIOUring() bool {
|
|
if (comptime !Environment.isLinux)
|
|
// it is not missing when it was not supposed to be there in the first place
|
|
return false;
|
|
|
|
// cache the boolean value
|
|
const Missing = struct {
|
|
pub var is_missing_io_uring: ?bool = null;
|
|
};
|
|
|
|
return Missing.is_missing_io_uring orelse brk: {
|
|
const kernel = Analytics.GenerateHeader.GeneratePlatform.kernelVersion();
|
|
// io_uring was introduced in earlier versions of Linux, but it was not
|
|
// really usable for us until 5.3
|
|
const result = kernel.major < 5 or (kernel.major == 5 and kernel.minor < 3);
|
|
Missing.is_missing_io_uring = result;
|
|
break :brk result;
|
|
};
|
|
}
|
|
|
|
pub const CLI = @import("./cli.zig");
|
|
|
|
pub const PackageManager = @import("./install/install.zig").PackageManager;
|
|
|
|
pub const fs = @import("./fs.zig");
|
|
pub const Bundler = bundler.Bundler;
|
|
pub const bundler = @import("./bundler.zig");
|
|
pub const which = @import("./which.zig").which;
|
|
pub const js_parser = @import("./js_parser.zig");
|
|
pub const js_printer = @import("./js_printer.zig");
|
|
pub const js_lexer = @import("./js_lexer.zig");
|
|
pub const JSON = @import("./json_parser.zig");
|
|
pub const JSAst = @import("./js_ast.zig");
|
|
pub const bit_set = @import("./bit_set.zig");
|
|
|
|
pub fn enumMap(comptime T: type, comptime args: anytype) (fn (T) []const u8) {
|
|
const Map = struct {
|
|
const vargs = args;
|
|
const labels = brk: {
|
|
var vabels_ = std.enums.EnumArray(T, []const u8).initFill("");
|
|
@setEvalBranchQuota(99999);
|
|
inline for (vargs) |field| {
|
|
vabels_.set(field.@"0", field.@"1");
|
|
}
|
|
break :brk vabels_;
|
|
};
|
|
|
|
pub fn get(input: T) []const u8 {
|
|
return labels.get(input);
|
|
}
|
|
};
|
|
|
|
return Map.get;
|
|
}
|
|
|
|
/// Write 0's for every byte in Type
|
|
/// Ignores default struct values.
|
|
pub fn zero(comptime Type: type) Type {
|
|
var out: [@sizeOf(Type)]u8 align(@alignOf(Type)) = undefined;
|
|
@memset(@ptrCast([*]u8, &out), 0, out.len);
|
|
return @bitCast(Type, out);
|
|
}
|
|
pub const c_ares = @import("./deps/c_ares.zig");
|
|
pub const URL = @import("./url.zig").URL;
|
|
pub const FormData = @import("./url.zig").FormData;
|
|
|
|
var needs_proc_self_workaround: bool = false;
|
|
|
|
// This is our "polyfill" when /proc/self/fd is not available it's only
|
|
// necessary on linux because other platforms don't have an optional
|
|
// /proc/self/fd
|
|
fn getFdPathViaCWD(fd: std.os.fd_t, buf: *[@This().MAX_PATH_BYTES]u8) ![]u8 {
|
|
const prev_fd = try std.os.openatZ(std.os.AT.FDCWD, ".", 0, 0);
|
|
var needs_chdir = false;
|
|
defer {
|
|
if (needs_chdir) std.os.fchdir(prev_fd) catch unreachable;
|
|
std.os.close(prev_fd);
|
|
}
|
|
try std.os.fchdir(fd);
|
|
needs_chdir = true;
|
|
return std.os.getcwd(buf);
|
|
}
|
|
|
|
/// Get the absolute path to a file descriptor.
|
|
/// On Linux, when `/proc/self/fd` is not available, this function will attempt to use `fchdir` and `getcwd` to get the path instead.
|
|
pub fn getFdPath(fd: std.os.fd_t, buf: *[@This().MAX_PATH_BYTES]u8) ![]u8 {
|
|
if (comptime !Environment.isLinux) {
|
|
return std.os.getFdPath(fd, buf);
|
|
}
|
|
|
|
if (needs_proc_self_workaround) {
|
|
return getFdPathViaCWD(fd, buf);
|
|
}
|
|
|
|
return std.os.getFdPath(fd, buf) catch |err| {
|
|
if (err == error.FileNotFound and !needs_proc_self_workaround) {
|
|
needs_proc_self_workaround = true;
|
|
return getFdPathViaCWD(fd, buf);
|
|
}
|
|
|
|
return err;
|
|
};
|
|
}
|
|
|
|
fn lenSliceTo(ptr: anytype, comptime end: meta.Elem(@TypeOf(ptr))) usize {
|
|
switch (@typeInfo(@TypeOf(ptr))) {
|
|
.Pointer => |ptr_info| switch (ptr_info.size) {
|
|
.One => switch (@typeInfo(ptr_info.child)) {
|
|
.Array => |array_info| {
|
|
if (array_info.sentinel) |sentinel_ptr| {
|
|
const sentinel = @ptrCast(*align(1) const array_info.child, sentinel_ptr).*;
|
|
if (sentinel == end) {
|
|
return indexOfSentinel(array_info.child, end, ptr);
|
|
}
|
|
}
|
|
return std.mem.indexOfScalar(array_info.child, ptr, end) orelse array_info.len;
|
|
},
|
|
else => {},
|
|
},
|
|
.Many => if (ptr_info.sentinel) |sentinel_ptr| {
|
|
const sentinel = @ptrCast(*align(1) const ptr_info.child, sentinel_ptr).*;
|
|
// We may be looking for something other than the sentinel,
|
|
// but iterating past the sentinel would be a bug so we need
|
|
// to check for both.
|
|
var i: usize = 0;
|
|
while (ptr[i] != end and ptr[i] != sentinel) i += 1;
|
|
return i;
|
|
},
|
|
.C => {
|
|
std.debug.assert(ptr != null);
|
|
return indexOfSentinel(ptr_info.child, end, ptr);
|
|
},
|
|
.Slice => {
|
|
if (ptr_info.sentinel) |sentinel_ptr| {
|
|
const sentinel = @ptrCast(*align(1) const ptr_info.child, sentinel_ptr).*;
|
|
if (sentinel == end) {
|
|
return indexOfSentinel(ptr_info.child, sentinel, ptr);
|
|
}
|
|
}
|
|
return std.mem.indexOfScalar(ptr_info.child, ptr, end) orelse ptr.len;
|
|
},
|
|
},
|
|
else => {},
|
|
}
|
|
@compileError("invalid type given to std.mem.sliceTo: " ++ @typeName(@TypeOf(ptr)));
|
|
}
|
|
|
|
/// Helper for the return type of sliceTo()
|
|
fn SliceTo(comptime T: type, comptime end: meta.Elem(T)) type {
|
|
switch (@typeInfo(T)) {
|
|
.Optional => |optional_info| {
|
|
return ?SliceTo(optional_info.child, end);
|
|
},
|
|
.Pointer => |ptr_info| {
|
|
var new_ptr_info = ptr_info;
|
|
new_ptr_info.size = .Slice;
|
|
switch (ptr_info.size) {
|
|
.One => switch (@typeInfo(ptr_info.child)) {
|
|
.Array => |array_info| {
|
|
new_ptr_info.child = array_info.child;
|
|
// The return type must only be sentinel terminated if we are guaranteed
|
|
// to find the value searched for, which is only the case if it matches
|
|
// the sentinel of the type passed.
|
|
if (array_info.sentinel) |sentinel_ptr| {
|
|
const sentinel = @ptrCast(*align(1) const array_info.child, sentinel_ptr).*;
|
|
if (end == sentinel) {
|
|
new_ptr_info.sentinel = &end;
|
|
} else {
|
|
new_ptr_info.sentinel = null;
|
|
}
|
|
}
|
|
},
|
|
else => {},
|
|
},
|
|
.Many, .Slice => {
|
|
// The return type must only be sentinel terminated if we are guaranteed
|
|
// to find the value searched for, which is only the case if it matches
|
|
// the sentinel of the type passed.
|
|
if (ptr_info.sentinel) |sentinel_ptr| {
|
|
const sentinel = @ptrCast(*align(1) const ptr_info.child, sentinel_ptr).*;
|
|
if (end == sentinel) {
|
|
new_ptr_info.sentinel = &end;
|
|
} else {
|
|
new_ptr_info.sentinel = null;
|
|
}
|
|
}
|
|
},
|
|
.C => {
|
|
new_ptr_info.sentinel = &end;
|
|
// C pointers are always allowzero, but we don't want the return type to be.
|
|
std.debug.assert(new_ptr_info.is_allowzero);
|
|
new_ptr_info.is_allowzero = false;
|
|
},
|
|
}
|
|
return @Type(.{ .Pointer = new_ptr_info });
|
|
},
|
|
else => {},
|
|
}
|
|
@compileError("invalid type given to std.mem.sliceTo: " ++ @typeName(T));
|
|
}
|
|
|
|
/// Takes an array, a pointer to an array, a sentinel-terminated pointer, or a slice and
|
|
/// iterates searching for the first occurrence of `end`, returning the scanned slice.
|
|
/// If `end` is not found, the full length of the array/slice/sentinel terminated pointer is returned.
|
|
/// If the pointer type is sentinel terminated and `end` matches that terminator, the
|
|
/// resulting slice is also sentinel terminated.
|
|
/// Pointer properties such as mutability and alignment are preserved.
|
|
/// C pointers are assumed to be non-null.
|
|
pub fn sliceTo(ptr: anytype, comptime end: meta.Elem(@TypeOf(ptr))) SliceTo(@TypeOf(ptr), end) {
|
|
if (@typeInfo(@TypeOf(ptr)) == .Optional) {
|
|
const non_null = ptr orelse return null;
|
|
return sliceTo(non_null, end);
|
|
}
|
|
const Result = SliceTo(@TypeOf(ptr), end);
|
|
const length = lenSliceTo(ptr, end);
|
|
const ptr_info = @typeInfo(Result).Pointer;
|
|
if (ptr_info.sentinel) |s_ptr| {
|
|
const s = @ptrCast(*align(1) const ptr_info.child, s_ptr).*;
|
|
return ptr[0..length :s];
|
|
} else {
|
|
return ptr[0..length];
|
|
}
|
|
}
|
|
|
|
pub fn cstring(input: []const u8) [:0]const u8 {
|
|
if (input.len == 0)
|
|
return "";
|
|
|
|
if (comptime Environment.allow_assert) {
|
|
std.debug.assert(
|
|
input.ptr[input.len] == 0,
|
|
);
|
|
}
|
|
return @ptrCast([*:0]const u8, input.ptr)[0..input.len :0];
|
|
}
|
|
|
|
pub const Semver = @import("./install/semver.zig");
|
|
pub const ImportRecord = @import("./import_record.zig").ImportRecord;
|
|
pub const ImportKind = @import("./import_record.zig").ImportKind;
|
|
|
|
pub usingnamespace @import("./util.zig");
|
|
pub const fast_debug_build_cmd = .None;
|
|
pub const fast_debug_build_mode = fast_debug_build_cmd != .None and
|
|
Environment.isDebug;
|
|
|
|
pub const MultiArrayList = @import("./multi_array_list.zig").MultiArrayList;
|
|
|
|
pub const Joiner = @import("./string_joiner.zig");
|
|
pub const renamer = @import("./renamer.zig");
|
|
pub const sourcemap = @import("./sourcemap/sourcemap.zig");
|
|
|
|
pub fn asByteSlice(buffer: anytype) []const u8 {
|
|
return switch (@TypeOf(buffer)) {
|
|
[]const u8, []u8, [:0]const u8, [:0]u8 => buffer.ptr[0..buffer.len],
|
|
[*:0]u8, [*:0]const u8 => buffer[0..len(buffer)],
|
|
[*c]const u8, [*c]u8 => span(buffer),
|
|
else => |T| {
|
|
if (comptime std.meta.trait.isPtrTo(.Array)(T)) {
|
|
return @as([]const u8, buffer);
|
|
}
|
|
|
|
@compileError("Unsupported type " ++ @typeName(T));
|
|
},
|
|
};
|
|
}
|
|
|
|
comptime {
|
|
if (fast_debug_build_cmd != .RunCommand and fast_debug_build_mode) {
|
|
_ = @import("./bun.js/node/buffer.zig").BufferVectorized.fill;
|
|
_ = @import("./cli/upgrade_command.zig").Version;
|
|
}
|
|
}
|
|
|
|
pub fn DebugOnlyDisabler(comptime Type: type) type {
|
|
return struct {
|
|
const T = Type;
|
|
threadlocal var disable_create_in_debug: if (Environment.allow_assert) usize else u0 = 0;
|
|
pub inline fn disable() void {
|
|
if (comptime !Environment.allow_assert) return;
|
|
disable_create_in_debug += 1;
|
|
}
|
|
|
|
pub inline fn enable() void {
|
|
if (comptime !Environment.allow_assert) return;
|
|
disable_create_in_debug -= 1;
|
|
}
|
|
|
|
pub inline fn assert() void {
|
|
if (comptime !Environment.allow_assert) return;
|
|
if (disable_create_in_debug > 0) {
|
|
Output.panic(comptime "[" ++ @typeName(T) ++ "] called while disabled (did you forget to call enable?)", .{});
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
const FailingAllocator = struct {
|
|
fn alloc(_: *anyopaque, _: usize, _: u8, _: usize) ?[*]u8 {
|
|
if (comptime Environment.allow_assert) {
|
|
unreachablePanic("FailingAllocator should never be reached. This means some memory was not defined", .{});
|
|
}
|
|
return null;
|
|
}
|
|
|
|
fn resize(_: *anyopaque, _: []u8, _: u8, _: usize, _: usize) bool {
|
|
if (comptime Environment.allow_assert) {
|
|
unreachablePanic("FailingAllocator should never be reached. This means some memory was not defined", .{});
|
|
}
|
|
return false;
|
|
}
|
|
|
|
fn free(
|
|
_: *anyopaque,
|
|
_: []u8,
|
|
_: u8,
|
|
_: usize,
|
|
) void {
|
|
unreachable;
|
|
}
|
|
};
|
|
|
|
/// When we want to avoid initializing a value as undefined, we can use this allocator
|
|
pub const failing_allocator = std.mem.Allocator{ .ptr = undefined, .vtable = &.{
|
|
.alloc = FailingAllocator.alloc,
|
|
.resize = FailingAllocator.resize,
|
|
.free = FailingAllocator.free,
|
|
} };
|
|
|
|
/// Reload Bun's process
|
|
///
|
|
/// This clones envp, argv, and gets the current executable path
|
|
///
|
|
/// Overwrites the current process with the new process
|
|
///
|
|
/// Must be able to allocate memory. malloc is not signal safe, but it's
|
|
/// best-effort. Not much we can do if it fails.
|
|
pub fn reloadProcess(
|
|
allocator: std.mem.Allocator,
|
|
clear_terminal: bool,
|
|
) void {
|
|
const PosixSpawn = @import("./bun.js/api/bun/spawn.zig").PosixSpawn;
|
|
|
|
var dupe_argv = allocator.allocSentinel(?[*:0]const u8, std.os.argv.len, null) catch unreachable;
|
|
for (std.os.argv, dupe_argv) |src, *dest| {
|
|
dest.* = (allocator.dupeZ(u8, sliceTo(src, 0)) catch unreachable).ptr;
|
|
}
|
|
|
|
var environ_slice = std.mem.span(std.c.environ);
|
|
var environ = allocator.allocSentinel(?[*:0]const u8, environ_slice.len, null) catch unreachable;
|
|
for (environ_slice, environ) |src, *dest| {
|
|
if (src == null) {
|
|
dest.* = null;
|
|
} else {
|
|
dest.* = (allocator.dupeZ(u8, sliceTo(src.?, 0)) catch unreachable).ptr;
|
|
}
|
|
}
|
|
|
|
// we must clone selfExePath incase the argv[0] was not an absolute path (what appears in the terminal)
|
|
const exec_path = (allocator.dupeZ(u8, std.fs.selfExePathAlloc(allocator) catch unreachable) catch unreachable).ptr;
|
|
|
|
// we clone argv so that the memory address isn't the same as the libc one
|
|
const argv = @ptrCast([*:null]?[*:0]const u8, dupe_argv.ptr);
|
|
|
|
// we clone envp so that the memory address of environment variables isn't the same as the libc one
|
|
const envp = @ptrCast([*:null]?[*:0]const u8, environ.ptr);
|
|
|
|
// Clear the terminal
|
|
if (clear_terminal) {
|
|
Output.flush();
|
|
Output.disableBuffering();
|
|
Output.resetTerminalAll();
|
|
}
|
|
|
|
// macOS doesn't have CLOEXEC, so we must go through posix_spawn
|
|
if (comptime Environment.isMac) {
|
|
var actions = PosixSpawn.Actions.init() catch unreachable;
|
|
actions.inherit(0) catch unreachable;
|
|
actions.inherit(1) catch unreachable;
|
|
actions.inherit(2) catch unreachable;
|
|
var attrs = PosixSpawn.Attr.init() catch unreachable;
|
|
attrs.set(
|
|
C.POSIX_SPAWN_CLOEXEC_DEFAULT |
|
|
// Apple Extension: If this bit is set, rather
|
|
// than returning to the caller, posix_spawn(2)
|
|
// and posix_spawnp(2) will behave as a more
|
|
// featureful execve(2).
|
|
C.POSIX_SPAWN_SETEXEC |
|
|
C.POSIX_SPAWN_SETSIGDEF | C.POSIX_SPAWN_SETSIGMASK,
|
|
) catch unreachable;
|
|
switch (PosixSpawn.spawnZ(exec_path, actions, attrs, @ptrCast([*:null]?[*:0]const u8, argv), @ptrCast([*:null]?[*:0]const u8, envp))) {
|
|
.err => |err| {
|
|
Output.panic("Unexpected error while reloading: {d} {s}", .{ err.errno, @tagName(err.getErrno()) });
|
|
},
|
|
.result => |_| {},
|
|
}
|
|
} else {
|
|
const err = std.os.execveZ(
|
|
exec_path,
|
|
argv,
|
|
envp,
|
|
);
|
|
Output.panic("Unexpected error while reloading: {s}", .{@errorName(err)});
|
|
}
|
|
}
|
|
pub var auto_reload_on_crash = false;
|
|
|
|
pub const options = @import("./options.zig");
|