authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-03-03 12:08:18-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-03-03 12:08:18-07:00
logdf4cfc2ecf498bf4615ccbaa93438849322bbd18
treea71611e86cacd8e021190cc4755574c514acb5c7
parent72443fb88cfddad8a58868c150eaf5818826cb21
parent75ff34db9e93056482233f8476a06f78b4a2f3c2

Merge remote-tracking branch 'origin/master' into llvm16


41 files changed, 1826 insertions(+), 588 deletions(-)

lib/build_runner.zig+1
......@@ -362,6 +362,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi
362362 \\ --cache-dir [path] Override path to local Zig cache directory
363363 \\ --global-cache-dir [path] Override path to global Zig cache directory
364364 \\ --zig-lib-dir [arg] Override path to Zig lib directory
365 \\ --build-runner [file] Override path to build runner
365366 \\ --debug-log [scope] Enable debugging the compiler
366367 \\ --verbose-link Enable compiler debug output for linking
367368 \\ --verbose-air Enable compiler debug output for Zig AIR
lib/std/Build/Cache.zig+7-2
......@@ -956,11 +956,16 @@ fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) !void {
956956
957957// Create/Write a file, close it, then grab its stat.mtime timestamp.
958958fn testGetCurrentFileTimestamp() !i128 {
959 var file = try fs.cwd().createFile("test-filetimestamp.tmp", .{
959 const test_out_file = "test-filetimestamp.tmp";
960
961 var file = try fs.cwd().createFile(test_out_file, .{
960962 .read = true,
961963 .truncate = true,
962964 });
963 defer file.close();
965 defer {
966 file.close();
967 fs.cwd().deleteFile(test_out_file) catch {};
968 }
964969
965970 return (try file.stat()).mtime;
966971}
lib/std/c.zig+2
......@@ -171,7 +171,9 @@ pub extern "c" fn dup(fd: c.fd_t) c_int;
171171pub extern "c" fn dup2(old_fd: c.fd_t, new_fd: c.fd_t) c_int;
172172pub extern "c" fn readlink(noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize;
173173pub extern "c" fn readlinkat(dirfd: c.fd_t, noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize;
174pub extern "c" fn chmod(path: [*:0]const u8, mode: c.mode_t) c_int;
174175pub extern "c" fn fchmod(fd: c.fd_t, mode: c.mode_t) c_int;
176pub extern "c" fn fchmodat(fd: c.fd_t, path: [*:0]const u8, mode: c.mode_t, flags: c_uint) c_int;
175177pub extern "c" fn fchown(fd: c.fd_t, owner: c.uid_t, group: c.gid_t) c_int;
176178pub extern "c" fn umask(mode: c.mode_t) c.mode_t;
177179
lib/std/child_process.zig+63-203
......@@ -19,8 +19,15 @@ const maxInt = std.math.maxInt;
1919const assert = std.debug.assert;
2020
2121pub const ChildProcess = struct {
22 pid: if (builtin.os.tag == .windows) void else i32,
23 handle: if (builtin.os.tag == .windows) windows.HANDLE else void,
22 pub const Id = switch (builtin.os.tag) {
23 .windows => windows.HANDLE,
24 else => os.pid_t,
25 };
26
27 /// Available after calling `spawn()`. This becomes `undefined` after calling `wait()`.
28 /// On Windows this is the hProcess.
29 /// On POSIX this is the pid.
30 id: Id,
2431 thread_handle: if (builtin.os.tag == .windows) windows.HANDLE else void,
2532
2633 allocator: mem.Allocator,
......@@ -105,8 +112,7 @@ pub const ChildProcess = struct {
105112 return .{
106113 .allocator = allocator,
107114 .argv = argv,
108 .pid = undefined,
109 .handle = undefined,
115 .id = undefined,
110116 .thread_handle = undefined,
111117 .err_pipe = null,
112118 .term = null,
......@@ -131,6 +137,7 @@ pub const ChildProcess = struct {
131137 }
132138
133139 /// On success must call `kill` or `wait`.
140 /// After spawning the `id` is available.
134141 pub fn spawn(self: *ChildProcess) SpawnError!void {
135142 if (!std.process.can_spawn) {
136143 @compileError("the target operating system cannot spawn processes");
......@@ -167,7 +174,7 @@ pub const ChildProcess = struct {
167174 return term;
168175 }
169176
170 try windows.TerminateProcess(self.handle, exit_code);
177 try windows.TerminateProcess(self.id, exit_code);
171178 try self.waitUnwrappedWindows();
172179 return self.term.?;
173180 }
......@@ -177,18 +184,21 @@ pub const ChildProcess = struct {
177184 self.cleanupStreams();
178185 return term;
179186 }
180 try os.kill(self.pid, os.SIG.TERM);
187 try os.kill(self.id, os.SIG.TERM);
181188 try self.waitUnwrapped();
182189 return self.term.?;
183190 }
184191
185192 /// Blocks until child process terminates and then cleans up all resources.
186193 pub fn wait(self: *ChildProcess) !Term {
187 if (builtin.os.tag == .windows) {
188 return self.waitWindows();
189 } else {
190 return self.waitPosix();
191 }
194 const term = if (builtin.os.tag == .windows)
195 try self.waitWindows()
196 else
197 try self.waitPosix();
198
199 self.id = undefined;
200
201 return term;
192202 }
193203
194204 pub const ExecResult = struct {
......@@ -197,6 +207,19 @@ pub const ChildProcess = struct {
197207 stderr: []u8,
198208 };
199209
210 fn fifoToOwnedArrayList(fifo: *std.io.PollFifo) std.ArrayList(u8) {
211 if (fifo.head > 0) {
212 std.mem.copy(u8, fifo.buf[0..fifo.count], fifo.buf[fifo.head .. fifo.head + fifo.count]);
213 }
214 const result = std.ArrayList(u8){
215 .items = fifo.buf[0..fifo.count],
216 .capacity = fifo.buf.len,
217 .allocator = fifo.allocator,
218 };
219 fifo.* = std.io.PollFifo.init(fifo.allocator);
220 return result;
221 }
222
200223 /// Collect the output from the process's stdout and stderr. Will return once all output
201224 /// has been collected. This does not mean that the process has ended. `wait` should still
202225 /// be called to wait for and clean up the process.
......@@ -210,196 +233,33 @@ pub const ChildProcess = struct {
210233 ) !void {
211234 debug.assert(child.stdout_behavior == .Pipe);
212235 debug.assert(child.stderr_behavior == .Pipe);
213 if (builtin.os.tag == .haiku) {
214 const stdout_in = child.stdout.?.reader();
215 const stderr_in = child.stderr.?.reader();
216
217 try stdout_in.readAllArrayList(stdout, max_output_bytes);
218 try stderr_in.readAllArrayList(stderr, max_output_bytes);
219 } else if (builtin.os.tag == .windows) {
220 try collectOutputWindows(child, stdout, stderr, max_output_bytes);
221 } else {
222 try collectOutputPosix(child, stdout, stderr, max_output_bytes);
223 }
224 }
225236
226 fn collectOutputPosix(
227 child: ChildProcess,
228 stdout: *std.ArrayList(u8),
229 stderr: *std.ArrayList(u8),
230 max_output_bytes: usize,
231 ) !void {
232 var poll_fds = [_]os.pollfd{
233 .{ .fd = child.stdout.?.handle, .events = os.POLL.IN, .revents = undefined },
234 .{ .fd = child.stderr.?.handle, .events = os.POLL.IN, .revents = undefined },
235 };
236
237 var dead_fds: usize = 0;
238 // We ask for ensureTotalCapacity with this much extra space. This has more of an
239 // effect on small reads because once the reads start to get larger the amount
240 // of space an ArrayList will allocate grows exponentially.
241 const bump_amt = 512;
242
243 const err_mask = os.POLL.ERR | os.POLL.NVAL | os.POLL.HUP;
244
245 while (dead_fds < poll_fds.len) {
246 const events = try os.poll(&poll_fds, std.math.maxInt(i32));
247 if (events == 0) continue;
248
249 var remove_stdout = false;
250 var remove_stderr = false;
251 // Try reading whatever is available before checking the error
252 // conditions.
253 // It's still possible to read after a POLL.HUP is received, always
254 // check if there's some data waiting to be read first.
255 if (poll_fds[0].revents & os.POLL.IN != 0) {
256 // stdout is ready.
257 const new_capacity = std.math.min(stdout.items.len + bump_amt, max_output_bytes);
258 try stdout.ensureTotalCapacity(new_capacity);
259 const buf = stdout.unusedCapacitySlice();
260 if (buf.len == 0) return error.StdoutStreamTooLong;
261 const nread = try os.read(poll_fds[0].fd, buf);
262 stdout.items.len += nread;
263
264 // Remove the fd when the EOF condition is met.
265 remove_stdout = nread == 0;
266 } else {
267 remove_stdout = poll_fds[0].revents & err_mask != 0;
268 }
237 // we could make this work with multiple allocators but YAGNI
238 if (stdout.allocator.ptr != stderr.allocator.ptr or
239 stdout.allocator.vtable != stderr.allocator.vtable)
240 @panic("ChildProcess.collectOutput only supports 1 allocator");
269241
270 if (poll_fds[1].revents & os.POLL.IN != 0) {
271 // stderr is ready.
272 const new_capacity = std.math.min(stderr.items.len + bump_amt, max_output_bytes);
273 try stderr.ensureTotalCapacity(new_capacity);
274 const buf = stderr.unusedCapacitySlice();
275 if (buf.len == 0) return error.StderrStreamTooLong;
276 const nread = try os.read(poll_fds[1].fd, buf);
277 stderr.items.len += nread;
278
279 // Remove the fd when the EOF condition is met.
280 remove_stderr = nread == 0;
281 } else {
282 remove_stderr = poll_fds[1].revents & err_mask != 0;
283 }
242 var poller = std.io.poll(stdout.allocator, enum { stdout, stderr }, .{
243 .stdout = child.stdout.?,
244 .stderr = child.stderr.?,
245 });
246 defer poller.deinit();
284247
285 // Exclude the fds that signaled an error.
286 if (remove_stdout) {
287 poll_fds[0].fd = -1;
288 dead_fds += 1;
289 }
290 if (remove_stderr) {
291 poll_fds[1].fd = -1;
292 dead_fds += 1;
293 }
248 while (try poller.poll()) {
249 if (poller.fifo(.stdout).count > max_output_bytes)
250 return error.StdoutStreamTooLong;
251 if (poller.fifo(.stderr).count > max_output_bytes)
252 return error.StderrStreamTooLong;
294253 }
295 }
296254
297 const WindowsAsyncReadResult = enum {
298 pending,
299 closed,
300 full,
301 };
302
303 fn windowsAsyncRead(
304 handle: windows.HANDLE,
305 overlapped: *windows.OVERLAPPED,
306 buf: *std.ArrayList(u8),
307 bump_amt: usize,
308 max_output_bytes: usize,
309 ) !WindowsAsyncReadResult {
310 while (true) {
311 const new_capacity = std.math.min(buf.items.len + bump_amt, max_output_bytes);
312 try buf.ensureTotalCapacity(new_capacity);
313 const next_buf = buf.unusedCapacitySlice();
314 if (next_buf.len == 0) return .full;
315 var read_bytes: u32 = undefined;
316 const read_result = windows.kernel32.ReadFile(handle, next_buf.ptr, math.cast(u32, next_buf.len) orelse maxInt(u32), &read_bytes, overlapped);
317 if (read_result == 0) return switch (windows.kernel32.GetLastError()) {
318 .IO_PENDING => .pending,
319 .BROKEN_PIPE => .closed,
320 else => |err| windows.unexpectedError(err),
321 };
322 buf.items.len += read_bytes;
323 }
255 stdout.* = fifoToOwnedArrayList(poller.fifo(.stdout));
256 stderr.* = fifoToOwnedArrayList(poller.fifo(.stderr));
324257 }
325258
326 fn collectOutputWindows(child: ChildProcess, stdout: *std.ArrayList(u8), stderr: *std.ArrayList(u8), max_output_bytes: usize) !void {
327 const bump_amt = 512;
328 const outs = [_]*std.ArrayList(u8){
329 stdout,
330 stderr,
331 };
332 const handles = [_]windows.HANDLE{
333 child.stdout.?.handle,
334 child.stderr.?.handle,
335 };
336
337 var overlapped = [_]windows.OVERLAPPED{
338 mem.zeroes(windows.OVERLAPPED),
339 mem.zeroes(windows.OVERLAPPED),
340 };
341
342 var wait_objects: [2]windows.HANDLE = undefined;
343 var wait_object_count: u2 = 0;
344
345 // we need to cancel all pending IO before returning so our OVERLAPPED values don't go out of scope
346 defer for (wait_objects[0..wait_object_count]) |o| {
347 _ = windows.kernel32.CancelIo(o);
348 };
349
350 // Windows Async IO requires an initial call to ReadFile before waiting on the handle
351 for ([_]u1{ 0, 1 }) |i| {
352 switch (try windowsAsyncRead(handles[i], &overlapped[i], outs[i], bump_amt, max_output_bytes)) {
353 .pending => {
354 wait_objects[wait_object_count] = handles[i];
355 wait_object_count += 1;
356 },
357 .closed => {}, // don't add to the wait_objects list
358 .full => return if (i == 0) error.StdoutStreamTooLong else error.StderrStreamTooLong,
359 }
360 }
361
362 while (wait_object_count > 0) {
363 const status = windows.kernel32.WaitForMultipleObjects(wait_object_count, &wait_objects, 0, windows.INFINITE);
364 if (status == windows.WAIT_FAILED) {
365 switch (windows.kernel32.GetLastError()) {
366 else => |err| return windows.unexpectedError(err),
367 }
368 }
369 if (status < windows.WAIT_OBJECT_0 or status > windows.WAIT_OBJECT_0 + wait_object_count - 1)
370 unreachable;
371
372 const wait_idx = status - windows.WAIT_OBJECT_0;
373
374 // this extra `i` index is needed to map the wait handle back to the stdout or stderr
375 // values since the wait_idx can change which handle it corresponds with
376 const i: u1 = if (wait_objects[wait_idx] == handles[0]) 0 else 1;
377
378 // remove completed event from the wait list
379 wait_object_count -= 1;
380 if (wait_idx == 0)
381 wait_objects[0] = wait_objects[1];
382
383 var read_bytes: u32 = undefined;
384 if (windows.kernel32.GetOverlappedResult(handles[i], &overlapped[i], &read_bytes, 0) == 0) {
385 switch (windows.kernel32.GetLastError()) {
386 .BROKEN_PIPE => continue,
387 else => |err| return windows.unexpectedError(err),
388 }
389 }
390
391 outs[i].items.len += read_bytes;
392
393 switch (try windowsAsyncRead(handles[i], &overlapped[i], outs[i], bump_amt, max_output_bytes)) {
394 .pending => {
395 wait_objects[wait_object_count] = handles[i];
396 wait_object_count += 1;
397 },
398 .closed => {}, // don't add to the wait_objects list
399 .full => return if (i == 0) error.StdoutStreamTooLong else error.StderrStreamTooLong,
400 }
401 }
402 }
259 pub const ExecError = os.GetCwdError || os.ReadError || SpawnError || os.PollError || error{
260 StdoutStreamTooLong,
261 StderrStreamTooLong,
262 };
403263
404264 /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns.
405265 /// If it succeeds, the caller owns result.stdout and result.stderr memory.
......@@ -411,7 +271,7 @@ pub const ChildProcess = struct {
411271 env_map: ?*const EnvMap = null,
412272 max_output_bytes: usize = 50 * 1024,
413273 expand_arg0: Arg0Expand = .no_expand,
414 }) !ExecResult {
274 }) ExecError!ExecResult {
415275 var child = ChildProcess.init(args.argv, args.allocator);
416276 child.stdin_behavior = .Ignore;
417277 child.stdout_behavior = .Pipe;
......@@ -459,18 +319,18 @@ pub const ChildProcess = struct {
459319 }
460320
461321 fn waitUnwrappedWindows(self: *ChildProcess) !void {
462 const result = windows.WaitForSingleObjectEx(self.handle, windows.INFINITE, false);
322 const result = windows.WaitForSingleObjectEx(self.id, windows.INFINITE, false);
463323
464324 self.term = @as(SpawnError!Term, x: {
465325 var exit_code: windows.DWORD = undefined;
466 if (windows.kernel32.GetExitCodeProcess(self.handle, &exit_code) == 0) {
326 if (windows.kernel32.GetExitCodeProcess(self.id, &exit_code) == 0) {
467327 break :x Term{ .Unknown = 0 };
468328 } else {
469329 break :x Term{ .Exited = @truncate(u8, exit_code) };
470330 }
471331 });
472332
473 os.close(self.handle);
333 os.close(self.id);
474334 os.close(self.thread_handle);
475335 self.cleanupStreams();
476336 return result;
......@@ -478,9 +338,9 @@ pub const ChildProcess = struct {
478338
479339 fn waitUnwrapped(self: *ChildProcess) !void {
480340 const res: os.WaitPidResult = if (comptime builtin.target.isDarwin())
481 try os.posix_spawn.waitpid(self.pid, 0)
341 try os.posix_spawn.waitpid(self.id, 0)
482342 else
483 os.waitpid(self.pid, 0);
343 os.waitpid(self.id, 0);
484344 const status = res.status;
485345 self.cleanupStreams();
486346 self.handleWaitResult(status);
......@@ -638,7 +498,7 @@ pub const ChildProcess = struct {
638498 self.stderr = null;
639499 }
640500
641 self.pid = pid;
501 self.id = pid;
642502 self.term = null;
643503
644504 if (self.stdin_behavior == StdIo.Pipe) {
......@@ -812,7 +672,7 @@ pub const ChildProcess = struct {
812672 self.stderr = null;
813673 }
814674
815 self.pid = pid;
675 self.id = pid;
816676 self.err_pipe = err_pipe;
817677 self.term = null;
818678
......@@ -1078,7 +938,7 @@ pub const ChildProcess = struct {
1078938 self.stderr = null;
1079939 }
1080940
1081 self.handle = piProcInfo.hProcess;
941 self.id = piProcInfo.hProcess;
1082942 self.thread_handle = piProcInfo.hThread;
1083943 self.term = null;
1084944
lib/std/crypto.zig+2
......@@ -47,6 +47,8 @@ pub const auth = struct {
4747/// Core functions, that should rarely be used directly by applications.
4848pub const core = struct {
4949 pub const aes = @import("crypto/aes.zig");
50 pub const keccak = @import("crypto/keccak_p.zig");
51
5052 pub const Ascon = @import("crypto/ascon.zig").State;
5153 pub const Gimli = @import("crypto/gimli.zig").State;
5254 pub const Xoodoo = @import("crypto/xoodoo.zig").State;
lib/std/crypto/25519/field.zig+1-1
......@@ -287,7 +287,7 @@ pub const Fe = struct {
287287 return _carry128(&r);
288288 }
289289
290 inline fn _sq(a: Fe, comptime double: bool) Fe {
290 fn _sq(a: Fe, comptime double: bool) Fe {
291291 var ax: [5]u128 = undefined;
292292 var r: [5]u128 = undefined;
293293 comptime var i = 0;
lib/std/crypto/benchmark.zig+2
......@@ -25,6 +25,8 @@ const hashes = [_]Crypto{
2525 Crypto{ .ty = crypto.hash.sha2.Sha512, .name = "sha512" },
2626 Crypto{ .ty = crypto.hash.sha3.Sha3_256, .name = "sha3-256" },
2727 Crypto{ .ty = crypto.hash.sha3.Sha3_512, .name = "sha3-512" },
28 Crypto{ .ty = crypto.hash.sha3.Shake128, .name = "shake-128" },
29 Crypto{ .ty = crypto.hash.sha3.Shake256, .name = "shake-256" },
2830 Crypto{ .ty = crypto.hash.Gimli, .name = "gimli-hash" },
2931 Crypto{ .ty = crypto.hash.blake2.Blake2s256, .name = "blake2s" },
3032 Crypto{ .ty = crypto.hash.blake2.Blake2b512, .name = "blake2b" },
lib/std/crypto/keccak_p.zig created+277
......@@ -0,0 +1,277 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const math = std.math;
4const mem = std.mem;
5
6/// The Keccak-f permutation.
7pub fn KeccakF(comptime f: u11) type {
8 comptime assert(f > 200 and f <= 1600 and f % 200 == 0); // invalid bit size
9 const T = std.meta.Int(.unsigned, f / 25);
10 const Block = [25]T;
11
12 const PI = [_]u5{
13 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
14 };
15
16 return struct {
17 const Self = @This();
18
19 /// Number of bytes in the state.
20 pub const block_bytes = f / 8;
21
22 /// Maximum number of rounds for the given f parameter.
23 pub const max_rounds = 12 + 2 * math.log2(f / 25);
24
25 // Round constants
26 const RC = rc: {
27 const RC64 = [_]u64{
28 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
29 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
30 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
31 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
32 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
33 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
34 };
35 var rc: [max_rounds]T = undefined;
36 for (&rc, RC64[0..max_rounds]) |*t, c| t.* = @truncate(T, c);
37 break :rc rc;
38 };
39
40 st: Block = [_]T{0} ** 25,
41
42 /// Initialize the state from a slice of bytes.
43 pub fn init(bytes: [block_bytes]u8) Self {
44 var self: Self = undefined;
45 inline for (&self.st, 0..) |*r, i| {
46 r.* = mem.readIntLittle(T, bytes[@sizeOf(T) * i ..][0..@sizeOf(T)]);
47 }
48 return self;
49 }
50
51 /// A representation of the state as bytes. The byte order is architecture-dependent.
52 pub fn asBytes(self: *Self) *[block_bytes]u8 {
53 return mem.asBytes(&self.st);
54 }
55
56 /// Byte-swap the entire state if the architecture doesn't match the required endianness.
57 pub fn endianSwap(self: *Self) void {
58 for (&self.st) |*w| {
59 w.* = mem.littleTooNative(T, w.*);
60 }
61 }
62
63 /// Set bytes starting at the beginning of the state.
64 pub fn setBytes(self: *Self, bytes: []const u8) void {
65 var i: usize = 0;
66 while (i + @sizeOf(T) <= bytes.len) : (i += @sizeOf(T)) {
67 self.st[i / @sizeOf(T)] = mem.readIntLittle(T, bytes[i..][0..@sizeOf(T)]);
68 }
69 if (i < bytes.len) {
70 var padded = [_]u8{0} ** @sizeOf(T);
71 mem.copy(u8, padded[0 .. bytes.len - i], bytes[i..]);
72 self.st[i / @sizeOf(T)] = mem.readIntLittle(T, padded[0..]);
73 }
74 }
75
76 /// XOR a byte into the state at a given offset.
77 pub fn addByte(self: *Self, byte: u8, offset: usize) void {
78 const z = @sizeOf(T) * @truncate(math.Log2Int(T), offset % @sizeOf(T));
79 self.st[offset / @sizeOf(T)] ^= @as(T, byte) << z;
80 }
81
82 /// XOR bytes into the beginning of the state.
83 pub fn addBytes(self: *Self, bytes: []const u8) void {
84 var i: usize = 0;
85 while (i + @sizeOf(T) <= bytes.len) : (i += @sizeOf(T)) {
86 self.st[i / @sizeOf(T)] ^= mem.readIntLittle(T, bytes[i..][0..@sizeOf(T)]);
87 }
88 if (i < bytes.len) {
89 var padded = [_]u8{0} ** @sizeOf(T);
90 mem.copy(u8, padded[0 .. bytes.len - i], bytes[i..]);
91 self.st[i / @sizeOf(T)] ^= mem.readIntLittle(T, padded[0..]);
92 }
93 }
94
95 /// Extract the first bytes of the state.
96 pub fn extractBytes(self: *Self, out: []u8) void {
97 var i: usize = 0;
98 while (i + @sizeOf(T) <= out.len) : (i += @sizeOf(T)) {
99 mem.writeIntLittle(T, out[i..][0..@sizeOf(T)], self.st[i / @sizeOf(T)]);
100 }
101 if (i < out.len) {
102 var padded = [_]u8{0} ** @sizeOf(T);
103 mem.writeIntLittle(T, padded[0..], self.st[i / @sizeOf(T)]);
104 mem.copy(u8, out[i..], padded[0 .. out.len - i]);
105 }
106 }
107
108 /// XOR the first bytes of the state into a slice of bytes.
109 pub fn xorBytes(self: *Self, out: []u8, in: []const u8) void {
110 assert(out.len == in.len);
111
112 var i: usize = 0;
113 while (i + @sizeOf(T) <= in.len) : (i += @sizeOf(T)) {
114 const x = mem.readIntNative(T, in[i..][0..@sizeOf(T)]) ^ mem.nativeToLittle(T, self.st[i / @sizeOf(T)]);
115 mem.writeIntNative(T, out[i..][0..@sizeOf(T)], x);
116 }
117 if (i < in.len) {
118 var padded = [_]u8{0} ** @sizeOf(T);
119 mem.copy(u8, padded[0 .. in.len - i], in[i..]);
120 const x = mem.readIntNative(T, &padded) ^ mem.nativeToLittle(T, self.st[i / @sizeOf(T)]);
121 mem.writeIntNative(T, &padded, x);
122 mem.copy(u8, out[i..], padded[0 .. in.len - i]);
123 }
124 }
125
126 /// Set the words storing the bytes of a given range to zero.
127 pub fn clear(self: *Self, from: usize, to: usize) void {
128 mem.set(T, self.st[from / @sizeOf(T) .. (to + @sizeOf(T) - 1) / @sizeOf(T)], 0);
129 }
130
131 /// Clear the entire state, disabling compiler optimizations.
132 pub fn secureZero(self: *Self) void {
133 std.crypto.utils.secureZero(T, &self.st);
134 }
135
136 inline fn round(self: *Self, rc: T) void {
137 const st = &self.st;
138
139 // theta
140 var t = [_]T{0} ** 5;
141 inline for (0..5) |i| {
142 inline for (0..5) |j| {
143 t[i] ^= st[j * 5 + i];
144 }
145 }
146 inline for (0..5) |i| {
147 inline for (0..5) |j| {
148 st[j * 5 + i] ^= t[(i + 4) % 5] ^ math.rotl(T, t[(i + 1) % 5], 1);
149 }
150 }
151
152 // rho+pi
153 var last = st[1];
154 comptime var rotc = 0;
155 inline for (0..24) |i| {
156 const x = PI[i];
157 const tmp = st[x];
158 rotc = (rotc + i + 1) % @bitSizeOf(T);
159 st[x] = math.rotl(T, last, rotc);
160 last = tmp;
161 }
162 inline for (0..5) |i| {
163 inline for (0..5) |j| {
164 t[j] = st[i * 5 + j];
165 }
166 inline for (0..5) |j| {
167 st[i * 5 + j] = t[j] ^ (~t[(j + 1) % 5] & t[(j + 2) % 5]);
168 }
169 }
170
171 // iota
172 st[0] ^= rc;
173 }
174
175 /// Apply a (possibly) reduced-round permutation to the state.
176 pub fn permuteR(self: *Self, comptime rounds: u5) void {
177 var i = RC.len - rounds;
178 while (i < rounds - rounds % 3) : (i += 3) {
179 self.round(RC[i]);
180 self.round(RC[i + 1]);
181 self.round(RC[i + 2]);
182 }
183 while (i < rounds) : (i += 1) {
184 self.round(RC[i]);
185 }
186 }
187
188 /// Apply a full-round permutation to the state.
189 pub fn permute(self: *Self) void {
190 self.permuteR(max_rounds);
191 }
192 };
193}
194
195/// A generic Keccak-P state.
196pub fn State(comptime f: u11, comptime capacity: u11, comptime delim: u8, comptime rounds: u5) type {
197 comptime assert(f > 200 and f <= 1600 and f % 200 == 0); // invalid state size
198 comptime assert(capacity < f and capacity % 8 == 0); // invalid capacity size
199
200 return struct {
201 const Self = @This();
202
203 /// The block length, or rate, in bytes.
204 pub const rate = KeccakF(f).block_bytes - capacity / 8;
205 /// Keccak does not have any options.
206 pub const Options = struct {};
207
208 offset: usize = 0,
209 buf: [rate]u8 = undefined,
210
211 st: KeccakF(f) = .{},
212
213 /// Absorb a slice of bytes into the sponge.
214 pub fn absorb(self: *Self, bytes_: []const u8) void {
215 var bytes = bytes_;
216 if (self.offset > 0) {
217 const left = math.min(rate - self.offset, bytes.len);
218 mem.copy(u8, self.buf[self.offset..], bytes[0..left]);
219 self.offset += left;
220 if (self.offset == rate) {
221 self.offset = 0;
222 self.st.addBytes(self.buf[0..]);
223 self.st.permuteR(rounds);
224 }
225 if (left == bytes.len) return;
226 bytes = bytes[left..];
227 }
228 while (bytes.len >= rate) {
229 self.st.addBytes(bytes[0..rate]);
230 self.st.permuteR(rounds);
231 bytes = bytes[rate..];
232 }
233 if (bytes.len > 0) {
234 self.st.addBytes(bytes[0..]);
235 self.offset = bytes.len;
236 }
237 }
238
239 /// Mark the end of the input.
240 pub fn pad(self: *Self) void {
241 self.st.addBytes(self.buf[0..self.offset]);
242 self.st.addByte(delim, self.offset);
243 self.st.addByte(0x80, rate - 1);
244 self.st.permuteR(rounds);
245 self.offset = 0;
246 }
247
248 /// Squeeze a slice of bytes from the sponge.
249 pub fn squeeze(self: *Self, out: []u8) void {
250 var i: usize = 0;
251 while (i < out.len) : (i += rate) {
252 const left = math.min(rate, out.len - i);
253 self.st.extractBytes(out[i..][0..left]);
254 self.st.permuteR(rounds);
255 }
256 }
257 };
258}
259
260test "Keccak-f800" {
261 var st: KeccakF(800) = .{
262 .st = .{
263 0xE531D45D, 0xF404C6FB, 0x23A0BF99, 0xF1F8452F, 0x51FFD042, 0xE539F578, 0xF00B80A7,
264 0xAF973664, 0xBF5AF34C, 0x227A2424, 0x88172715, 0x9F685884, 0xB15CD054, 0x1BF4FC0E,
265 0x6166FA91, 0x1A9E599A, 0xA3970A1F, 0xAB659687, 0xAFAB8D68, 0xE74B1015, 0x34001A98,
266 0x4119EFF3, 0x930A0E76, 0x87B28070, 0x11EFE996,
267 },
268 };
269 st.permute();
270 const expected: [25]u32 = .{
271 0x75BF2D0D, 0x9B610E89, 0xC826AF40, 0x64CD84AB, 0xF905BDD6, 0xBC832835, 0x5F8001B9,
272 0x15662CCE, 0x8E38C95E, 0x701FE543, 0x1B544380, 0x89ACDEFF, 0x51EDB5DE, 0x0E9702D9,
273 0x6C19AA16, 0xA2913EEE, 0x60754E9A, 0x9819063C, 0xF4709254, 0xD09F9084, 0x772DA259,
274 0x1DB35DF7, 0x5AA60162, 0x358825D5, 0xB3783BAB,
275 };
276 try std.testing.expectEqualSlices(u32, &st.st, &expected);
277}
lib/std/crypto/sha3.zig+164-135
......@@ -1,84 +1,63 @@
1const std = @import("../std.zig");
2const mem = std.mem;
1const std = @import("std");
2const assert = std.debug.assert;
33const math = std.math;
4const debug = std.debug;
5const htest = @import("test.zig");
4const mem = std.mem;
5
6const KeccakState = std.crypto.core.keccak.State;
7
8pub const Sha3_224 = Keccak(1600, 224, 0x06, 24);
9pub const Sha3_256 = Keccak(1600, 256, 0x06, 24);
10pub const Sha3_384 = Keccak(1600, 384, 0x06, 24);
11pub const Sha3_512 = Keccak(1600, 512, 0x06, 24);
12
13pub const Keccak256 = Keccak(1600, 256, 0x01, 24);
14pub const Keccak512 = Keccak(1600, 512, 0x01, 24);
15pub const Keccak_256 = @compileError("Deprecated: use `Keccak256` instead");
16pub const Keccak_512 = @compileError("Deprecated: use `Keccak512` instead");
17
18pub const Shake128 = Shake(128);
19pub const Shake256 = Shake(256);
20
21/// A generic Keccak hash function.
22pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime delim: u8, comptime rounds: u5) type {
23 comptime assert(output_bits > 0 and output_bits * 2 < f and output_bits % 8 == 0); // invalid output length
624
7pub const Sha3_224 = Keccak(224, 0x06);
8pub const Sha3_256 = Keccak(256, 0x06);
9pub const Sha3_384 = Keccak(384, 0x06);
10pub const Sha3_512 = Keccak(512, 0x06);
11pub const Keccak_256 = Keccak(256, 0x01);
12pub const Keccak_512 = Keccak(512, 0x01);
25 const State = KeccakState(f, output_bits * 2, delim, rounds);
1326
14fn Keccak(comptime bits: usize, comptime delim: u8) type {
1527 return struct {
1628 const Self = @This();
29
30 st: State = .{},
31
1732 /// The output length, in bytes.
18 pub const digest_length = bits / 8;
33 pub const digest_length = output_bits / 8;
1934 /// The block length, or rate, in bytes.
20 pub const block_length = 200 - bits / 4;
35 pub const block_length = State.rate;
2136 /// Keccak does not have any options.
2237 pub const Options = struct {};
2338
24 s: [200]u8,
25 offset: usize,
26
39 /// Initialize a Keccak hash function.
2740 pub fn init(options: Options) Self {
2841 _ = options;
29 return Self{ .s = [_]u8{0} ** 200, .offset = 0 };
42 return Self{};
3043 }
3144
32 pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void {
33 var d = Self.init(options);
34 d.update(b);
35 d.final(out);
45 /// Hash a slice of bytes.
46 pub fn hash(bytes: []const u8, out: *[digest_length]u8, options: Options) void {
47 var st = Self.init(options);
48 st.update(bytes);
49 st.final(out);
3650 }
3751
38 pub fn update(d: *Self, b: []const u8) void {
39 var ip: usize = 0;
40 var len = b.len;
41 var rate = block_length - d.offset;
42 var offset = d.offset;
43
44 // absorb
45 while (len >= rate) {
46 for (d.s[offset .. offset + rate], 0..) |*r, i|
47 r.* ^= b[ip..][i];
48
49 keccakF(1600, &d.s);
50
51 ip += rate;
52 len -= rate;
53 rate = block_length;
54 offset = 0;
55 }
56
57 for (d.s[offset .. offset + len], 0..) |*r, i|
58 r.* ^= b[ip..][i];
59
60 d.offset = offset + len;
52 /// Absorb a slice of bytes into the state.
53 pub fn update(self: *Self, bytes: []const u8) void {
54 self.st.absorb(bytes);
6155 }
6256
63 pub fn final(d: *Self, out: *[digest_length]u8) void {
64 // padding
65 d.s[d.offset] ^= delim;
66 d.s[block_length - 1] ^= 0x80;
67
68 keccakF(1600, &d.s);
69
70 // squeeze
71 var op: usize = 0;
72 var len: usize = bits / 8;
73
74 while (len >= block_length) {
75 mem.copy(u8, out[op..], d.s[0..block_length]);
76 keccakF(1600, &d.s);
77 op += block_length;
78 len -= block_length;
79 }
80
81 mem.copy(u8, out[op..], d.s[0..len]);
57 /// Return the hash of the absorbed bytes.
58 pub fn final(self: *Self, out: *[digest_length]u8) void {
59 self.st.pad();
60 self.st.squeeze(out[0..]);
8261 }
8362
8463 pub const Error = error{};
......@@ -95,87 +74,101 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type {
9574 };
9675}
9776
98const RC = [_]u64{
99 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
100 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
101 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
102 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
103 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
104 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
105};
106
107const ROTC = [_]usize{
108 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
109};
110
111const PIL = [_]usize{
112 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
113};
114
115const M5 = [_]usize{
116 0, 1, 2, 3, 4, 0, 1, 2, 3, 4,
117};
118
119fn keccakF(comptime F: usize, d: *[F / 8]u8) void {
120 const B = F / 25;
121 const no_rounds = comptime x: {
122 break :x 12 + 2 * math.log2(B);
123 };
77/// The SHAKE extendable output hash function.
78pub fn Shake(comptime security_level: u11) type {
79 const f = 1600;
80 const rounds = 24;
81 const State = KeccakState(f, security_level * 2, 0x1f, rounds);
12482
125 var s = [_]u64{0} ** 25;
126 var t = [_]u64{0} ** 1;
127 var c = [_]u64{0} ** 5;
83 return struct {
84 const Self = @This();
12885
129 for (&s, 0..) |*r, i| {
130 r.* = mem.readIntLittle(u64, d[8 * i ..][0..8]);
131 }
86 st: State = .{},
87 buf: [State.rate]u8 = undefined,
88 offset: usize = 0,
89 padded: bool = false,
13290
133 for (RC[0..no_rounds]) |round| {
134 // theta
135 comptime var x: usize = 0;
136 inline while (x < 5) : (x += 1) {
137 c[x] = s[x] ^ s[x + 5] ^ s[x + 10] ^ s[x + 15] ^ s[x + 20];
91 /// The recommended output length, in bytes.
92 pub const digest_length = security_level / 2;
93 /// The block length, or rate, in bytes.
94 pub const block_length = State.rate;
95 /// Keccak does not have any options.
96 pub const Options = struct {};
97
98 /// Initialize a SHAKE extensible hash function.
99 pub fn init(options: Options) Self {
100 _ = options;
101 return Self{};
138102 }
139 x = 0;
140 inline while (x < 5) : (x += 1) {
141 t[0] = c[M5[x + 4]] ^ math.rotl(u64, c[M5[x + 1]], @as(usize, 1));
142 comptime var y: usize = 0;
143 inline while (y < 5) : (y += 1) {
144 s[x + y * 5] ^= t[0];
145 }
103
104 /// Hash a slice of bytes.
105 /// `out` can be any length.
106 pub fn hash(bytes: []const u8, out: []u8, options: Options) void {
107 var st = Self.init(options);
108 st.update(bytes);
109 st.squeeze(out);
146110 }
147111
148 // rho+pi
149 t[0] = s[1];
150 x = 0;
151 inline while (x < 24) : (x += 1) {
152 c[0] = s[PIL[x]];
153 s[PIL[x]] = math.rotl(u64, t[0], ROTC[x]);
154 t[0] = c[0];
112 /// Absorb a slice of bytes into the state.
113 pub fn update(self: *Self, bytes: []const u8) void {
114 self.st.absorb(bytes);
155115 }
156116
157 // chi
158 comptime var y: usize = 0;
159 inline while (y < 5) : (y += 1) {
160 x = 0;
161 inline while (x < 5) : (x += 1) {
162 c[x] = s[x + y * 5];
117 /// Squeeze a slice of bytes from the state.
118 /// `out` can be any length, and the function can be called multiple times.
119 pub fn squeeze(self: *Self, out_: []u8) void {
120 if (!self.padded) {
121 self.st.pad();
122 self.padded = true;
123 }
124 var out = out_;
125 if (self.offset > 0) {
126 const left = self.buf.len - self.offset;
127 if (left > 0) {
128 const n = math.min(left, out.len);
129 mem.copy(u8, out[0..n], self.buf[self.offset..][0..n]);
130 out = out[n..];
131 self.offset += n;
132 if (out.len == 0) {
133 return;
134 }
135 }
163136 }
164 x = 0;
165 inline while (x < 5) : (x += 1) {
166 s[x + y * 5] = c[x] ^ (~c[M5[x + 1]] & c[M5[x + 2]]);
137 const full_blocks = out[0 .. out.len - out.len % State.rate];
138 if (full_blocks.len > 0) {
139 self.st.squeeze(full_blocks);
140 out = out[full_blocks.len..];
141 }
142 if (out.len > 0) {
143 self.st.squeeze(self.buf[0..]);
144 mem.copy(u8, out[0..], self.buf[0..out.len]);
145 self.offset = out.len;
167146 }
168147 }
169148
170 // iota
171 s[0] ^= round;
172 }
149 /// Return the hash of the absorbed bytes.
150 /// `out` can be of any length, but the function must not be called multiple times (use `squeeze` for that purpose instead).
151 pub fn final(self: *Self, out: []u8) void {
152 self.squeeze(out);
153 self.st.st.clear(0, State.rate);
154 }
173155
174 for (s, 0..) |r, i| {
175 mem.writeIntLittle(u64, d[8 * i ..][0..8], r);
176 }
156 pub const Error = error{};
157 pub const Writer = std.io.Writer(*Self, Error, write);
158
159 fn write(self: *Self, bytes: []const u8) Error!usize {
160 self.update(bytes);
161 return bytes.len;
162 }
163
164 pub fn writer(self: *Self) Writer {
165 return .{ .context = self };
166 }
167 };
177168}
178169
170const htest = @import("test.zig");
171
179172test "sha3-224 single" {
180173 try htest.assertEqualHash(Sha3_224, "6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7", "");
181174 try htest.assertEqualHash(Sha3_224, "e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", "abc");
......@@ -309,13 +302,49 @@ test "sha3-512 aligned final" {
309302}
310303
311304test "keccak-256 single" {
312 try htest.assertEqualHash(Keccak_256, "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "");
313 try htest.assertEqualHash(Keccak_256, "4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45", "abc");
314 try htest.assertEqualHash(Keccak_256, "f519747ed599024f3882238e5ab43960132572b7345fbeb9a90769dafd21ad67", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
305 try htest.assertEqualHash(Keccak256, "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "");
306 try htest.assertEqualHash(Keccak256, "4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45", "abc");
307 try htest.assertEqualHash(Keccak256, "f519747ed599024f3882238e5ab43960132572b7345fbeb9a90769dafd21ad67", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
315308}
316309
317310test "keccak-512 single" {
318 try htest.assertEqualHash(Keccak_512, "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e", "");
319 try htest.assertEqualHash(Keccak_512, "18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96", "abc");
320 try htest.assertEqualHash(Keccak_512, "ac2fb35251825d3aa48468a9948c0a91b8256f6d97d8fa4160faff2dd9dfcc24f3f1db7a983dad13d53439ccac0b37e24037e7b95f80f59f37a2f683c4ba4682", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
311 try htest.assertEqualHash(Keccak512, "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e", "");
312 try htest.assertEqualHash(Keccak512, "18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96", "abc");
313 try htest.assertEqualHash(Keccak512, "ac2fb35251825d3aa48468a9948c0a91b8256f6d97d8fa4160faff2dd9dfcc24f3f1db7a983dad13d53439ccac0b37e24037e7b95f80f59f37a2f683c4ba4682", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
314}
315
316test "SHAKE-128 single" {
317 var out: [10]u8 = undefined;
318 Shake128.hash("hello123", &out, .{});
319 try htest.assertEqual("1b85861510bc4d8e467d", &out);
320}
321
322test "SHAKE-128 multisqueeze" {
323 var out: [10]u8 = undefined;
324 var h = Shake128.init(.{});
325 h.update("hello123");
326 h.squeeze(out[0..4]);
327 h.squeeze(out[4..]);
328 try htest.assertEqual("1b85861510bc4d8e467d", &out);
329}
330
331test "SHAKE-128 multisqueeze with multiple blocks" {
332 var out: [100]u8 = undefined;
333 var out2: [100]u8 = undefined;
334
335 var h = Shake128.init(.{});
336 h.update("hello123");
337 h.squeeze(out[0..50]);
338 h.squeeze(out[50..]);
339
340 var h2 = Shake128.init(.{});
341 h2.update("hello123");
342 h2.squeeze(&out2);
343 try std.testing.expectEqualSlices(u8, &out, &out2);
344}
345
346test "SHAKE-256 single" {
347 var out: [10]u8 = undefined;
348 Shake256.hash("hello123", &out, .{});
349 try htest.assertEqual("ade612ba265f92de4a37", &out);
321350}
lib/std/fs.zig+5
......@@ -11,6 +11,11 @@ const math = std.math;
1111
1212const is_darwin = builtin.os.tag.isDarwin();
1313
14pub const has_executable_bit = switch (builtin.os.tag) {
15 .windows, .wasi => false,
16 else => true,
17};
18
1419pub const path = @import("fs/path.zig");
1520pub const File = @import("fs/file.zig").File;
1621pub const wasi = @import("fs/wasi.zig");
lib/std/fs/file.zig+6
......@@ -48,6 +48,12 @@ pub const File = struct {
4848 Unknown,
4949 };
5050
51 /// This is the default mode given to POSIX operating systems for creating
52 /// files. `0o666` is "-rw-rw-rw-" which is counter-intuitive at first,
53 /// since most people would expect "-rw-r--r--", for example, when using
54 /// the `touch` command, which would correspond to `0o644`. However, POSIX
55 /// libc implementations use `0o666` inside `fopen` and then rely on the
56 /// process-scoped "umask" setting to adjust this number for file creation.
5157 pub const default_mode = switch (builtin.os.tag) {
5258 .windows => 0,
5359 .wasi => 0,
lib/std/hash_map.zig+5-4
......@@ -1595,16 +1595,17 @@ pub fn HashMapUnmanaged(
15951595 self.available = 0;
15961596 }
15971597
1598 /// This function is used in tools/zig-gdb.py to fetch the header type to facilitate
1599 /// fancy debug printing for this type.
1600 fn gdbHelper(self: *Self, hdr: *Header) void {
1598 /// This function is used in the debugger pretty formatters in tools/ to fetch the
1599 /// header type to facilitate fancy debug printing for this type.
1600 fn dbHelper(self: *Self, hdr: *Header, entry: *Entry) void {
16011601 _ = self;
16021602 _ = hdr;
1603 _ = entry;
16031604 }
16041605
16051606 comptime {
16061607 if (builtin.mode == .Debug) {
1607 _ = gdbHelper;
1608 _ = dbHelper;
16081609 }
16091610 }
16101611 };
lib/std/heap/general_purpose_allocator.zig+1
......@@ -423,6 +423,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
423423 }
424424 } else struct {};
425425
426 /// Returns true if there were leaks; false otherwise.
426427 pub fn deinit(self: *Self) bool {
427428 const leaks = if (config.safety) self.detectLeaks() else false;
428429 if (config.retain_metadata) {
lib/std/io.zig+250
......@@ -168,6 +168,256 @@ test "null_writer" {
168168 null_writer.writeAll("yay" ** 10) catch |err| switch (err) {};
169169}
170170
171pub fn poll(
172 allocator: std.mem.Allocator,
173 comptime StreamEnum: type,
174 files: PollFiles(StreamEnum),
175) Poller(StreamEnum) {
176 const enum_fields = @typeInfo(StreamEnum).Enum.fields;
177 var result: Poller(StreamEnum) = undefined;
178
179 if (builtin.os.tag == .windows) result.windows = .{
180 .first_read_done = false,
181 .overlapped = [1]os.windows.OVERLAPPED{
182 mem.zeroes(os.windows.OVERLAPPED),
183 } ** enum_fields.len,
184 .active = .{
185 .count = 0,
186 .handles_buf = undefined,
187 .stream_map = undefined,
188 },
189 };
190
191 inline for (0..enum_fields.len) |i| {
192 result.fifos[i] = .{
193 .allocator = allocator,
194 .buf = &.{},
195 .head = 0,
196 .count = 0,
197 };
198 if (builtin.os.tag == .windows) {
199 result.windows.active.handles_buf[i] = @field(files, enum_fields[i].name).handle;
200 } else {
201 result.poll_fds[i] = .{
202 .fd = @field(files, enum_fields[i].name).handle,
203 .events = os.POLL.IN,
204 .revents = undefined,
205 };
206 }
207 }
208 return result;
209}
210
211pub const PollFifo = std.fifo.LinearFifo(u8, .Dynamic);
212
213pub fn Poller(comptime StreamEnum: type) type {
214 return struct {
215 const enum_fields = @typeInfo(StreamEnum).Enum.fields;
216 const PollFd = if (builtin.os.tag == .windows) void else std.os.pollfd;
217
218 fifos: [enum_fields.len]PollFifo,
219 poll_fds: [enum_fields.len]PollFd,
220 windows: if (builtin.os.tag == .windows) struct {
221 first_read_done: bool,
222 overlapped: [enum_fields.len]os.windows.OVERLAPPED,
223 active: struct {
224 count: math.IntFittingRange(0, enum_fields.len),
225 handles_buf: [enum_fields.len]os.windows.HANDLE,
226 stream_map: [enum_fields.len]StreamEnum,
227
228 pub fn removeAt(self: *@This(), index: u32) void {
229 std.debug.assert(index < self.count);
230 for (index + 1..self.count) |i| {
231 self.handles_buf[i - 1] = self.handles_buf[i];
232 self.stream_map[i - 1] = self.stream_map[i];
233 }
234 self.count -= 1;
235 }
236 },
237 } else void,
238
239 const Self = @This();
240
241 pub fn deinit(self: *Self) void {
242 if (builtin.os.tag == .windows) {
243 // cancel any pending IO to prevent clobbering OVERLAPPED value
244 for (self.windows.active.handles_buf[0..self.windows.active.count]) |h| {
245 _ = os.windows.kernel32.CancelIo(h);
246 }
247 }
248 inline for (&self.fifos) |*q| q.deinit();
249 self.* = undefined;
250 }
251
252 pub fn poll(self: *Self) !bool {
253 if (builtin.os.tag == .windows) {
254 return pollWindows(self);
255 } else {
256 return pollPosix(self);
257 }
258 }
259
260 pub inline fn fifo(self: *Self, comptime which: StreamEnum) *PollFifo {
261 return &self.fifos[@enumToInt(which)];
262 }
263
264 fn pollWindows(self: *Self) !bool {
265 const bump_amt = 512;
266
267 if (!self.windows.first_read_done) {
268 // Windows Async IO requires an initial call to ReadFile before waiting on the handle
269 for (0..enum_fields.len) |i| {
270 const handle = self.windows.active.handles_buf[i];
271 switch (try windowsAsyncRead(
272 handle,
273 &self.windows.overlapped[i],
274 &self.fifos[i],
275 bump_amt,
276 )) {
277 .pending => {
278 self.windows.active.handles_buf[self.windows.active.count] = handle;
279 self.windows.active.stream_map[self.windows.active.count] = @intToEnum(StreamEnum, i);
280 self.windows.active.count += 1;
281 },
282 .closed => {}, // don't add to the wait_objects list
283 }
284 }
285 self.windows.first_read_done = true;
286 }
287
288 while (true) {
289 if (self.windows.active.count == 0) return false;
290
291 const status = os.windows.kernel32.WaitForMultipleObjects(
292 self.windows.active.count,
293 &self.windows.active.handles_buf,
294 0,
295 os.windows.INFINITE,
296 );
297 if (status == os.windows.WAIT_FAILED)
298 return os.windows.unexpectedError(os.windows.kernel32.GetLastError());
299
300 if (status < os.windows.WAIT_OBJECT_0 or status > os.windows.WAIT_OBJECT_0 + enum_fields.len - 1)
301 unreachable;
302
303 const active_idx = status - os.windows.WAIT_OBJECT_0;
304
305 const handle = self.windows.active.handles_buf[active_idx];
306 const stream_idx = @enumToInt(self.windows.active.stream_map[active_idx]);
307 var read_bytes: u32 = undefined;
308 if (0 == os.windows.kernel32.GetOverlappedResult(
309 handle,
310 &self.windows.overlapped[stream_idx],
311 &read_bytes,
312 0,
313 )) switch (os.windows.kernel32.GetLastError()) {
314 .BROKEN_PIPE => {
315 self.windows.active.removeAt(active_idx);
316 continue;
317 },
318 else => |err| return os.windows.unexpectedError(err),
319 };
320
321 self.fifos[stream_idx].update(read_bytes);
322
323 switch (try windowsAsyncRead(
324 handle,
325 &self.windows.overlapped[stream_idx],
326 &self.fifos[stream_idx],
327 bump_amt,
328 )) {
329 .pending => {},
330 .closed => self.windows.active.removeAt(active_idx),
331 }
332 return true;
333 }
334 }
335
336 fn pollPosix(self: *Self) !bool {
337 // We ask for ensureUnusedCapacity with this much extra space. This
338 // has more of an effect on small reads because once the reads
339 // start to get larger the amount of space an ArrayList will
340 // allocate grows exponentially.
341 const bump_amt = 512;
342
343 const err_mask = os.POLL.ERR | os.POLL.NVAL | os.POLL.HUP;
344
345 const events_len = try os.poll(&self.poll_fds, std.math.maxInt(i32));
346 if (events_len == 0) {
347 for (self.poll_fds) |poll_fd| {
348 if (poll_fd.fd != -1) return true;
349 } else return false;
350 }
351
352 var keep_polling = false;
353 inline for (&self.poll_fds, &self.fifos) |*poll_fd, *q| {
354 // Try reading whatever is available before checking the error
355 // conditions.
356 // It's still possible to read after a POLL.HUP is received,
357 // always check if there's some data waiting to be read first.
358 if (poll_fd.revents & os.POLL.IN != 0) {
359 const buf = try q.writableWithSize(bump_amt);
360 const amt = try os.read(poll_fd.fd, buf);
361 q.update(amt);
362 if (amt == 0) {
363 // Remove the fd when the EOF condition is met.
364 poll_fd.fd = -1;
365 } else {
366 keep_polling = true;
367 }
368 } else if (poll_fd.revents & err_mask != 0) {
369 // Exclude the fds that signaled an error.
370 poll_fd.fd = -1;
371 } else if (poll_fd.fd != -1) {
372 keep_polling = true;
373 }
374 }
375 return keep_polling;
376 }
377 };
378}
379
380fn windowsAsyncRead(
381 handle: os.windows.HANDLE,
382 overlapped: *os.windows.OVERLAPPED,
383 fifo: *PollFifo,
384 bump_amt: usize,
385) !enum { pending, closed } {
386 while (true) {
387 const buf = try fifo.writableWithSize(bump_amt);
388 var read_bytes: u32 = undefined;
389 const read_result = os.windows.kernel32.ReadFile(handle, buf.ptr, math.cast(u32, buf.len) orelse math.maxInt(u32), &read_bytes, overlapped);
390 if (read_result == 0) return switch (os.windows.kernel32.GetLastError()) {
391 .IO_PENDING => .pending,
392 .BROKEN_PIPE => .closed,
393 else => |err| os.windows.unexpectedError(err),
394 };
395 fifo.update(read_bytes);
396 }
397}
398
399/// Given an enum, returns a struct with fields of that enum, each field
400/// representing an I/O stream for polling.
401pub fn PollFiles(comptime StreamEnum: type) type {
402 const enum_fields = @typeInfo(StreamEnum).Enum.fields;
403 var struct_fields: [enum_fields.len]std.builtin.Type.StructField = undefined;
404 for (&struct_fields, enum_fields) |*struct_field, enum_field| {
405 struct_field.* = .{
406 .name = enum_field.name,
407 .type = fs.File,
408 .default_value = null,
409 .is_comptime = false,
410 .alignment = @alignOf(fs.File),
411 };
412 }
413 return @Type(.{ .Struct = .{
414 .layout = .Auto,
415 .fields = &struct_fields,
416 .decls = &.{},
417 .is_tuple = false,
418 } });
419}
420
171421test {
172422 _ = @import("io/bit_reader.zig");
173423 _ = @import("io/bit_writer.zig");
lib/std/multi_array_list.zig+23-6
......@@ -131,8 +131,8 @@ pub fn MultiArrayList(comptime S: type) type {
131131 .capacity = self.capacity,
132132 };
133133 var ptr: [*]u8 = self.bytes;
134 for (sizes.bytes, 0..) |field_size, i| {
135 result.ptrs[sizes.fields[i]] = ptr;
134 for (sizes.bytes, sizes.fields) |field_size, i| {
135 result.ptrs[i] = ptr;
136136 ptr += field_size * self.capacity;
137137 }
138138 return result;
......@@ -446,16 +446,33 @@ pub fn MultiArrayList(comptime S: type) type {
446446 return meta.fieldInfo(S, field).type;
447447 }
448448
449 /// This function is used in tools/zig-gdb.py to fetch the child type to facilitate
450 /// fancy debug printing for this type.
451 fn gdbHelper(self: *Self, child: *S) void {
449 const Entry = entry: {
450 var entry_fields: [fields.len]std.builtin.Type.StructField = undefined;
451 for (&entry_fields, sizes.fields) |*entry_field, i| entry_field.* = .{
452 .name = fields[i].name ++ "_ptr",
453 .type = *fields[i].type,
454 .default_value = null,
455 .is_comptime = fields[i].is_comptime,
456 .alignment = fields[i].alignment,
457 };
458 break :entry @Type(.{ .Struct = .{
459 .layout = .Extern,
460 .fields = &entry_fields,
461 .decls = &.{},
462 .is_tuple = false,
463 } });
464 };
465 /// This function is used in the debugger pretty formatters in tools/ to fetch the
466 /// child type to facilitate fancy debug printing for this type.
467 fn dbHelper(self: *Self, child: *S, entry: *Entry) void {
452468 _ = self;
453469 _ = child;
470 _ = entry;
454471 }
455472
456473 comptime {
457474 if (builtin.mode == .Debug) {
458 _ = gdbHelper;
475 _ = dbHelper;
459476 }
460477 }
461478 };
lib/std/os.zig+32-3
......@@ -302,8 +302,7 @@ pub const FChmodError = error{
302302/// successfully, or must have the effective user ID matching the owner
303303/// of the file.
304304pub fn fchmod(fd: fd_t, mode: mode_t) FChmodError!void {
305 if (builtin.os.tag == .windows or builtin.os.tag == .wasi)
306 @compileError("Unsupported OS");
305 if (!std.fs.has_executable_bit) @compileError("fchmod unsupported by target OS");
307306
308307 while (true) {
309308 const res = system.fchmod(fd, mode);
......@@ -311,8 +310,38 @@ pub fn fchmod(fd: fd_t, mode: mode_t) FChmodError!void {
311310 switch (system.getErrno(res)) {
312311 .SUCCESS => return,
313312 .INTR => continue,
314 .BADF => unreachable, // Can be reached if the fd refers to a non-iterable directory.
313 .BADF => unreachable,
314 .FAULT => unreachable,
315 .INVAL => unreachable,
316 .ACCES => return error.AccessDenied,
317 .IO => return error.InputOutput,
318 .LOOP => return error.SymLinkLoop,
319 .NOENT => return error.FileNotFound,
320 .NOMEM => return error.SystemResources,
321 .NOTDIR => return error.FileNotFound,
322 .PERM => return error.AccessDenied,
323 .ROFS => return error.ReadOnlyFileSystem,
324 else => |err| return unexpectedErrno(err),
325 }
326 }
327}
328
329const FChmodAtError = FChmodError || error{
330 NameTooLong,
331};
315332
333pub fn fchmodat(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtError!void {
334 if (!std.fs.has_executable_bit) @compileError("fchmodat unsupported by target OS");
335
336 const path_c = try toPosixPath(path);
337
338 while (true) {
339 const res = system.fchmodat(dirfd, &path_c, mode, flags);
340
341 switch (system.getErrno(res)) {
342 .SUCCESS => return,
343 .INTR => continue,
344 .BADF => unreachable,
316345 .FAULT => unreachable,
317346 .INVAL => unreachable,
318347 .ACCES => return error.AccessDenied,
lib/std/os/linux.zig+18
......@@ -769,6 +769,20 @@ pub fn fchmod(fd: i32, mode: mode_t) usize {
769769 return syscall2(.fchmod, @bitCast(usize, @as(isize, fd)), mode);
770770}
771771
772pub fn chmod(path: [*:0]const u8, mode: mode_t) usize {
773 if (@hasField(SYS, "chmod")) {
774 return syscall2(.chmod, @ptrToInt(path), mode);
775 } else {
776 return syscall4(
777 .fchmodat,
778 @bitCast(usize, @as(isize, AT.FDCWD)),
779 @ptrToInt(path),
780 mode,
781 0,
782 );
783 }
784}
785
772786pub fn fchown(fd: i32, owner: uid_t, group: gid_t) usize {
773787 if (@hasField(SYS, "fchown32")) {
774788 return syscall3(.fchown32, @bitCast(usize, @as(isize, fd)), owner, group);
......@@ -777,6 +791,10 @@ pub fn fchown(fd: i32, owner: uid_t, group: gid_t) usize {
777791 }
778792}
779793
794pub fn fchmodat(fd: i32, path: [*:0]const u8, mode: mode_t, flags: u32) usize {
795 return syscall4(.fchmodat, @bitCast(usize, @as(isize, fd)), @ptrToInt(path), mode, flags);
796}
797
780798/// Can only be called on 32 bit systems. For 64 bit see `lseek`.
781799pub fn llseek(fd: i32, offset: u64, result: ?*u64, whence: usize) usize {
782800 // NOTE: The offset parameter splitting is independent from the target
lib/std/os/test.zig+43-29
......@@ -531,17 +531,17 @@ test "memfd_create" {
531531 else => return error.SkipZigTest,
532532 }
533533
534 const fd = std.os.memfd_create("test", 0) catch |err| switch (err) {
534 const fd = os.memfd_create("test", 0) catch |err| switch (err) {
535535 // Related: https://github.com/ziglang/zig/issues/4019
536536 error.SystemOutdated => return error.SkipZigTest,
537537 else => |e| return e,
538538 };
539 defer std.os.close(fd);
540 try expect((try std.os.write(fd, "test")) == 4);
541 try std.os.lseek_SET(fd, 0);
539 defer os.close(fd);
540 try expect((try os.write(fd, "test")) == 4);
541 try os.lseek_SET(fd, 0);
542542
543543 var buf: [10]u8 = undefined;
544 const bytes_read = try std.os.read(fd, &buf);
544 const bytes_read = try os.read(fd, &buf);
545545 try expect(bytes_read == 4);
546546 try expect(mem.eql(u8, buf[0..4], "test"));
547547}
......@@ -688,7 +688,7 @@ test "signalfd" {
688688 .linux, .solaris => {},
689689 else => return error.SkipZigTest,
690690 }
691 _ = std.os.signalfd;
691 _ = os.signalfd;
692692}
693693
694694test "sync" {
......@@ -757,11 +757,11 @@ test "shutdown socket" {
757757 if (native_os == .wasi)
758758 return error.SkipZigTest;
759759 if (native_os == .windows) {
760 _ = try std.os.windows.WSAStartup(2, 2);
760 _ = try os.windows.WSAStartup(2, 2);
761761 }
762762 defer {
763763 if (native_os == .windows) {
764 std.os.windows.WSACleanup() catch unreachable;
764 os.windows.WSACleanup() catch unreachable;
765765 }
766766 }
767767 const sock = try os.socket(os.AF.INET, os.SOCK.STREAM, 0);
......@@ -855,13 +855,13 @@ test "dup & dup2" {
855855 var file = try tmp.dir.createFile("os_dup_test", .{});
856856 defer file.close();
857857
858 var duped = std.fs.File{ .handle = try std.os.dup(file.handle) };
858 var duped = std.fs.File{ .handle = try os.dup(file.handle) };
859859 defer duped.close();
860860 try duped.writeAll("dup");
861861
862862 // Tests aren't run in parallel so using the next fd shouldn't be an issue.
863863 const new_fd = duped.handle + 1;
864 try std.os.dup2(file.handle, new_fd);
864 try os.dup2(file.handle, new_fd);
865865 var dup2ed = std.fs.File{ .handle = new_fd };
866866 defer dup2ed.close();
867867 try dup2ed.writeAll("dup2");
......@@ -909,46 +909,46 @@ test "POSIX file locking with fcntl" {
909909 const fd = file.handle;
910910
911911 // Place an exclusive lock on the first byte, and a shared lock on the second byte:
912 var struct_flock = std.mem.zeroInit(std.os.Flock, .{ .type = std.os.F.WRLCK });
913 _ = try std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock));
912 var struct_flock = std.mem.zeroInit(os.Flock, .{ .type = os.F.WRLCK });
913 _ = try os.fcntl(fd, os.F.SETLK, @ptrToInt(&struct_flock));
914914 struct_flock.start = 1;
915 struct_flock.type = std.os.F.RDLCK;
916 _ = try std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock));
915 struct_flock.type = os.F.RDLCK;
916 _ = try os.fcntl(fd, os.F.SETLK, @ptrToInt(&struct_flock));
917917
918918 // Check the locks in a child process:
919 const pid = try std.os.fork();
919 const pid = try os.fork();
920920 if (pid == 0) {
921921 // child expects be denied the exclusive lock:
922922 struct_flock.start = 0;
923 struct_flock.type = std.os.F.WRLCK;
924 try expectError(error.Locked, std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock)));
923 struct_flock.type = os.F.WRLCK;
924 try expectError(error.Locked, os.fcntl(fd, os.F.SETLK, @ptrToInt(&struct_flock)));
925925 // child expects to get the shared lock:
926926 struct_flock.start = 1;
927 struct_flock.type = std.os.F.RDLCK;
928 _ = try std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock));
927 struct_flock.type = os.F.RDLCK;
928 _ = try os.fcntl(fd, os.F.SETLK, @ptrToInt(&struct_flock));
929929 // child waits for the exclusive lock in order to test deadlock:
930930 struct_flock.start = 0;
931 struct_flock.type = std.os.F.WRLCK;
932 _ = try std.os.fcntl(fd, std.os.F.SETLKW, @ptrToInt(&struct_flock));
931 struct_flock.type = os.F.WRLCK;
932 _ = try os.fcntl(fd, os.F.SETLKW, @ptrToInt(&struct_flock));
933933 // child exits without continuing:
934 std.os.exit(0);
934 os.exit(0);
935935 } else {
936936 // parent waits for child to get shared lock:
937937 std.time.sleep(1 * std.time.ns_per_ms);
938938 // parent expects deadlock when attempting to upgrade the shared lock to exclusive:
939939 struct_flock.start = 1;
940 struct_flock.type = std.os.F.WRLCK;
941 try expectError(error.DeadLock, std.os.fcntl(fd, std.os.F.SETLKW, @ptrToInt(&struct_flock)));
940 struct_flock.type = os.F.WRLCK;
941 try expectError(error.DeadLock, os.fcntl(fd, os.F.SETLKW, @ptrToInt(&struct_flock)));
942942 // parent releases exclusive lock:
943943 struct_flock.start = 0;
944 struct_flock.type = std.os.F.UNLCK;
945 _ = try std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock));
944 struct_flock.type = os.F.UNLCK;
945 _ = try os.fcntl(fd, os.F.SETLK, @ptrToInt(&struct_flock));
946946 // parent releases shared lock:
947947 struct_flock.start = 1;
948 struct_flock.type = std.os.F.UNLCK;
949 _ = try std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock));
948 struct_flock.type = os.F.UNLCK;
949 _ = try os.fcntl(fd, os.F.SETLK, @ptrToInt(&struct_flock));
950950 // parent waits for child:
951 const result = std.os.waitpid(pid, 0);
951 const result = os.waitpid(pid, 0);
952952 try expect(result.status == 0 * 256);
953953 }
954954}
......@@ -1182,3 +1182,17 @@ test "pwrite with empty buffer" {
11821182
11831183 _ = try os.pwrite(file.handle, bytes, 0);
11841184}
1185
1186test "fchmodat smoke test" {
1187 if (!std.fs.has_executable_bit) return error.SkipZigTest;
1188
1189 var tmp = tmpDir(.{});
1190 defer tmp.cleanup();
1191
1192 try expectError(error.FileNotFound, os.fchmodat(tmp.dir.fd, "foo.txt", 0o666, 0));
1193 const fd = try os.openat(tmp.dir.fd, "foo.txt", os.O.RDWR | os.O.CREAT | os.O.EXCL, 0o666);
1194 os.close(fd);
1195 try os.fchmodat(tmp.dir.fd, "foo.txt", 0o755, 0);
1196 const st = try os.fstatat(tmp.dir.fd, "foo.txt", 0);
1197 try expectEqual(@as(os.mode_t, 0o755), st.mode & 0b111_111_111);
1198}
lib/std/os/windows.zig+1-1
......@@ -2068,7 +2068,7 @@ pub fn loadWinsockExtensionFunction(comptime T: type, sock: ws2_32.SOCKET, guid:
20682068 ws2_32.SIO_GET_EXTENSION_FUNCTION_POINTER,
20692069 @ptrCast(*const anyopaque, &guid),
20702070 @sizeOf(GUID),
2071 @intToPtr(?*anyopaque, @ptrToInt(function)),
2071 @intToPtr(?*anyopaque, @ptrToInt(&function)),
20722072 @sizeOf(T),
20732073 &num_bytes,
20742074 null,
lib/std/os/windows/test.zig+25
......@@ -63,3 +63,28 @@ test "removeDotDirs" {
6363 try testRemoveDotDirs("a\\b\\..\\", "a\\");
6464 try testRemoveDotDirs("a\\b\\..\\c", "a\\c");
6565}
66
67test "loadWinsockExtensionFunction" {
68 _ = try windows.WSAStartup(2, 2);
69 defer windows.WSACleanup() catch unreachable;
70
71 const LPFN_CONNECTEX = *const fn (
72 Socket: windows.ws2_32.SOCKET,
73 SockAddr: *const windows.ws2_32.sockaddr,
74 SockLen: std.os.socklen_t,
75 SendBuf: ?*const anyopaque,
76 SendBufLen: windows.DWORD,
77 BytesSent: *windows.DWORD,
78 Overlapped: *windows.OVERLAPPED,
79 ) callconv(windows.WINAPI) windows.BOOL;
80
81 _ = windows.loadWinsockExtensionFunction(
82 LPFN_CONNECTEX,
83 try std.os.socket(std.os.AF.INET, std.os.SOCK.DGRAM, 0),
84 windows.ws2_32.WSAID_CONNECTEX,
85 ) catch |err| switch (err) {
86 error.OperationNotSupported => unreachable,
87 error.ShortRead => unreachable,
88 else => |e| return e,
89 };
90}
lib/std/process.zig+1
......@@ -9,6 +9,7 @@ const assert = std.debug.assert;
99const testing = std.testing;
1010const child_process = @import("child_process.zig");
1111
12pub const Child = child_process.ChildProcess;
1213pub const abort = os.abort;
1314pub const exit = os.exit;
1415pub const changeCurDir = os.chdir;
lib/std/std.zig+1
......@@ -12,6 +12,7 @@ pub const BoundedArray = @import("bounded_array.zig").BoundedArray;
1212pub const Build = @import("Build.zig");
1313pub const BufMap = @import("buf_map.zig").BufMap;
1414pub const BufSet = @import("buf_set.zig").BufSet;
15/// Deprecated: use `process.Child`.
1516pub const ChildProcess = @import("child_process.zig").ChildProcess;
1617pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringMap;
1718pub const DynLib = @import("dynamic_library.zig").DynLib;
src/AstGen.zig+2-2
......@@ -2342,10 +2342,10 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
23422342
23432343 .while_simple,
23442344 .while_cont,
2345 .@"while", => _ = try whileExpr(gz, scope, .{ .rl = .discard }, inner_node, tree.fullWhile(inner_node).?, true),
2345 .@"while", => _ = try whileExpr(gz, scope, .{ .rl = .none }, inner_node, tree.fullWhile(inner_node).?, true),
23462346
23472347 .for_simple,
2348 .@"for", => _ = try forExpr(gz, scope, .{ .rl = .discard }, inner_node, tree.fullFor(inner_node).?, true),
2348 .@"for", => _ = try forExpr(gz, scope, .{ .rl = .none }, inner_node, tree.fullFor(inner_node).?, true),
23492349
23502350 else => noreturn_src_node = try unusedResultExpr(gz, scope, inner_node),
23512351 // zig fmt: on
src/Package.zig+26-8
......@@ -225,6 +225,7 @@ pub fn fetchAndAddDependencies(
225225 build_roots_source: *std.ArrayList(u8),
226226 name_prefix: []const u8,
227227 color: main.Color,
228 all_modules: *AllModules,
228229) !void {
229230 const max_bytes = 10 * 1024 * 1024;
230231 const gpa = thread_pool.allocator;
......@@ -291,6 +292,7 @@ pub fn fetchAndAddDependencies(
291292 report,
292293 build_roots_source,
293294 fqn,
295 all_modules,
294296 );
295297
296298 try pkg.fetchAndAddDependencies(
......@@ -304,6 +306,7 @@ pub fn fetchAndAddDependencies(
304306 build_roots_source,
305307 sub_prefix,
306308 color,
309 all_modules,
307310 );
308311
309312 try add(pkg, gpa, fqn, sub_pkg);
......@@ -402,6 +405,11 @@ const Report = struct {
402405 }
403406};
404407
408const hex_multihash_len = 2 * Manifest.multihash_len;
409const MultiHashHexDigest = [hex_multihash_len]u8;
410/// This is to avoid creating multiple modules for the same build.zig file.
411pub const AllModules = std.AutoHashMapUnmanaged(MultiHashHexDigest, *Package);
412
405413fn fetchAndUnpack(
406414 thread_pool: *ThreadPool,
407415 http_client: *std.http.Client,
......@@ -410,6 +418,7 @@ fn fetchAndUnpack(
410418 report: Report,
411419 build_roots_source: *std.ArrayList(u8),
412420 fqn: []const u8,
421 all_modules: *AllModules,
413422) !*Package {
414423 const gpa = http_client.allocator;
415424 const s = fs.path.sep_str;
......@@ -417,9 +426,24 @@ fn fetchAndUnpack(
417426 // Check if the expected_hash is already present in the global package
418427 // cache, and thereby avoid both fetching and unpacking.
419428 if (dep.hash) |h| cached: {
420 const hex_multihash_len = 2 * Manifest.multihash_len;
421429 const hex_digest = h[0..hex_multihash_len];
422430 const pkg_dir_sub_path = "p" ++ s ++ hex_digest;
431
432 const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path});
433 errdefer gpa.free(build_root);
434
435 try build_roots_source.writer().print(" pub const {s} = \"{}\";\n", .{
436 std.zig.fmtId(fqn), std.zig.fmtEscapes(build_root),
437 });
438
439 // The compiler has a rule that a file must not be included in multiple modules,
440 // so we must detect if a module has been created for this package and reuse it.
441 const gop = try all_modules.getOrPut(gpa, hex_digest.*);
442 if (gop.found_existing) {
443 gpa.free(build_root);
444 return gop.value_ptr.*;
445 }
446
423447 var pkg_dir = global_cache_directory.handle.openDir(pkg_dir_sub_path, .{}) catch |err| switch (err) {
424448 error.FileNotFound => break :cached,
425449 else => |e| return e,
......@@ -432,13 +456,6 @@ fn fetchAndUnpack(
432456 const owned_src_path = try gpa.dupe(u8, build_zig_basename);
433457 errdefer gpa.free(owned_src_path);
434458
435 const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path});
436 errdefer gpa.free(build_root);
437
438 try build_roots_source.writer().print(" pub const {s} = \"{}\";\n", .{
439 std.zig.fmtId(fqn), std.zig.fmtEscapes(build_root),
440 });
441
442459 ptr.* = .{
443460 .root_src_directory = .{
444461 .path = build_root,
......@@ -448,6 +465,7 @@ fn fetchAndUnpack(
448465 .root_src_path = owned_src_path,
449466 };
450467
468 gop.value_ptr.* = ptr;
451469 return ptr;
452470 }
453471
src/Sema.zig+2-2
......@@ -17328,11 +17328,11 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
1732817328 break :blk abi_align;
1732917329 } else 0;
1733017330
17331 const address_space = if (inst_data.flags.has_addrspace) blk: {
17331 const address_space: std.builtin.AddressSpace = if (inst_data.flags.has_addrspace) blk: {
1733217332 const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]);
1733317333 extra_i += 1;
1733417334 break :blk try sema.analyzeAddressSpace(block, addrspace_src, ref, .pointer);
17335 } else .generic;
17335 } else if (elem_ty.zigTypeTag() == .Fn and target.cpu.arch == .avr) .flash else .generic;
1733617336
1733717337 const bit_offset = if (inst_data.flags.has_bit_range) blk: {
1733817338 const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]);
src/arch/aarch64/CodeGen.zig+4-2
......@@ -4177,8 +4177,10 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
41774177}
41784178
41794179fn airArg(self: *Self, inst: Air.Inst.Index) !void {
4180 const arg_index = self.arg_index;
4181 self.arg_index += 1;
4180 // skip zero-bit arguments as they don't have a corresponding arg instruction
4181 var arg_index = self.arg_index;
4182 while (self.args[arg_index] == .none) arg_index += 1;
4183 self.arg_index = arg_index + 1;
41824184
41834185 const ty = self.air.typeOfIndex(inst);
41844186 const tag = self.air.instructions.items(.tag)[inst];
src/arch/arm/CodeGen.zig+4-2
......@@ -4125,8 +4125,10 @@ fn genInlineMemsetCode(
41254125}
41264126
41274127fn airArg(self: *Self, inst: Air.Inst.Index) !void {
4128 const arg_index = self.arg_index;
4129 self.arg_index += 1;
4128 // skip zero-bit arguments as they don't have a corresponding arg instruction
4129 var arg_index = self.arg_index;
4130 while (self.args[arg_index] == .none) arg_index += 1;
4131 self.arg_index = arg_index + 1;
41304132
41314133 const ty = self.air.typeOfIndex(inst);
41324134 const tag = self.air.instructions.items(.tag)[inst];
src/arch/x86_64/CodeGen.zig+4-2
......@@ -3827,8 +3827,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
38273827}
38283828
38293829fn airArg(self: *Self, inst: Air.Inst.Index) !void {
3830 const arg_index = self.arg_index;
3831 self.arg_index += 1;
3830 // skip zero-bit arguments as they don't have a corresponding arg instruction
3831 var arg_index = self.arg_index;
3832 while (self.args[arg_index] == .none) arg_index += 1;
3833 self.arg_index = arg_index + 1;
38323834
38333835 const ty = self.air.typeOfIndex(inst);
38343836 const mcv = self.args[arg_index];
src/codegen/llvm.zig+7-2
......@@ -2219,11 +2219,16 @@ pub const Object = struct {
22192219 ));
22202220 }
22212221
2222 const union_name = if (layout.tag_size == 0) name.ptr else "AnonUnion";
2222 var union_name_buf: ?[:0]const u8 = null;
2223 defer if (union_name_buf) |buf| gpa.free(buf);
2224 const union_name = if (layout.tag_size == 0) name else name: {
2225 union_name_buf = try std.fmt.allocPrintZ(gpa, "{s}:Payload", .{name});
2226 break :name union_name_buf.?;
2227 };
22232228
22242229 const union_di_ty = dib.createUnionType(
22252230 compile_unit_scope,
2226 union_name,
2231 union_name.ptr,
22272232 null, // file
22282233 0, // line
22292234 ty.abiSize(target) * 8, // size in bits
src/link/Wasm.zig+27-5
......@@ -345,7 +345,17 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
345345 }
346346
347347 // TODO: read the file and keep valid parts instead of truncating
348 const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true });
348 const file = try options.emit.?.directory.handle.createFile(sub_path, .{
349 .truncate = true,
350 .read = true,
351 .mode = if (fs.has_executable_bit)
352 if (options.target.os.tag == .wasi and options.output_mode == .Exe)
353 fs.File.default_mode | 0b001_000_000
354 else
355 fs.File.default_mode
356 else
357 0,
358 });
349359 wasm_bin.base.file = file;
350360 wasm_bin.name = sub_path;
351361
......@@ -3750,10 +3760,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
37503760 if (wasm.base.options.import_symbols) {
37513761 try argv.append("--allow-undefined");
37523762 }
3753 try argv.appendSlice(&[_][]const u8{
3754 "-o",
3755 full_out_path,
3756 });
3763 try argv.appendSlice(&.{ "-o", full_out_path });
37573764
37583765 if (target.cpu.arch == .wasm64) {
37593766 try argv.append("-mwasm64");
......@@ -3889,6 +3896,21 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
38893896 }
38903897 }
38913898 }
3899
3900 // Give +x to the .wasm file if it is an executable and the OS is WASI.
3901 // Some systems may be configured to execute such binaries directly. Even if that
3902 // is not the case, it means we will get "exec format error" when trying to run
3903 // it, and then can react to that in the same way as trying to run an ELF file
3904 // from a foreign CPU architecture.
3905 if (fs.has_executable_bit and target.os.tag == .wasi and
3906 wasm.base.options.output_mode == .Exe)
3907 {
3908 // TODO: what's our strategy for reporting linker errors from this function?
3909 // report a nice error here with the file path if it fails instead of
3910 // just returning the error code.
3911 // chmod does not interact with umask, so we use a conservative -rwxr--r-- here.
3912 try std.os.fchmodat(fs.cwd().fd, full_out_path, 0o744, 0);
3913 }
38923914 }
38933915
38943916 if (!wasm.base.options.disable_lld_caching) {
src/main.zig+34-4
......@@ -4013,6 +4013,7 @@ pub const usage_build =
40134013 \\ --cache-dir [path] Override path to local Zig cache directory
40144014 \\ --global-cache-dir [path] Override path to global Zig cache directory
40154015 \\ --zig-lib-dir [arg] Override path to Zig lib directory
4016 \\ --build-runner [file] Override path to build runner
40164017 \\ --prominent-compile-errors Output compile errors formatted for a human to read
40174018 \\ -h, --help Print this help and exit
40184019 \\
......@@ -4031,6 +4032,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
40314032 var override_lib_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIB_DIR");
40324033 var override_global_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_GLOBAL_CACHE_DIR");
40334034 var override_local_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LOCAL_CACHE_DIR");
4035 var override_build_runner: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_BUILD_RUNNER");
40344036 var child_argv = std.ArrayList([]const u8).init(arena);
40354037 var reference_trace: ?u32 = null;
40364038 var debug_compile_errors = false;
......@@ -4065,6 +4067,11 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
40654067 override_lib_dir = args[i];
40664068 try child_argv.appendSlice(&[_][]const u8{ arg, args[i] });
40674069 continue;
4070 } else if (mem.eql(u8, arg, "--build-runner")) {
4071 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
4072 i += 1;
4073 override_build_runner = args[i];
4074 continue;
40684075 } else if (mem.eql(u8, arg, "--cache-dir")) {
40694076 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
40704077 i += 1;
......@@ -4197,10 +4204,29 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
41974204 try thread_pool.init(gpa);
41984205 defer thread_pool.deinit();
41994206
4200 var main_pkg: Package = .{
4201 .root_src_directory = zig_lib_directory,
4202 .root_src_path = "build_runner.zig",
4203 };
4207 var cleanup_build_runner_dir: ?fs.Dir = null;
4208 defer if (cleanup_build_runner_dir) |*dir| dir.close();
4209
4210 var main_pkg: Package = if (override_build_runner) |build_runner_path|
4211 .{
4212 .root_src_directory = blk: {
4213 if (std.fs.path.dirname(build_runner_path)) |dirname| {
4214 const dir = fs.cwd().openDir(dirname, .{}) catch |err| {
4215 fatal("unable to open directory to build runner from argument 'build-runner', '{s}': {s}", .{ dirname, @errorName(err) });
4216 };
4217 cleanup_build_runner_dir = dir;
4218 break :blk .{ .path = dirname, .handle = dir };
4219 }
4220
4221 break :blk .{ .path = null, .handle = fs.cwd() };
4222 },
4223 .root_src_path = std.fs.path.basename(build_runner_path),
4224 }
4225 else
4226 .{
4227 .root_src_directory = zig_lib_directory,
4228 .root_src_path = "build_runner.zig",
4229 };
42044230
42054231 if (!build_options.omit_pkg_fetching_code) {
42064232 var http_client: std.http.Client = .{ .allocator = gpa };
......@@ -4218,6 +4244,9 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
42184244 var build_roots_source = std.ArrayList(u8).init(gpa);
42194245 defer build_roots_source.deinit();
42204246
4247 var all_modules: Package.AllModules = .{};
4248 defer all_modules.deinit(gpa);
4249
42214250 // Here we borrow main package's table and will replace it with a fresh
42224251 // one after this process completes.
42234252 main_pkg.fetchAndAddDependencies(
......@@ -4231,6 +4260,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
42314260 &build_roots_source,
42324261 "",
42334262 color,
4263 &all_modules,
42344264 ) catch |err| switch (err) {
42354265 error.PackageFetchFailed => process.exit(1),
42364266 else => |e| return e,
src/target.zig+3-2
......@@ -648,8 +648,9 @@ pub fn defaultAddressSpace(
648648 function,
649649 },
650650) AddressSpace {
651 _ = target;
652 _ = context;
651 // The default address space for functions on AVR is .flash to produce
652 // correct fixups into progmem.
653 if (context == .function and target.cpu.arch == .avr) return .flash;
653654 return .generic;
654655}
655656
src/type.zig+30
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const builtin = @import("builtin");
23const Value = @import("value.zig").Value;
34const assert = std.debug.assert;
45const Allocator = std.mem.Allocator;
......@@ -6694,4 +6695,33 @@ pub const Type = extern union {
66946695 /// This is only used for comptime asserts. Bump this number when you make a change
66956696 /// to packed struct layout to find out all the places in the codebase you need to edit!
66966697 pub const packed_struct_layout_version = 2;
6698
6699 /// This function is used in the debugger pretty formatters in tools/ to fetch the
6700 /// Tag to Payload mapping to facilitate fancy debug printing for this type.
6701 fn dbHelper(self: *Type, tag_to_payload_map: *map: {
6702 const tags = @typeInfo(Tag).Enum.fields;
6703 var fields: [tags.len]std.builtin.Type.StructField = undefined;
6704 for (&fields, tags) |*field, t| field.* = .{
6705 .name = t.name,
6706 .type = *if (t.value < Tag.no_payload_count) void else @field(Tag, t.name).Type(),
6707 .default_value = null,
6708 .is_comptime = false,
6709 .alignment = 0,
6710 };
6711 break :map @Type(.{ .Struct = .{
6712 .layout = .Extern,
6713 .fields = &fields,
6714 .decls = &.{},
6715 .is_tuple = false,
6716 } });
6717 }) void {
6718 _ = self;
6719 _ = tag_to_payload_map;
6720 }
6721
6722 comptime {
6723 if (builtin.mode == .Debug) {
6724 _ = dbHelper;
6725 }
6726 }
66976727};
src/value.zig+30
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const builtin = @import("builtin");
23const Type = @import("type.zig").Type;
34const log2 = std.math.log2;
45const assert = std.debug.assert;
......@@ -5584,6 +5585,35 @@ pub const Value = extern union {
55845585 ri.* = @intToEnum(RuntimeIndex, @enumToInt(ri.*) + 1);
55855586 }
55865587 };
5588
5589 /// This function is used in the debugger pretty formatters in tools/ to fetch the
5590 /// Tag to Payload mapping to facilitate fancy debug printing for this type.
5591 fn dbHelper(self: *Value, tag_to_payload_map: *map: {
5592 const tags = @typeInfo(Tag).Enum.fields;
5593 var fields: [tags.len]std.builtin.Type.StructField = undefined;
5594 for (&fields, tags) |*field, t| field.* = .{
5595 .name = t.name,
5596 .type = *if (t.value < Tag.no_payload_count) void else @field(Tag, t.name).Type(),
5597 .default_value = null,
5598 .is_comptime = false,
5599 .alignment = 0,
5600 };
5601 break :map @Type(.{ .Struct = .{
5602 .layout = .Extern,
5603 .fields = &fields,
5604 .decls = &.{},
5605 .is_tuple = false,
5606 } });
5607 }) void {
5608 _ = self;
5609 _ = tag_to_payload_map;
5610 }
5611
5612 comptime {
5613 if (builtin.mode == .Debug) {
5614 _ = dbHelper;
5615 }
5616 }
55875617};
55885618
55895619var negative_one_payload: Value.Payload.I64 = .{
test/cases/compile_errors/for_loop_break_value_ignored.zig created+15
......@@ -0,0 +1,15 @@
1fn returns() usize {
2 return 2;
3}
4
5export fn f1() void {
6 for ("hello") |_| {
7 break returns();
8 }
9}
10
11// error
12// backend=stage2
13// target=native
14//
15// :6:5: error: incompatible types: 'usize' and 'void'
test/cases/compile_errors/while_loop_break_value_ignored.zig created+26
......@@ -0,0 +1,26 @@
1fn returns() usize {
2 return 2;
3}
4
5export fn f1() void {
6 var a: bool = true;
7 while (a) {
8 break returns();
9 }
10}
11
12export fn f2() void {
13 var x: bool = true;
14 outer: while (x) {
15 while (x) {
16 break :outer returns();
17 }
18 }
19}
20
21// error
22// backend=stage2
23// target=native
24//
25// :7:5: error: incompatible types: 'usize' and 'void'
26// :14:12: error: incompatible types: 'usize' and 'void'
tools/lldb_pretty_printers.py created+575
......@@ -0,0 +1,575 @@
1# pretty printing for the zig language, zig standard library, and zig stage 2 compiler.
2# put commands in ~/.lldbinit to run them automatically when starting lldb
3# `command script /path/to/stage2_lldb_pretty_printers.py` to import this file
4# `type category enable zig` to enable pretty printing for the zig language
5# `type category enable zig.std` to enable pretty printing for the zig standard library
6# `type category enable zig.stage2` to enable pretty printing for the zig stage 2 compiler
7import lldb
8import re
9
10page_size = 1 << 12
11
12def log2_int(i): return i.bit_length() - 1
13
14# Define Zig Language
15
16zig_keywords = {
17 'addrspace',
18 'align',
19 'allowzero',
20 'and',
21 'anyframe',
22 'anytype',
23 'asm',
24 'async',
25 'await',
26 'break',
27 'callconv',
28 'catch',
29 'comptime',
30 'const',
31 'continue',
32 'defer',
33 'else',
34 'enum',
35 'errdefer',
36 'error',
37 'export',
38 'extern',
39 'fn',
40 'for',
41 'if',
42 'inline',
43 'noalias',
44 'noinline',
45 'nosuspend',
46 'opaque',
47 'or',
48 'orelse',
49 'packed',
50 'pub',
51 'resume',
52 'return',
53 'linksection',
54 'struct',
55 'suspend',
56 'switch',
57 'test',
58 'threadlocal',
59 'try',
60 'union',
61 'unreachable',
62 'usingnamespace',
63 'var',
64 'volatile',
65 'while',
66}
67zig_primitives = {
68 'anyerror',
69 'anyframe',
70 'anyopaque',
71 'bool',
72 'c_int',
73 'c_long',
74 'c_longdouble',
75 'c_longlong',
76 'c_short',
77 'c_uint',
78 'c_ulong',
79 'c_ulonglong',
80 'c_ushort',
81 'comptime_float',
82 'comptime_int',
83 'f128',
84 'f16',
85 'f32',
86 'f64',
87 'f80',
88 'false',
89 'isize',
90 'noreturn',
91 'null',
92 'true',
93 'type',
94 'undefined',
95 'usize',
96 'void',
97}
98zig_integer_type = re.compile('[iu][1-9][0-9]+')
99zig_identifier_regex = re.compile('[A-Z_a-z][0-9A-Z_a-z]*')
100def zig_IsVariableName(string): return string != '_' and string not in zig_keywords and string not in zig_primitives and not zig_integer_type.fullmatch(string) and zig_identifier_regex.fullmatch(string)
101def zig_IsFieldName(string): return string not in zig_keywords and zig_identifier_regex.fullmatch(string)
102
103class zig_Slice_SynthProvider:
104 def __init__(self, value, _=None): self.value = value
105 def update(self):
106 try:
107 self.ptr = self.value.GetChildMemberWithName('ptr')
108 self.len = self.value.GetChildMemberWithName('len').unsigned if self.ptr.unsigned > page_size else 0
109 self.elem_type = self.ptr.type.GetPointeeType()
110 self.elem_size = self.elem_type.size
111 except: pass
112 def has_children(self): return True
113 def num_children(self): return self.len or 0
114 def get_child_index(self, name):
115 try: return int(name.removeprefix('[').removesuffix(']'))
116 except: return -1
117 def get_child_at_index(self, index):
118 if index < 0 or index >= self.len: return None
119 try: return self.ptr.CreateChildAtOffset('[%d]' % index, index * self.elem_size, self.elem_type)
120 except: return None
121
122def zig_String_decode(value, offset=0, length=None):
123 try:
124 value = value.GetNonSyntheticValue()
125 data = value.GetChildMemberWithName('ptr').GetPointeeData(offset, length if length is not None else value.GetChildMemberWithName('len').unsigned)
126 b = bytes(data.uint8)
127 b = b.replace(b'\\', b'\\\\')
128 b = b.replace(b'\n', b'\\n')
129 b = b.replace(b'\r', b'\\r')
130 b = b.replace(b'\t', b'\\t')
131 b = b.replace(b'"', b'\\"')
132 b = b.replace(b'\'', b'\\\'')
133 s = b.decode(encoding='ascii', errors='backslashreplace')
134 return s if s.isprintable() else ''.join((c if c.isprintable() else '\\x%02x' % ord(c) for c in s))
135 except: return None
136def zig_String_SummaryProvider(value, _=None): return '"%s"' % zig_String_decode(value)
137def zig_String_AsIdentifier(value, pred):
138 string = zig_String_decode(value)
139 return string if pred(string) else '@"%s"' % string
140
141class zig_Optional_SynthProvider:
142 def __init__(self, value, _=None): self.value = value
143 def update(self):
144 try:
145 self.child = self.value.GetChildMemberWithName('some').unsigned == 1 and self.value.GetChildMemberWithName('data').Clone('child')
146 except: pass
147 def has_children(self): return bool(self.child)
148 def num_children(self): return int(self.child)
149 def get_child_index(self, name): return 0 if self.child and (name == 'child' or name == '?') else -1
150 def get_child_at_index(self, index): return self.child if self.child and index == 0 else None
151def zig_Optional_SummaryProvider(value, _=None):
152 child = value.GetChildMemberWithName('child')
153 return child or 'null'
154
155class zig_ErrorUnion_SynthProvider:
156 def __init__(self, value, _=None): self.value = value
157 def update(self):
158 try:
159 self.error_set = self.value.GetChildMemberWithName('tag').Clone('error_set')
160 self.payload = self.value.GetChildMemberWithName('value').Clone('payload') if self.error_set.unsigned == 0 else None
161 except: pass
162 def has_children(self): return True
163 def num_children(self): return 1
164 def get_child_index(self, name): return 0 if name == ('payload' if self.payload else 'error_set') else -1
165 def get_child_at_index(self, index): return self.payload or self.error_set if index == 0 else None
166
167# Define Zig Standard Library
168
169class std_SegmentedList_SynthProvider:
170 def __init__(self, value, _=None): self.value = value
171 def update(self):
172 try:
173 self.prealloc_segment = self.value.GetChildMemberWithName('prealloc_segment')
174 self.dynamic_segments = zig_Slice_SynthProvider(self.value.GetChildMemberWithName('dynamic_segments'))
175 self.dynamic_segments.update()
176 self.len = self.value.GetChildMemberWithName('len').unsigned
177 except: pass
178 def has_children(self): return True
179 def num_children(self): return self.len
180 def get_child_index(self, name):
181 try: return int(name.removeprefix('[').removesuffix(']'))
182 except: return -1
183 def get_child_at_index(self, index):
184 try:
185 if index < 0 or index >= self.len: return None
186 prealloc_item_count = len(self.prealloc_segment)
187 if index < prealloc_item_count: return self.prealloc_segment.child[index]
188 prealloc_exp = prealloc_item_count.bit_length() - 1
189 shelf_index = log2_int(index + 1) if prealloc_item_count == 0 else log2_int(index + prealloc_item_count) - prealloc_exp - 1
190 shelf = self.dynamic_segments.get_child_at_index(shelf_index)
191 box_index = (index + 1) - (1 << shelf_index) if prealloc_item_count == 0 else index + prealloc_item_count - (1 << ((prealloc_exp + 1) + shelf_index))
192 elem_type = shelf.type.GetPointeeType()
193 return shelf.CreateChildAtOffset('[%d]' % index, box_index * elem_type.size, elem_type)
194 except: return None
195
196class std_MultiArrayList_SynthProvider:
197 def __init__(self, value, _=None): self.value = value
198 def update(self):
199 try:
200 self.len = 0
201
202 value_type = self.value.type
203 for helper in self.value.target.FindFunctions('%s.dbHelper' % value_type.name, lldb.eFunctionNameTypeFull):
204 ptr_self_type, ptr_child_type, ptr_entry_type = helper.function.type.GetFunctionArgumentTypes()
205 if ptr_self_type.GetPointeeType() == value_type: break
206 else: return
207
208 self.entry_type = ptr_entry_type.GetPointeeType()
209 self.bytes = self.value.GetChildMemberWithName('bytes')
210 self.len = self.value.GetChildMemberWithName('len').unsigned
211 self.capacity = self.value.GetChildMemberWithName('capacity').unsigned
212 except: pass
213 def has_children(self): return True
214 def num_children(self): return self.len
215 def get_child_index(self, name):
216 try: return int(name.removeprefix('[').removesuffix(']'))
217 except: return -1
218 def get_child_at_index(self, index):
219 try:
220 if index < 0 or index >= self.len: return None
221 offset = 0
222 data = lldb.SBData()
223 for field in self.entry_type.fields:
224 ptr_field_type = field.type
225 field_size = ptr_field_type.GetPointeeType().size
226 data.Append(self.bytes.CreateChildAtOffset(field.name, offset + index * field_size, ptr_field_type).address_of.data)
227 offset += self.capacity * field_size
228 return self.bytes.CreateValueFromData('[%d]' % index, data, self.entry_type)
229 except: return None
230
231class std_HashMapUnmanaged_SynthProvider:
232 def __init__(self, value, _=None): self.value = value
233 def update(self):
234 try:
235 self.capacity = 0
236 self.indices = tuple()
237
238 self.metadata = self.value.GetChildMemberWithName('metadata')
239 if not self.metadata.unsigned: return
240
241 value_type = self.value.type
242 for helper in self.value.target.FindFunctions('%s.dbHelper' % value_type.name, lldb.eFunctionNameTypeFull):
243 ptr_self_type, ptr_hdr_type, ptr_entry_type = helper.function.type.GetFunctionArgumentTypes()
244 if ptr_self_type.GetPointeeType() == value_type: break
245 else: return
246 self.entry_type = ptr_entry_type.GetPointeeType()
247
248 hdr_type = ptr_hdr_type.GetPointeeType()
249 hdr = self.metadata.CreateValueFromAddress('header', self.metadata.deref.load_addr - hdr_type.size, hdr_type)
250 self.values = hdr.GetChildMemberWithName('values')
251 self.keys = hdr.GetChildMemberWithName('keys')
252 self.capacity = hdr.GetChildMemberWithName('capacity').unsigned
253
254 self.indices = tuple(i for i, value in enumerate(self.metadata.GetPointeeData(0, self.capacity).sint8) if value < 0)
255 except: pass
256 def has_children(self): return True
257 def num_children(self): return len(self.indices)
258 def get_capacity(self): return self.capacity
259 def get_child_index(self, name):
260 try: return int(name.removeprefix('[').removesuffix(']'))
261 except: return -1
262 def get_child_at_index(self, index):
263 try:
264 fields = {name: base.CreateChildAtOffset(name, self.indices[index] * pointee_type.size, pointee_type).address_of.data for name, base, pointee_type in ((name, base, base.type.GetPointeeType()) for name, base in (('key_ptr', self.keys), ('value_ptr', self.values)))}
265 data = lldb.SBData()
266 for field in self.entry_type.fields: data.Append(fields[field.name])
267 return self.metadata.CreateValueFromData('[%d]' % index, data, self.entry_type)
268 except: return None
269def std_HashMapUnmanaged_SummaryProvider(value, _=None):
270 synth = std_HashMapUnmanaged_SynthProvider(value.GetNonSyntheticValue(), _)
271 synth.update()
272 return 'len=%d capacity=%d' % (synth.num_children(), synth.get_capacity())
273
274# formats a struct of fields of the form `name_ptr: *Type` by auto dereferencing its fields
275class std_Entry_SynthProvider:
276 def __init__(self, value, _=None): self.value = value
277 def update(self):
278 try:
279 self.children = tuple(child.Clone(child.name.removesuffix('_ptr')) for child in self.value.children if child.type.GetPointeeType().size != 0)
280 self.indices = {child.name: i for i, child in enumerate(self.children)}
281 except: pass
282 def has_children(self): return self.num_children() != 0
283 def num_children(self): return len(self.children)
284 def get_child_index(self, name): return self.indices.get(name)
285 def get_child_at_index(self, index): return self.children[index].deref if index >= 0 and index < len(self.children) else None
286
287# Define Zig Stage2 Compiler
288
289class TagAndPayload_SynthProvider:
290 def __init__(self, value, _=None): self.value = value
291 def update(self):
292 try:
293 self.tag = self.value.GetChildMemberWithName('tag') or self.value.GetChildMemberWithName('tag_ptr').deref.Clone('tag')
294 data = self.value.GetChildMemberWithName('data_ptr') or self.value.GetChildMemberWithName('data')
295 self.payload = data.GetChildMemberWithName('payload').GetChildMemberWithName(data.GetChildMemberWithName('tag').value)
296 except: pass
297 def has_children(self): return True
298 def num_children(self): return 2
299 def get_child_index(self, name):
300 try: return ('tag', 'payload').index(name)
301 except: return -1
302 def get_child_at_index(self, index): return (self.tag, self.payload)[index] if index >= 0 and index < 2 else None
303
304def Inst_Ref_SummaryProvider(value, _=None):
305 members = value.type.enum_members
306 return value if any(value.unsigned == member.unsigned for member in members) else 'instructions[%d]' % (value.unsigned - len(members))
307
308class Module_Decl__Module_Decl_Index_SynthProvider:
309 def __init__(self, value, _=None): self.value = value
310 def update(self):
311 try:
312 for frame in self.value.thread:
313 mod = frame.FindVariable('mod') or frame.FindVariable('module')
314 if mod: break
315 else: return
316 self.ptr = mod.GetChildMemberWithName('allocated_decls').GetChildAtIndex(self.value.unsigned).Clone('decl')
317 except: pass
318 def has_children(self): return True
319 def num_children(self): return 1
320 def get_child_index(self, name): return 0 if name == 'decl' else -1
321 def get_child_at_index(self, index): return self.ptr if index == 0 else None
322
323class TagOrPayloadPtr_SynthProvider:
324 def __init__(self, value, _=None): self.value = value
325 def update(self):
326 try:
327 value_type = self.value.type
328 for helper in self.value.target.FindFunctions('%s.dbHelper' % value_type.name, lldb.eFunctionNameTypeFull):
329 ptr_self_type, ptr_tag_to_payload_map_type = helper.function.type.GetFunctionArgumentTypes()
330 self_type = ptr_self_type.GetPointeeType()
331 if self_type == value_type: break
332 else: return
333 tag_to_payload_map = {field.name: field.type for field in ptr_tag_to_payload_map_type.GetPointeeType().fields}
334
335 tag = self.value.GetChildMemberWithName('tag_if_small_enough')
336 if tag.unsigned < page_size:
337 self.tag = tag.Clone('tag')
338 self.payload = None
339 else:
340 ptr_otherwise = self.value.GetChildMemberWithName('ptr_otherwise')
341 self.tag = ptr_otherwise.GetChildMemberWithName('tag')
342 self.payload = ptr_otherwise.Cast(tag_to_payload_map[self.tag.value]).GetChildMemberWithName('data').Clone('payload')
343 except: pass
344 def has_children(self): return True
345 def num_children(self): return 1 + (self.payload is not None)
346 def get_child_index(self, name):
347 try: return ('tag', 'payload').index(name)
348 except: return -1
349 def get_child_at_index(self, index): return (self.tag, self.payload)[index] if index >= 0 and index < 2 else None
350
351def Module_Decl_name(decl):
352 error = lldb.SBError()
353 return decl.process.ReadCStringFromMemory(decl.GetChildMemberWithName('name').deref.load_addr, 256, error)
354
355def Module_Namespace_RenderFullyQualifiedName(namespace):
356 parent = namespace.GetChildMemberWithName('parent')
357 if parent.unsigned < page_size: return zig_String_decode(namespace.GetChildMemberWithName('file_scope').GetChildMemberWithName('sub_file_path')).removesuffix('.zig').replace('/', '.')
358 return '.'.join((Module_Namespace_RenderFullyQualifiedName(parent), Module_Decl_name(namespace.GetChildMemberWithName('ty').GetChildMemberWithName('payload').GetChildMemberWithName('owner_decl').GetChildMemberWithName('decl'))))
359
360def Module_Decl_RenderFullyQualifiedName(decl): return '.'.join((Module_Namespace_RenderFullyQualifiedName(decl.GetChildMemberWithName('src_namespace')), Module_Decl_name(decl)))
361
362def OwnerDecl_RenderFullyQualifiedName(payload): return Module_Decl_RenderFullyQualifiedName(payload.GetChildMemberWithName('owner_decl').GetChildMemberWithName('decl'))
363
364def type_Type_pointer(payload):
365 pointee_type = payload.GetChildMemberWithName('pointee_type')
366 sentinel = payload.GetChildMemberWithName('sentinel').GetChildMemberWithName('child')
367 align = payload.GetChildMemberWithName('align').unsigned
368 addrspace = payload.GetChildMemberWithName('addrspace').value
369 bit_offset = payload.GetChildMemberWithName('bit_offset').unsigned
370 host_size = payload.GetChildMemberWithName('host_size').unsigned
371 vector_index = payload.GetChildMemberWithName('vector_index')
372 allowzero = payload.GetChildMemberWithName('allowzero').unsigned
373 const = not payload.GetChildMemberWithName('mutable').unsigned
374 volatile = payload.GetChildMemberWithName('volatile').unsigned
375 size = payload.GetChildMemberWithName('size').value
376
377 if size == 'One': summary = '*'
378 elif size == 'Many': summary = '[*'
379 elif size == 'Slice': summary = '['
380 elif size == 'C': summary = '[*c'
381 if sentinel: summary += ':%s' % value_Value_SummaryProvider(sentinel)
382 if size != 'One': summary += ']'
383 if allowzero: summary += 'allowzero '
384 if align != 0 or host_size != 0 or vector_index.value != 'none': summary += 'align(%d%s%s) ' % (align, ':%d:%d' % (bit_offset, host_size) if bit_offset != 0 or host_size != 0 else '', ':?' if vector_index.value == 'runtime' else ':%d' % vector_index.unsigned if vector_index.value != 'none' else '')
385 if addrspace != 'generic': summary += 'addrspace(.%s) ' % addrspace
386 if const: summary += 'const '
387 if volatile: summary += 'volatile '
388 summary += type_Type_SummaryProvider(pointee_type)
389 return summary
390
391def type_Type_function(payload):
392 param_types = payload.GetChildMemberWithName('param_types').children
393 comptime_params = payload.GetChildMemberWithName('comptime_params').GetPointeeData(0, len(param_types)).uint8
394 return_type = payload.GetChildMemberWithName('return_type')
395 alignment = payload.GetChildMemberWithName('alignment').unsigned
396 noalias_bits = payload.GetChildMemberWithName('noalias_bits').unsigned
397 cc = payload.GetChildMemberWithName('cc').value
398 is_var_args = payload.GetChildMemberWithName('is_var_args').unsigned
399
400 return 'fn(%s)%s%s %s' % (', '.join(tuple(''.join(('comptime ' if comptime_param else '', 'noalias ' if noalias_bits & 1 << i else '', type_Type_SummaryProvider(param_type))) for i, (comptime_param, param_type) in enumerate(zip(comptime_params, param_types))) + (('...',) if is_var_args else ())), ' align(%d)' % alignment if alignment != 0 else '', ' callconv(.%s)' % cc if cc != 'Unspecified' else '', type_Type_SummaryProvider(return_type))
401
402def type_Type_SummaryProvider(value, _=None):
403 tag = value.GetChildMemberWithName('tag').value
404 return type_tag_handlers.get(tag, lambda payload: tag)(value.GetChildMemberWithName('payload'))
405
406type_tag_handlers = {
407 'atomic_order': lambda payload: 'std.builtin.AtomicOrder',
408 'atomic_rmw_op': lambda payload: 'std.builtin.AtomicRmwOp',
409 'calling_convention': lambda payload: 'std.builtin.CallingConvention',
410 'address_space': lambda payload: 'std.builtin.AddressSpace',
411 'float_mode': lambda payload: 'std.builtin.FloatMode',
412 'reduce_op': lambda payload: 'std.builtin.ReduceOp',
413 'modifier': lambda payload: 'std.builtin.CallModifier',
414 'prefetch_options': lambda payload: 'std.builtin.PrefetchOptions',
415 'export_options': lambda payload: 'std.builtin.ExportOptions',
416 'extern_options': lambda payload: 'std.builtin.ExternOptions',
417 'type_info': lambda payload: 'std.builtin.Type',
418
419 'enum_literal': lambda payload: '@TypeOf(.enum_literal)',
420 'null': lambda payload: '@TypeOf(null)',
421 'undefined': lambda payload: '@TypeOf(undefined)',
422 'empty_struct_literal': lambda payload: '@TypeOf(.{})',
423
424 'anyerror_void_error_union': lambda payload: 'anyerror!void',
425 'const_slice_u8': lambda payload: '[]const u8',
426 'const_slice_u8_sentinel_0': lambda payload: '[:0]const u8',
427 'fn_noreturn_no_args': lambda payload: 'fn() noreturn',
428 'fn_void_no_args': lambda payload: 'fn() void',
429 'fn_naked_noreturn_no_args': lambda payload: 'fn() callconv(.Naked) noreturn',
430 'fn_ccc_void_no_args': lambda payload: 'fn() callconv(.C) void',
431 'single_const_pointer_to_comptime_int': lambda payload: '*const comptime_int',
432 'manyptr_u8': lambda payload: '[*]u8',
433 'manyptr_const_u8': lambda payload: '[*]const u8',
434 'manyptr_const_u8_sentinel_0': lambda payload: '[*:0]const u8',
435
436 'function': type_Type_function,
437 'error_union': lambda payload: '%s!%s' % (type_Type_SummaryProvider(payload.GetChildMemberWithName('error_set')), type_Type_SummaryProvider(payload.GetChildMemberWithName('payload'))),
438 'array_u8': lambda payload: '[%d]u8' % payload.unsigned,
439 'array_u8_sentinel_0': lambda payload: '[%d:0]u8' % payload.unsigned,
440 'vector': lambda payload: '@Vector(%d, %s)' % (payload.GetChildMemberWithName('len').unsigned, type_Type_SummaryProvider(payload.GetChildMemberWithName('elem_type'))),
441 'array': lambda payload: '[%d]%s' % (payload.GetChildMemberWithName('len').unsigned, type_Type_SummaryProvider(payload.GetChildMemberWithName('elem_type'))),
442 'array_sentinel': lambda payload: '[%d:%s]%s' % (payload.GetChildMemberWithName('len').unsigned, value_Value_SummaryProvider(payload.GetChildMemberWithName('sentinel')), type_Type_SummaryProvider(payload.GetChildMemberWithName('elem_type'))),
443 'tuple': lambda payload: 'tuple{%s}' % ', '.join(('comptime %%s = %s' % value_Value_SummaryProvider(value) if value.GetChildMemberWithName('tag').value != 'unreachable_value' else '%s') % type_Type_SummaryProvider(type) for type, value in zip(payload.GetChildMemberWithName('types').children, payload.GetChildMemberWithName('values').children)),
444 'anon_struct': lambda payload: 'struct{%s}' % ', '.join(('comptime %%s: %%s = %s' % value_Value_SummaryProvider(value) if value.GetChildMemberWithName('tag').value != 'unreachable_value' else '%s: %s') % (zig_String_AsIdentifier(name, zig_IsFieldName), type_Type_SummaryProvider(type)) for name, type, value in zip(payload.GetChildMemberWithName('names').children, payload.GetChildMemberWithName('types').children, payload.GetChildMemberWithName('values').children)),
445 'pointer': type_Type_pointer,
446 'single_const_pointer': lambda payload: '*const %s' % type_Type_SummaryProvider(payload),
447 'single_mut_pointer': lambda payload: '*%s' % type_Type_SummaryProvider(payload),
448 'many_const_pointer': lambda payload: '[*]const %s' % type_Type_SummaryProvider(payload),
449 'many_mut_pointer': lambda payload: '[*]%s' % type_Type_SummaryProvider(payload),
450 'c_const_pointer': lambda payload: '[*c]const %s' % type_Type_SummaryProvider(payload),
451 'c_mut_pointer': lambda payload: '[*c]%s' % type_Type_SummaryProvider(payload),
452 'const_slice': lambda payload: '[]const %s' % type_Type_SummaryProvider(payload),
453 'mut_slice': lambda payload: '[]%s' % type_Type_SummaryProvider(payload),
454 'int_signed': lambda payload: 'i%d' % payload.unsigned,
455 'int_unsigned': lambda payload: 'u%d' % payload.unsigned,
456 'optional': lambda payload: '?%s' % type_Type_SummaryProvider(payload),
457 'optional_single_mut_pointer': lambda payload: '?*%s' % type_Type_SummaryProvider(payload),
458 'optional_single_const_pointer': lambda payload: '?*const %s' % type_Type_SummaryProvider(payload),
459 'anyframe_T': lambda payload: 'anyframe->%s' % type_Type_SummaryProvider(payload),
460 'error_set': lambda payload: type_tag_handlers['error_set_merged'](payload.GetChildMemberWithName('names')),
461 'error_set_single': lambda payload: 'error{%s}' % zig_String_AsIdentifier(payload, zig_IsFieldName),
462 'error_set_merged': lambda payload: 'error{%s}' % ','.join(zig_String_AsIdentifier(child.GetChildMemberWithName('key'), zig_IsFieldName) for child in payload.GetChildMemberWithName('entries').children),
463 'error_set_inferred': lambda payload: '@typeInfo(@typeInfo(@TypeOf(%s)).Fn.return_type.?).ErrorUnion.error_set' % OwnerDecl_RenderFullyQualifiedName(payload.GetChildMemberWithName('func')),
464
465 'enum_full': OwnerDecl_RenderFullyQualifiedName,
466 'enum_nonexhaustive': OwnerDecl_RenderFullyQualifiedName,
467 'enum_numbered': OwnerDecl_RenderFullyQualifiedName,
468 'enum_simple': OwnerDecl_RenderFullyQualifiedName,
469 'struct': OwnerDecl_RenderFullyQualifiedName,
470 'union': OwnerDecl_RenderFullyQualifiedName,
471 'union_safety_tagged': OwnerDecl_RenderFullyQualifiedName,
472 'union_tagged': OwnerDecl_RenderFullyQualifiedName,
473 'opaque': OwnerDecl_RenderFullyQualifiedName,
474}
475
476def value_Value_str_lit(payload):
477 for frame in payload.thread:
478 mod = frame.FindVariable('mod') or frame.FindVariable('module')
479 if mod: break
480 else: return
481 return '"%s"' % zig_String_decode(mod.GetChildMemberWithName('string_literal_bytes').GetChildMemberWithName('items'), payload.GetChildMemberWithName('index').unsigned, payload.GetChildMemberWithName('len').unsigned)
482
483def value_Value_SummaryProvider(value, _=None):
484 tag = value.GetChildMemberWithName('tag').value
485 return value_tag_handlers.get(tag, lambda payload: tag.removesuffix('_type'))(value.GetChildMemberWithName('payload'))
486
487value_tag_handlers = {
488 'undef': lambda payload: 'undefined',
489 'zero': lambda payload: '0',
490 'one': lambda payload: '1',
491 'void_value': lambda payload: '{}',
492 'unreachable_value': lambda payload: 'unreachable',
493 'null_value': lambda payload: 'null',
494 'bool_true': lambda payload: 'true',
495 'bool_false': lambda payload: 'false',
496
497 'empty_struct_value': lambda payload: '.{}',
498 'empty_array': lambda payload: '.{}',
499
500 'ty': type_Type_SummaryProvider,
501 'int_type': lambda payload: '%c%d' % (payload.GetChildMemberWithName('bits').unsigned, 's' if payload.GetChildMemberWithName('signed').unsigned == 1 else 'u'),
502 'int_u64': lambda payload: '%d' % payload.unsigned,
503 'int_i64': lambda payload: '%d' % payload.signed,
504 'int_big_positive': lambda payload: sum(child.unsigned << i * child.type.size * 8 for i, child in enumerate(payload.children)),
505 'int_big_negative': lambda payload: '-%s' % value_tag_handlers['int_big_positive'](payload),
506 'function': OwnerDecl_RenderFullyQualifiedName,
507 'extern_fn': OwnerDecl_RenderFullyQualifiedName,
508 'variable': lambda payload: value_Value_SummaryProvider(payload.GetChildMemberWithName('decl').GetChildMemberWithName('val')),
509 'runtime_value': value_Value_SummaryProvider,
510 'decl_ref': lambda payload: value_Value_SummaryProvider(payload.GetChildMemberWithName('decl').GetChildMemberWithName('val')),
511 'decl_ref_mut': lambda payload: value_Value_SummaryProvider(payload.GetChildMemberWithName('decl_index').GetChildMemberWithName('decl').GetChildMemberWithName('val')),
512 'comptime_field_ptr': lambda payload: '&%s' % value_Value_SummaryProvider(payload.GetChildMemberWithName('field_val')),
513 'elem_ptr': lambda payload: '(%s)[%d]' % (value_Value_SummaryProvider(payload.GetChildMemberWithName('array_ptr')), payload.GetChildMemberWithName('index').unsigned),
514 'field_ptr': lambda payload: '(%s).field[%d]' % (value_Value_SummaryProvider(payload.GetChildMemberWithName('container_ptr')), payload.GetChildMemberWithName('field_index').unsigned),
515 'bytes': lambda payload: '"%s"' % zig_String_decode(payload),
516 'str_lit': value_Value_str_lit,
517 'repeated': lambda payload: '.{%s} ** _' % value_Value_SummaryProvider(payload),
518 'empty_array_sentinel': lambda payload: '.{%s}' % value_Value_SummaryProvider(payload),
519 'slice': lambda payload: '(%s)[0..%s]' % tuple(value_Value_SummaryProvider(payload.GetChildMemberWithName(name)) for name in ('ptr', 'len')),
520 'float_16': lambda payload: payload.value,
521 'float_32': lambda payload: payload.value,
522 'float_64': lambda payload: payload.value,
523 'float_80': lambda payload: payload.value,
524 'float_128': lambda payload: payload.value,
525 'enum_literal': lambda payload: '.%s' % zig_String_AsIdentifier(payload, zig_IsFieldName),
526 'enum_field_index': lambda payload: 'field[%d]' % payload.unsigned,
527 'error': lambda payload: 'error.%s' % zig_String_AsIdentifier(payload.GetChildMemberWithName('name'), zig_IsFieldName),
528 'eu_payload': value_Value_SummaryProvider,
529 'eu_payload_ptr': lambda payload: '&((%s).* catch unreachable)' % value_Value_SummaryProvider(payload.GetChildMemberWithName('container_ptr')),
530 'opt_payload': value_Value_SummaryProvider,
531 'opt_payload_ptr': lambda payload: '&(%s).*.?' % value_Value_SummaryProvider(payload.GetChildMemberWithName('container_ptr')),
532 'aggregate': lambda payload: '.{%s}' % ', '.join(map(value_Value_SummaryProvider, payload.children)),
533 'union': lambda payload: '.{.%s = %s}' % tuple(value_Value_SummaryProvider(payload.GetChildMemberWithName(name)) for name in ('tag', 'val')),
534
535 'lazy_align': lambda payload: '@alignOf(%s)' % type_Type_SummaryProvider(payload),
536 'lazy_size': lambda payload: '@sizeOf(%s)' % type_Type_SummaryProvider(payload),
537}
538
539# Initialize
540
541def add(debugger, *, category, regex=False, type, identifier=None, synth=False, inline_children=False, expand=False, summary=False):
542 prefix = '.'.join((__name__, (identifier or type).replace('.', '_').replace(':', '_')))
543 if summary: debugger.HandleCommand('type summary add --category %s%s%s "%s"' % (category, ' --inline-children' if inline_children else ''.join((' --expand' if expand else '', ' --python-function %s_SummaryProvider' % prefix if summary == True else ' --summary-string "%s"' % summary)), ' --regex' if regex else '', type))
544 if synth: debugger.HandleCommand('type synthetic add --category %s%s --python-class %s_SynthProvider "%s"' % (category, ' --regex' if regex else '', prefix, type))
545
546def MultiArrayList_Entry(type): return '^multi_array_list\\.MultiArrayList\\(%s\\)\\.Entry__struct_[1-9][0-9]*$' % type
547
548def __lldb_init_module(debugger, _=None):
549 # Initialize Zig Language
550 add(debugger, category='zig', regex=True, type='^\\[\\]', identifier='zig_Slice', synth=True, expand=True, summary='len=${svar%#}')
551 add(debugger, category='zig', type='[]u8', identifier='zig_String', summary=True)
552 add(debugger, category='zig', regex=True, type='^\\?', identifier='zig_Optional', synth=True, summary=True)
553 add(debugger, category='zig', regex=True, type='^(error{.*}|anyerror)!', identifier='zig_ErrorUnion', synth=True, inline_children=True, summary=True)
554
555 # Initialize Zig Standard Library
556 add(debugger, category='zig.std', type='mem.Allocator', summary='${var.ptr}')
557 add(debugger, category='zig.std', regex=True, type='^segmented_list\\.SegmentedList\\(.*\\)$', identifier='std_SegmentedList', synth=True, expand=True, summary='len=${var.len}')
558 add(debugger, category='zig.std', regex=True, type='^multi_array_list\\.MultiArrayList\\(.*\\)$', identifier='std_MultiArrayList', synth=True, expand=True, summary='len=${var.len} capacity=${var.capacity}')
559 add(debugger, category='zig.std', regex=True, type=MultiArrayList_Entry('.*'), identifier='std_Entry', synth=True, inline_children=True, summary=True)
560 add(debugger, category='zig.std', regex=True, type='^hash_map\\.HashMapUnmanaged\\(.*\\)$', identifier='std_HashMapUnmanaged', synth=True, expand=True, summary=True)
561 add(debugger, category='zig.std', regex=True, type='^hash_map\\.HashMapUnmanaged\\(.*\\)\\.Entry$', identifier = 'std_Entry', synth=True, inline_children=True, summary=True)
562
563 # Initialize Zig Stage2 Compiler
564 add(debugger, category='zig.stage2', type='Zir.Inst', identifier='TagAndPayload', synth=True, inline_children=True, summary=True)
565 add(debugger, category='zig.stage2', regex=True, type=MultiArrayList_Entry('Zir\\.Inst'), identifier='TagAndPayload', synth=True, inline_children=True, summary=True)
566 add(debugger, category='zig.stage2', regex=True, type='^Zir\\.Inst\\.Data\\.Data__struct_[1-9][0-9]*$', inline_children=True, summary=True)
567 add(debugger, category='zig.stage2', type='Zir.Inst::Zir.Inst.Ref', identifier='Inst_Ref', summary=True)
568 add(debugger, category='zig.stage2', type='Air.Inst', identifier='TagAndPayload', synth=True, inline_children=True, summary=True)
569 add(debugger, category='zig.stage2', regex=True, type=MultiArrayList_Entry('Air\\.Inst'), identifier='TagAndPayload', synth=True, inline_children=True, summary=True)
570 add(debugger, category='zig.stage2', regex=True, type='^Air\\.Inst\\.Data\\.Data__struct_[1-9][0-9]*$', inline_children=True, summary=True)
571 add(debugger, category='zig.stage2', type='Module.Decl::Module.Decl.Index', synth=True)
572 add(debugger, category='zig.stage2', type='type.Type', identifier='TagOrPayloadPtr', synth=True)
573 add(debugger, category='zig.stage2', type='type.Type', summary=True)
574 add(debugger, category='zig.stage2', type='value.Value', identifier='TagOrPayloadPtr', synth=True)
575 add(debugger, category='zig.stage2', type='value.Value', summary=True)
tools/stage2_gdb_pretty_printers.py+105-14
......@@ -3,13 +3,55 @@
33import re
44import gdb.printing
55
6import sys
7from pathlib import Path
8sys.path.insert(0, str(Path(__file__).parent))
9import stage2_pretty_printers_common as common
10
11
126class TypePrinter:
7 no_payload_count = 4096
8
9 # Keep in sync with src/type.zig
10 # Types which have no payload do not need to be entered here.
11 payload_type_names = {
12 'array_u8': 'Type.Payload.Len',
13 'array_u8_sentinel_0': 'Type.Payload.Len',
14
15 'single_const_pointer': 'Type.Payload.ElemType',
16 'single_mut_pointer': 'Type.Payload.ElemType',
17 'many_const_pointer': 'Type.Payload.ElemType',
18 'many_mut_pointer': 'Type.Payload.ElemType',
19 'c_const_pointer': 'Type.Payload.ElemType',
20 'c_mut_pointer': 'Type.Payload.ElemType',
21 'const_slice': 'Type.Payload.ElemType',
22 'mut_slice': 'Type.Payload.ElemType',
23 'optional': 'Type.Payload.ElemType',
24 'optional_single_mut_pointer': 'Type.Payload.ElemType',
25 'optional_single_const_pointer': 'Type.Payload.ElemType',
26 'anyframe_T': 'Type.Payload.ElemType',
27
28 'int_signed': 'Type.Payload.Bits',
29 'int_unsigned': 'Type.Payload.Bits',
30
31 'error_set': 'Type.Payload.ErrorSet',
32 'error_set_inferred': 'Type.Payload.ErrorSetInferred',
33 'error_set_merged': 'Type.Payload.ErrorSetMerged',
34
35 'array': 'Type.Payload.Array',
36 'vector': 'Type.Payload.Array',
37
38 'array_sentinel': 'Type.Payload.ArraySentinel',
39 'pointer': 'Type.Payload.Pointer',
40 'function': 'Type.Payload.Function',
41 'error_union': 'Type.Payload.ErrorUnion',
42 'error_set_single': 'Type.Payload.Name',
43 'opaque': 'Type.Payload.Opaque',
44 'struct': 'Type.Payload.Struct',
45 'union': 'Type.Payload.Union',
46 'union_tagged': 'Type.Payload.Union',
47 'enum_full, .enum_nonexhaustive': 'Type.Payload.EnumFull',
48 'enum_simple': 'Type.Payload.EnumSimple',
49 'enum_numbered': 'Type.Payload.EnumNumbered',
50 'empty_struct': 'Type.Payload.ContainerScope',
51 'tuple': 'Type.Payload.Tuple',
52 'anon_struct': 'Type.Payload.AnonStruct',
53 }
54
1355 def __init__(self, val):
1456 self.val = val
1557
......@@ -17,7 +59,7 @@ class TypePrinter:
1759 tag_if_small_enough = self.val['tag_if_small_enough']
1860 tag_type = tag_if_small_enough.type
1961
20 if tag_if_small_enough < common.Type.no_payload_count:
62 if tag_if_small_enough < TypePrinter.no_payload_count:
2163 return tag_if_small_enough
2264 else:
2365 return self.val['ptr_otherwise'].dereference()['tag']
......@@ -27,7 +69,7 @@ class TypePrinter:
2769 if tag is None:
2870 return None
2971
30 type_name = common.Type.payload_type_names.get(str(tag))
72 type_name = TypePrinter.payload_type_names.get(str(tag))
3173 if type_name is None:
3274 return None
3375 return gdb.lookup_type('struct type.%s' % type_name)
......@@ -36,12 +78,12 @@ class TypePrinter:
3678 tag = self.tag()
3779 if tag is None:
3880 return '(invalid type)'
39 if self.val['tag_if_small_enough'] < common.Type.no_payload_count:
81 if self.val['tag_if_small_enough'] < TypePrinter.no_payload_count:
4082 return '.%s' % str(tag)
4183 return None
4284
4385 def children(self):
44 if self.val['tag_if_small_enough'] < common.Type.no_payload_count:
86 if self.val['tag_if_small_enough'] < TypePrinter.no_payload_count:
4587 return
4688
4789 yield ('tag', '.%s' % str(self.tag()))
......@@ -51,6 +93,55 @@ class TypePrinter:
5193 yield ('payload', self.val['ptr_otherwise'].cast(payload_type.pointer()).dereference()['data'])
5294
5395class ValuePrinter:
96 no_payload_count = 4096
97
98 # Keep in sync with src/value.zig
99 # Values which have no payload do not need to be entered here.
100 payload_type_names = {
101 'big_int_positive': 'Value.Payload.BigInt',
102 'big_int_negative': 'Value.Payload.BigInt',
103
104 'extern_fn': 'Value.Payload.ExternFn',
105
106 'decl_ref': 'Value.Payload.Decl',
107
108 'repeated': 'Value.Payload.SubValue',
109 'eu_payload': 'Value.Payload.SubValue',
110 'opt_payload': 'Value.Payload.SubValue',
111 'empty_array_sentinel': 'Value.Payload.SubValue',
112
113 'eu_payload_ptr': 'Value.Payload.PayloadPtr',
114 'opt_payload_ptr': 'Value.Payload.PayloadPtr',
115
116 'bytes': 'Value.Payload.Bytes',
117 'enum_literal': 'Value.Payload.Bytes',
118
119 'slice': 'Value.Payload.Slice',
120
121 'enum_field_index': 'Value.Payload.U32',
122
123 'ty': 'Value.Payload.Ty',
124 'int_type': 'Value.Payload.IntType',
125 'int_u64': 'Value.Payload.U64',
126 'int_i64': 'Value.Payload.I64',
127 'function': 'Value.Payload.Function',
128 'variable': 'Value.Payload.Variable',
129 'decl_ref_mut': 'Value.Payload.DeclRefMut',
130 'elem_ptr': 'Value.Payload.ElemPtr',
131 'field_ptr': 'Value.Payload.FieldPtr',
132 'float_16': 'Value.Payload.Float_16',
133 'float_32': 'Value.Payload.Float_32',
134 'float_64': 'Value.Payload.Float_64',
135 'float_80': 'Value.Payload.Float_80',
136 'float_128': 'Value.Payload.Float_128',
137 'error': 'Value.Payload.Error',
138 'inferred_alloc': 'Value.Payload.InferredAlloc',
139 'inferred_alloc_comptime': 'Value.Payload.InferredAllocComptime',
140 'aggregate': 'Value.Payload.Aggregate',
141 'union': 'Value.Payload.Union',
142 'bound_fn': 'Value.Payload.BoundFn',
143 }
144
54145 def __init__(self, val):
55146 self.val = val
56147
......@@ -58,7 +149,7 @@ class ValuePrinter:
58149 tag_if_small_enough = self.val['tag_if_small_enough']
59150 tag_type = tag_if_small_enough.type
60151
61 if tag_if_small_enough < common.Value.no_payload_count:
152 if tag_if_small_enough < ValuePrinter.no_payload_count:
62153 return tag_if_small_enough
63154 else:
64155 return self.val['ptr_otherwise'].dereference()['tag']
......@@ -68,7 +159,7 @@ class ValuePrinter:
68159 if tag is None:
69160 return None
70161
71 type_name = Comman.Value.payload_type_names.get(str(tag))
162 type_name = ValuePrinter.payload_type_names.get(str(tag))
72163 if type_name is None:
73164 return None
74165 return gdb.lookup_type('struct value.%s' % type_name)
......@@ -77,12 +168,12 @@ class ValuePrinter:
77168 tag = self.tag()
78169 if tag is None:
79170 return '(invalid value)'
80 if self.val['tag_if_small_enough'] < common.Value.no_payload_count:
171 if self.val['tag_if_small_enough'] < ValuePrinter.no_payload_count:
81172 return '.%s' % str(tag)
82173 return None
83174
84175 def children(self):
85 if self.val['tag_if_small_enough'] < common.Value.no_payload_count:
176 if self.val['tag_if_small_enough'] < ValuePrinter.no_payload_count:
86177 return
87178
88179 yield ('tag', '.%s' % str(self.tag()))
tools/stage2_lldb_pretty_printers.py deleted-59
......@@ -1,59 +0,0 @@
1# pretty printing for stage 2.
2# put "command script /path/to/stage2_lldb_pretty_printers.py" and "type category enable stage2" in ~/.lldbinit to load it automatically.
3import lldb
4import stage2_pretty_printers_common as common
5
6category = 'stage2'
7module = category + '_lldb_pretty_printers'
8
9class type_Type_SynthProvider:
10 def __init__(self, type, _=None):
11 self.type = type
12
13 def update(self):
14 self.tag = self.type.GetChildMemberWithName('tag_if_small_enough').Clone('tag')
15 self.payload = None
16 if self.tag.GetValueAsUnsigned() >= common.Type.no_payload_count:
17 ptr_otherwise = self.type.GetChildMemberWithName('ptr_otherwise')
18 self.tag = ptr_otherwise.Dereference().GetChildMemberWithName('tag')
19 payload_type = self.type.target.FindFirstType('type.' + common.Type.payload_type_names[self.tag.GetValue()])
20 self.payload = ptr_otherwise.Cast(payload_type.GetPointerType()).Dereference().GetChildMemberWithName('data').Clone('payload')
21
22 def num_children(self):
23 return 1 + (self.payload is not None)
24
25 def get_child_index(self, name):
26 return ['tag', 'payload'].index(name)
27
28 def get_child_at_index(self, index):
29 return [self.tag, self.payload][index]
30
31class value_Value_SynthProvider:
32 def __init__(self, value, _=None):
33 self.value = value
34
35 def update(self):
36 self.tag = self.value.GetChildMemberWithName('tag_if_small_enough').Clone('tag')
37 self.payload = None
38 if self.tag.GetValueAsUnsigned() >= common.Value.no_payload_count:
39 ptr_otherwise = self.value.GetChildMemberWithName('ptr_otherwise')
40 self.tag = ptr_otherwise.Dereference().GetChildMemberWithName('tag')
41 payload_type = self.value.target.FindFirstType('value.' + common.Value.payload_type_names[self.tag.GetValue()])
42 self.payload = ptr_otherwise.Cast(payload_type.GetPointerType()).Dereference().GetChildMemberWithName('data').Clone('payload')
43
44 def num_children(self):
45 return 1 + (self.payload is not None)
46
47 def get_child_index(self, name):
48 return ['tag', 'payload'].index(name)
49
50 def get_child_at_index(self, index):
51 return [self.tag, self.payload][index]
52
53def add(debugger, type, summary=False, synth=False):
54 if summary: debugger.HandleCommand('type summary add --python-function ' + module + '.' + type.replace('.', '_') + '_SummaryProvider "' + type + '" --category ' + category)
55 if synth: debugger.HandleCommand('type synthetic add --python-class ' + module + '.' + type.replace('.', '_') + '_SynthProvider "' + type + '" --category ' + category)
56
57def __lldb_init_module(debugger, _=None):
58 add(debugger, 'type.Type', synth=True)
59 add(debugger, 'value.Value', synth=True)
tools/stage2_pretty_printers_common.py deleted-98
......@@ -1,98 +0,0 @@
1class Type:
2 no_payload_count = 4096
3
4 # Keep in sync with src/type.zig
5 # Types which have no payload do not need to be entered here.
6 payload_type_names = {
7 'array_u8': 'Type.Payload.Len',
8 'array_u8_sentinel_0': 'Type.Payload.Len',
9
10 'single_const_pointer': 'Type.Payload.ElemType',
11 'single_mut_pointer': 'Type.Payload.ElemType',
12 'many_const_pointer': 'Type.Payload.ElemType',
13 'many_mut_pointer': 'Type.Payload.ElemType',
14 'c_const_pointer': 'Type.Payload.ElemType',
15 'c_mut_pointer': 'Type.Payload.ElemType',
16 'const_slice': 'Type.Payload.ElemType',
17 'mut_slice': 'Type.Payload.ElemType',
18 'optional': 'Type.Payload.ElemType',
19 'optional_single_mut_pointer': 'Type.Payload.ElemType',
20 'optional_single_const_pointer': 'Type.Payload.ElemType',
21 'anyframe_T': 'Type.Payload.ElemType',
22
23 'int_signed': 'Type.Payload.Bits',
24 'int_unsigned': 'Type.Payload.Bits',
25
26 'error_set': 'Type.Payload.ErrorSet',
27 'error_set_inferred': 'Type.Payload.ErrorSetInferred',
28 'error_set_merged': 'Type.Payload.ErrorSetMerged',
29
30 'array': 'Type.Payload.Array',
31 'vector': 'Type.Payload.Array',
32
33 'array_sentinel': 'Type.Payload.ArraySentinel',
34 'pointer': 'Type.Payload.Pointer',
35 'function': 'Type.Payload.Function',
36 'error_union': 'Type.Payload.ErrorUnion',
37 'error_set_single': 'Type.Payload.Name',
38 'opaque': 'Type.Payload.Opaque',
39 'struct': 'Type.Payload.Struct',
40 'union': 'Type.Payload.Union',
41 'union_tagged': 'Type.Payload.Union',
42 'enum_full, .enum_nonexhaustive': 'Type.Payload.EnumFull',
43 'enum_simple': 'Type.Payload.EnumSimple',
44 'enum_numbered': 'Type.Payload.EnumNumbered',
45 'empty_struct': 'Type.Payload.ContainerScope',
46 'tuple': 'Type.Payload.Tuple',
47 'anon_struct': 'Type.Payload.AnonStruct',
48 }
49
50class Value:
51 no_payload_count = 4096
52
53 # Keep in sync with src/value.zig
54 # Values which have no payload do not need to be entered here.
55 payload_type_names = {
56 'big_int_positive': 'Value.Payload.BigInt',
57 'big_int_negative': 'Value.Payload.BigInt',
58
59 'extern_fn': 'Value.Payload.ExternFn',
60
61 'decl_ref': 'Value.Payload.Decl',
62
63 'repeated': 'Value.Payload.SubValue',
64 'eu_payload': 'Value.Payload.SubValue',
65 'opt_payload': 'Value.Payload.SubValue',
66 'empty_array_sentinel': 'Value.Payload.SubValue',
67
68 'eu_payload_ptr': 'Value.Payload.PayloadPtr',
69 'opt_payload_ptr': 'Value.Payload.PayloadPtr',
70
71 'bytes': 'Value.Payload.Bytes',
72 'enum_literal': 'Value.Payload.Bytes',
73
74 'slice': 'Value.Payload.Slice',
75
76 'enum_field_index': 'Value.Payload.U32',
77
78 'ty': 'Value.Payload.Ty',
79 'int_type': 'Value.Payload.IntType',
80 'int_u64': 'Value.Payload.U64',
81 'int_i64': 'Value.Payload.I64',
82 'function': 'Value.Payload.Function',
83 'variable': 'Value.Payload.Variable',
84 'decl_ref_mut': 'Value.Payload.DeclRefMut',
85 'elem_ptr': 'Value.Payload.ElemPtr',
86 'field_ptr': 'Value.Payload.FieldPtr',
87 'float_16': 'Value.Payload.Float_16',
88 'float_32': 'Value.Payload.Float_32',
89 'float_64': 'Value.Payload.Float_64',
90 'float_80': 'Value.Payload.Float_80',
91 'float_128': 'Value.Payload.Float_128',
92 'error': 'Value.Payload.Error',
93 'inferred_alloc': 'Value.Payload.InferredAlloc',
94 'inferred_alloc_comptime': 'Value.Payload.InferredAllocComptime',
95 'aggregate': 'Value.Payload.Aggregate',
96 'union': 'Value.Payload.Union',
97 'bound_fn': 'Value.Payload.BoundFn',
98 }
tools/std_gdb_pretty_printers.py+2-2
......@@ -26,7 +26,7 @@ class MultiArrayListPrinter:
2626 self.val = val
2727
2828 def child_type(self):
29 (helper_fn, _) = gdb.lookup_symbol('%s.gdbHelper' % self.val.type.name)
29 (helper_fn, _) = gdb.lookup_symbol('%s.dbHelper' % self.val.type.name)
3030 return helper_fn.type.fields()[1].type.target()
3131
3232 def to_string(self):
......@@ -65,7 +65,7 @@ class HashMapPrinter:
6565 self.val = val['unmanaged'] if is_managed else val
6666
6767 def header_ptr_type(self):
68 (helper_fn, _) = gdb.lookup_symbol('%s.gdbHelper' % self.val.type.name)
68 (helper_fn, _) = gdb.lookup_symbol('%s.dbHelper' % self.val.type.name)
6969 return helper_fn.type.fields()[1].type
7070
7171 def header(self):