| author | |
| committer | |
| log | df4cfc2ecf498bf4615ccbaa93438849322bbd18 |
| tree | a71611e86cacd8e021190cc4755574c514acb5c7 |
| parent | 72443fb88cfddad8a58868c150eaf5818826cb21 |
| parent | 75ff34db9e93056482233f8476a06f78b4a2f3c2 |
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 |
| 362 | 362 | \\ --cache-dir [path] Override path to local Zig cache directory |
| 363 | 363 | \\ --global-cache-dir [path] Override path to global Zig cache directory |
| 364 | 364 | \\ --zig-lib-dir [arg] Override path to Zig lib directory |
| 365 | \\ --build-runner [file] Override path to build runner | |
| 365 | 366 | \\ --debug-log [scope] Enable debugging the compiler |
| 366 | 367 | \\ --verbose-link Enable compiler debug output for linking |
| 367 | 368 | \\ --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 { |
| 956 | 956 | |
| 957 | 957 | // Create/Write a file, close it, then grab its stat.mtime timestamp. |
| 958 | 958 | fn 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, .{ | |
| 960 | 962 | .read = true, |
| 961 | 963 | .truncate = true, |
| 962 | 964 | }); |
| 963 | defer file.close(); | |
| 965 | defer { | |
| 966 | file.close(); | |
| 967 | fs.cwd().deleteFile(test_out_file) catch {}; | |
| 968 | } | |
| 964 | 969 | |
| 965 | 970 | return (try file.stat()).mtime; |
| 966 | 971 | } |
lib/std/c.zig+2| ... | ... | @@ -171,7 +171,9 @@ pub extern "c" fn dup(fd: c.fd_t) c_int; |
| 171 | 171 | pub extern "c" fn dup2(old_fd: c.fd_t, new_fd: c.fd_t) c_int; |
| 172 | 172 | pub extern "c" fn readlink(noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize; |
| 173 | 173 | pub extern "c" fn readlinkat(dirfd: c.fd_t, noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize; |
| 174 | pub extern "c" fn chmod(path: [*:0]const u8, mode: c.mode_t) c_int; | |
| 174 | 175 | pub extern "c" fn fchmod(fd: c.fd_t, mode: c.mode_t) c_int; |
| 176 | pub extern "c" fn fchmodat(fd: c.fd_t, path: [*:0]const u8, mode: c.mode_t, flags: c_uint) c_int; | |
| 175 | 177 | pub extern "c" fn fchown(fd: c.fd_t, owner: c.uid_t, group: c.gid_t) c_int; |
| 176 | 178 | pub extern "c" fn umask(mode: c.mode_t) c.mode_t; |
| 177 | 179 |
lib/std/child_process.zig+63-203| ... | ... | @@ -19,8 +19,15 @@ const maxInt = std.math.maxInt; |
| 19 | 19 | const assert = std.debug.assert; |
| 20 | 20 | |
| 21 | 21 | pub 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, | |
| 24 | 31 | thread_handle: if (builtin.os.tag == .windows) windows.HANDLE else void, |
| 25 | 32 | |
| 26 | 33 | allocator: mem.Allocator, |
| ... | ... | @@ -105,8 +112,7 @@ pub const ChildProcess = struct { |
| 105 | 112 | return .{ |
| 106 | 113 | .allocator = allocator, |
| 107 | 114 | .argv = argv, |
| 108 | .pid = undefined, | |
| 109 | .handle = undefined, | |
| 115 | .id = undefined, | |
| 110 | 116 | .thread_handle = undefined, |
| 111 | 117 | .err_pipe = null, |
| 112 | 118 | .term = null, |
| ... | ... | @@ -131,6 +137,7 @@ pub const ChildProcess = struct { |
| 131 | 137 | } |
| 132 | 138 | |
| 133 | 139 | /// On success must call `kill` or `wait`. |
| 140 | /// After spawning the `id` is available. | |
| 134 | 141 | pub fn spawn(self: *ChildProcess) SpawnError!void { |
| 135 | 142 | if (!std.process.can_spawn) { |
| 136 | 143 | @compileError("the target operating system cannot spawn processes"); |
| ... | ... | @@ -167,7 +174,7 @@ pub const ChildProcess = struct { |
| 167 | 174 | return term; |
| 168 | 175 | } |
| 169 | 176 | |
| 170 | try windows.TerminateProcess(self.handle, exit_code); | |
| 177 | try windows.TerminateProcess(self.id, exit_code); | |
| 171 | 178 | try self.waitUnwrappedWindows(); |
| 172 | 179 | return self.term.?; |
| 173 | 180 | } |
| ... | ... | @@ -177,18 +184,21 @@ pub const ChildProcess = struct { |
| 177 | 184 | self.cleanupStreams(); |
| 178 | 185 | return term; |
| 179 | 186 | } |
| 180 | try os.kill(self.pid, os.SIG.TERM); | |
| 187 | try os.kill(self.id, os.SIG.TERM); | |
| 181 | 188 | try self.waitUnwrapped(); |
| 182 | 189 | return self.term.?; |
| 183 | 190 | } |
| 184 | 191 | |
| 185 | 192 | /// Blocks until child process terminates and then cleans up all resources. |
| 186 | 193 | 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; | |
| 192 | 202 | } |
| 193 | 203 | |
| 194 | 204 | pub const ExecResult = struct { |
| ... | ... | @@ -197,6 +207,19 @@ pub const ChildProcess = struct { |
| 197 | 207 | stderr: []u8, |
| 198 | 208 | }; |
| 199 | 209 | |
| 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 | ||
| 200 | 223 | /// Collect the output from the process's stdout and stderr. Will return once all output |
| 201 | 224 | /// has been collected. This does not mean that the process has ended. `wait` should still |
| 202 | 225 | /// be called to wait for and clean up the process. |
| ... | ... | @@ -210,196 +233,33 @@ pub const ChildProcess = struct { |
| 210 | 233 | ) !void { |
| 211 | 234 | debug.assert(child.stdout_behavior == .Pipe); |
| 212 | 235 | 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 | } | |
| 225 | 236 | |
| 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"); | |
| 269 | 241 | |
| 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(); | |
| 284 | 247 | |
| 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; | |
| 294 | 253 | } |
| 295 | } | |
| 296 | 254 | |
| 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)); | |
| 324 | 257 | } |
| 325 | 258 | |
| 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 | }; | |
| 403 | 263 | |
| 404 | 264 | /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns. |
| 405 | 265 | /// If it succeeds, the caller owns result.stdout and result.stderr memory. |
| ... | ... | @@ -411,7 +271,7 @@ pub const ChildProcess = struct { |
| 411 | 271 | env_map: ?*const EnvMap = null, |
| 412 | 272 | max_output_bytes: usize = 50 * 1024, |
| 413 | 273 | expand_arg0: Arg0Expand = .no_expand, |
| 414 | }) !ExecResult { | |
| 274 | }) ExecError!ExecResult { | |
| 415 | 275 | var child = ChildProcess.init(args.argv, args.allocator); |
| 416 | 276 | child.stdin_behavior = .Ignore; |
| 417 | 277 | child.stdout_behavior = .Pipe; |
| ... | ... | @@ -459,18 +319,18 @@ pub const ChildProcess = struct { |
| 459 | 319 | } |
| 460 | 320 | |
| 461 | 321 | 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); | |
| 463 | 323 | |
| 464 | 324 | self.term = @as(SpawnError!Term, x: { |
| 465 | 325 | 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) { | |
| 467 | 327 | break :x Term{ .Unknown = 0 }; |
| 468 | 328 | } else { |
| 469 | 329 | break :x Term{ .Exited = @truncate(u8, exit_code) }; |
| 470 | 330 | } |
| 471 | 331 | }); |
| 472 | 332 | |
| 473 | os.close(self.handle); | |
| 333 | os.close(self.id); | |
| 474 | 334 | os.close(self.thread_handle); |
| 475 | 335 | self.cleanupStreams(); |
| 476 | 336 | return result; |
| ... | ... | @@ -478,9 +338,9 @@ pub const ChildProcess = struct { |
| 478 | 338 | |
| 479 | 339 | fn waitUnwrapped(self: *ChildProcess) !void { |
| 480 | 340 | 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) | |
| 482 | 342 | else |
| 483 | os.waitpid(self.pid, 0); | |
| 343 | os.waitpid(self.id, 0); | |
| 484 | 344 | const status = res.status; |
| 485 | 345 | self.cleanupStreams(); |
| 486 | 346 | self.handleWaitResult(status); |
| ... | ... | @@ -638,7 +498,7 @@ pub const ChildProcess = struct { |
| 638 | 498 | self.stderr = null; |
| 639 | 499 | } |
| 640 | 500 | |
| 641 | self.pid = pid; | |
| 501 | self.id = pid; | |
| 642 | 502 | self.term = null; |
| 643 | 503 | |
| 644 | 504 | if (self.stdin_behavior == StdIo.Pipe) { |
| ... | ... | @@ -812,7 +672,7 @@ pub const ChildProcess = struct { |
| 812 | 672 | self.stderr = null; |
| 813 | 673 | } |
| 814 | 674 | |
| 815 | self.pid = pid; | |
| 675 | self.id = pid; | |
| 816 | 676 | self.err_pipe = err_pipe; |
| 817 | 677 | self.term = null; |
| 818 | 678 | |
| ... | ... | @@ -1078,7 +938,7 @@ pub const ChildProcess = struct { |
| 1078 | 938 | self.stderr = null; |
| 1079 | 939 | } |
| 1080 | 940 | |
| 1081 | self.handle = piProcInfo.hProcess; | |
| 941 | self.id = piProcInfo.hProcess; | |
| 1082 | 942 | self.thread_handle = piProcInfo.hThread; |
| 1083 | 943 | self.term = null; |
| 1084 | 944 |
lib/std/crypto.zig+2| ... | ... | @@ -47,6 +47,8 @@ pub const auth = struct { |
| 47 | 47 | /// Core functions, that should rarely be used directly by applications. |
| 48 | 48 | pub const core = struct { |
| 49 | 49 | pub const aes = @import("crypto/aes.zig"); |
| 50 | pub const keccak = @import("crypto/keccak_p.zig"); | |
| 51 | ||
| 50 | 52 | pub const Ascon = @import("crypto/ascon.zig").State; |
| 51 | 53 | pub const Gimli = @import("crypto/gimli.zig").State; |
| 52 | 54 | pub const Xoodoo = @import("crypto/xoodoo.zig").State; |
lib/std/crypto/25519/field.zig+1-1| ... | ... | @@ -287,7 +287,7 @@ pub const Fe = struct { |
| 287 | 287 | return _carry128(&r); |
| 288 | 288 | } |
| 289 | 289 | |
| 290 | inline fn _sq(a: Fe, comptime double: bool) Fe { | |
| 290 | fn _sq(a: Fe, comptime double: bool) Fe { | |
| 291 | 291 | var ax: [5]u128 = undefined; |
| 292 | 292 | var r: [5]u128 = undefined; |
| 293 | 293 | comptime var i = 0; |
lib/std/crypto/benchmark.zig+2| ... | ... | @@ -25,6 +25,8 @@ const hashes = [_]Crypto{ |
| 25 | 25 | Crypto{ .ty = crypto.hash.sha2.Sha512, .name = "sha512" }, |
| 26 | 26 | Crypto{ .ty = crypto.hash.sha3.Sha3_256, .name = "sha3-256" }, |
| 27 | 27 | 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" }, | |
| 28 | 30 | Crypto{ .ty = crypto.hash.Gimli, .name = "gimli-hash" }, |
| 29 | 31 | Crypto{ .ty = crypto.hash.blake2.Blake2s256, .name = "blake2s" }, |
| 30 | 32 | Crypto{ .ty = crypto.hash.blake2.Blake2b512, .name = "blake2b" }, |
lib/std/crypto/keccak_p.zig created+277| ... | ... | @@ -0,0 +1,277 @@ |
| 1 | const std = @import("std"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const math = std.math; | |
| 4 | const mem = std.mem; | |
| 5 | ||
| 6 | /// The Keccak-f permutation. | |
| 7 | pub 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. | |
| 196 | pub 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 | ||
| 260 | test "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 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const mem = std.mem; | |
| 1 | const std = @import("std"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | 3 | const math = std.math; |
| 4 | const debug = std.debug; | |
| 5 | const htest = @import("test.zig"); | |
| 4 | const mem = std.mem; | |
| 5 | ||
| 6 | const KeccakState = std.crypto.core.keccak.State; | |
| 7 | ||
| 8 | pub const Sha3_224 = Keccak(1600, 224, 0x06, 24); | |
| 9 | pub const Sha3_256 = Keccak(1600, 256, 0x06, 24); | |
| 10 | pub const Sha3_384 = Keccak(1600, 384, 0x06, 24); | |
| 11 | pub const Sha3_512 = Keccak(1600, 512, 0x06, 24); | |
| 12 | ||
| 13 | pub const Keccak256 = Keccak(1600, 256, 0x01, 24); | |
| 14 | pub const Keccak512 = Keccak(1600, 512, 0x01, 24); | |
| 15 | pub const Keccak_256 = @compileError("Deprecated: use `Keccak256` instead"); | |
| 16 | pub const Keccak_512 = @compileError("Deprecated: use `Keccak512` instead"); | |
| 17 | ||
| 18 | pub const Shake128 = Shake(128); | |
| 19 | pub const Shake256 = Shake(256); | |
| 20 | ||
| 21 | /// A generic Keccak hash function. | |
| 22 | pub 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 | |
| 6 | 24 | |
| 7 | pub const Sha3_224 = Keccak(224, 0x06); | |
| 8 | pub const Sha3_256 = Keccak(256, 0x06); | |
| 9 | pub const Sha3_384 = Keccak(384, 0x06); | |
| 10 | pub const Sha3_512 = Keccak(512, 0x06); | |
| 11 | pub const Keccak_256 = Keccak(256, 0x01); | |
| 12 | pub const Keccak_512 = Keccak(512, 0x01); | |
| 25 | const State = KeccakState(f, output_bits * 2, delim, rounds); | |
| 13 | 26 | |
| 14 | fn Keccak(comptime bits: usize, comptime delim: u8) type { | |
| 15 | 27 | return struct { |
| 16 | 28 | const Self = @This(); |
| 29 | ||
| 30 | st: State = .{}, | |
| 31 | ||
| 17 | 32 | /// The output length, in bytes. |
| 18 | pub const digest_length = bits / 8; | |
| 33 | pub const digest_length = output_bits / 8; | |
| 19 | 34 | /// The block length, or rate, in bytes. |
| 20 | pub const block_length = 200 - bits / 4; | |
| 35 | pub const block_length = State.rate; | |
| 21 | 36 | /// Keccak does not have any options. |
| 22 | 37 | pub const Options = struct {}; |
| 23 | 38 | |
| 24 | s: [200]u8, | |
| 25 | offset: usize, | |
| 26 | ||
| 39 | /// Initialize a Keccak hash function. | |
| 27 | 40 | pub fn init(options: Options) Self { |
| 28 | 41 | _ = options; |
| 29 | return Self{ .s = [_]u8{0} ** 200, .offset = 0 }; | |
| 42 | return Self{}; | |
| 30 | 43 | } |
| 31 | 44 | |
| 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); | |
| 36 | 50 | } |
| 37 | 51 | |
| 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); | |
| 61 | 55 | } |
| 62 | 56 | |
| 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..]); | |
| 82 | 61 | } |
| 83 | 62 | |
| 84 | 63 | pub const Error = error{}; |
| ... | ... | @@ -95,87 +74,101 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type { |
| 95 | 74 | }; |
| 96 | 75 | } |
| 97 | 76 | |
| 98 | const 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 | ||
| 107 | const 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 | ||
| 111 | const 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 | ||
| 115 | const M5 = [_]usize{ | |
| 116 | 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, | |
| 117 | }; | |
| 118 | ||
| 119 | fn 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. | |
| 78 | pub 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); | |
| 124 | 82 | |
| 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(); | |
| 128 | 85 | |
| 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, | |
| 132 | 90 | |
| 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{}; | |
| 138 | 102 | } |
| 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); | |
| 146 | 110 | } |
| 147 | 111 | |
| 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); | |
| 155 | 115 | } |
| 156 | 116 | |
| 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 | } | |
| 163 | 136 | } |
| 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; | |
| 167 | 146 | } |
| 168 | 147 | } |
| 169 | 148 | |
| 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 | } | |
| 173 | 155 | |
| 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 | }; | |
| 177 | 168 | } |
| 178 | 169 | |
| 170 | const htest = @import("test.zig"); | |
| 171 | ||
| 179 | 172 | test "sha3-224 single" { |
| 180 | 173 | try htest.assertEqualHash(Sha3_224, "6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7", ""); |
| 181 | 174 | try htest.assertEqualHash(Sha3_224, "e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", "abc"); |
| ... | ... | @@ -309,13 +302,49 @@ test "sha3-512 aligned final" { |
| 309 | 302 | } |
| 310 | 303 | |
| 311 | 304 | test "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"); | |
| 315 | 308 | } |
| 316 | 309 | |
| 317 | 310 | test "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 | ||
| 316 | test "SHAKE-128 single" { | |
| 317 | var out: [10]u8 = undefined; | |
| 318 | Shake128.hash("hello123", &out, .{}); | |
| 319 | try htest.assertEqual("1b85861510bc4d8e467d", &out); | |
| 320 | } | |
| 321 | ||
| 322 | test "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 | ||
| 331 | test "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 | ||
| 346 | test "SHAKE-256 single" { | |
| 347 | var out: [10]u8 = undefined; | |
| 348 | Shake256.hash("hello123", &out, .{}); | |
| 349 | try htest.assertEqual("ade612ba265f92de4a37", &out); | |
| 321 | 350 | } |
lib/std/fs.zig+5| ... | ... | @@ -11,6 +11,11 @@ const math = std.math; |
| 11 | 11 | |
| 12 | 12 | const is_darwin = builtin.os.tag.isDarwin(); |
| 13 | 13 | |
| 14 | pub const has_executable_bit = switch (builtin.os.tag) { | |
| 15 | .windows, .wasi => false, | |
| 16 | else => true, | |
| 17 | }; | |
| 18 | ||
| 14 | 19 | pub const path = @import("fs/path.zig"); |
| 15 | 20 | pub const File = @import("fs/file.zig").File; |
| 16 | 21 | pub const wasi = @import("fs/wasi.zig"); |
lib/std/fs/file.zig+6| ... | ... | @@ -48,6 +48,12 @@ pub const File = struct { |
| 48 | 48 | Unknown, |
| 49 | 49 | }; |
| 50 | 50 | |
| 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. | |
| 51 | 57 | pub const default_mode = switch (builtin.os.tag) { |
| 52 | 58 | .windows => 0, |
| 53 | 59 | .wasi => 0, |
lib/std/hash_map.zig+5-4| ... | ... | @@ -1595,16 +1595,17 @@ pub fn HashMapUnmanaged( |
| 1595 | 1595 | self.available = 0; |
| 1596 | 1596 | } |
| 1597 | 1597 | |
| 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 { | |
| 1601 | 1601 | _ = self; |
| 1602 | 1602 | _ = hdr; |
| 1603 | _ = entry; | |
| 1603 | 1604 | } |
| 1604 | 1605 | |
| 1605 | 1606 | comptime { |
| 1606 | 1607 | if (builtin.mode == .Debug) { |
| 1607 | _ = gdbHelper; | |
| 1608 | _ = dbHelper; | |
| 1608 | 1609 | } |
| 1609 | 1610 | } |
| 1610 | 1611 | }; |
lib/std/heap/general_purpose_allocator.zig+1| ... | ... | @@ -423,6 +423,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 423 | 423 | } |
| 424 | 424 | } else struct {}; |
| 425 | 425 | |
| 426 | /// Returns true if there were leaks; false otherwise. | |
| 426 | 427 | pub fn deinit(self: *Self) bool { |
| 427 | 428 | const leaks = if (config.safety) self.detectLeaks() else false; |
| 428 | 429 | if (config.retain_metadata) { |
lib/std/io.zig+250| ... | ... | @@ -168,6 +168,256 @@ test "null_writer" { |
| 168 | 168 | null_writer.writeAll("yay" ** 10) catch |err| switch (err) {}; |
| 169 | 169 | } |
| 170 | 170 | |
| 171 | pub 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 | ||
| 211 | pub const PollFifo = std.fifo.LinearFifo(u8, .Dynamic); | |
| 212 | ||
| 213 | pub 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 | ||
| 380 | fn 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. | |
| 401 | pub 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 | ||
| 171 | 421 | test { |
| 172 | 422 | _ = @import("io/bit_reader.zig"); |
| 173 | 423 | _ = @import("io/bit_writer.zig"); |
lib/std/multi_array_list.zig+23-6| ... | ... | @@ -131,8 +131,8 @@ pub fn MultiArrayList(comptime S: type) type { |
| 131 | 131 | .capacity = self.capacity, |
| 132 | 132 | }; |
| 133 | 133 | 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; | |
| 136 | 136 | ptr += field_size * self.capacity; |
| 137 | 137 | } |
| 138 | 138 | return result; |
| ... | ... | @@ -446,16 +446,33 @@ pub fn MultiArrayList(comptime S: type) type { |
| 446 | 446 | return meta.fieldInfo(S, field).type; |
| 447 | 447 | } |
| 448 | 448 | |
| 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 { | |
| 452 | 468 | _ = self; |
| 453 | 469 | _ = child; |
| 470 | _ = entry; | |
| 454 | 471 | } |
| 455 | 472 | |
| 456 | 473 | comptime { |
| 457 | 474 | if (builtin.mode == .Debug) { |
| 458 | _ = gdbHelper; | |
| 475 | _ = dbHelper; | |
| 459 | 476 | } |
| 460 | 477 | } |
| 461 | 478 | }; |
lib/std/os.zig+32-3| ... | ... | @@ -302,8 +302,7 @@ pub const FChmodError = error{ |
| 302 | 302 | /// successfully, or must have the effective user ID matching the owner |
| 303 | 303 | /// of the file. |
| 304 | 304 | pub 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"); | |
| 307 | 306 | |
| 308 | 307 | while (true) { |
| 309 | 308 | const res = system.fchmod(fd, mode); |
| ... | ... | @@ -311,8 +310,38 @@ pub fn fchmod(fd: fd_t, mode: mode_t) FChmodError!void { |
| 311 | 310 | switch (system.getErrno(res)) { |
| 312 | 311 | .SUCCESS => return, |
| 313 | 312 | .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 | ||
| 329 | const FChmodAtError = FChmodError || error{ | |
| 330 | NameTooLong, | |
| 331 | }; | |
| 315 | 332 | |
| 333 | pub 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, | |
| 316 | 345 | .FAULT => unreachable, |
| 317 | 346 | .INVAL => unreachable, |
| 318 | 347 | .ACCES => return error.AccessDenied, |
lib/std/os/linux.zig+18| ... | ... | @@ -769,6 +769,20 @@ pub fn fchmod(fd: i32, mode: mode_t) usize { |
| 769 | 769 | return syscall2(.fchmod, @bitCast(usize, @as(isize, fd)), mode); |
| 770 | 770 | } |
| 771 | 771 | |
| 772 | pub 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 | ||
| 772 | 786 | pub fn fchown(fd: i32, owner: uid_t, group: gid_t) usize { |
| 773 | 787 | if (@hasField(SYS, "fchown32")) { |
| 774 | 788 | 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 { |
| 777 | 791 | } |
| 778 | 792 | } |
| 779 | 793 | |
| 794 | pub 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 | ||
| 780 | 798 | /// Can only be called on 32 bit systems. For 64 bit see `lseek`. |
| 781 | 799 | pub fn llseek(fd: i32, offset: u64, result: ?*u64, whence: usize) usize { |
| 782 | 800 | // NOTE: The offset parameter splitting is independent from the target |
lib/std/os/test.zig+43-29| ... | ... | @@ -531,17 +531,17 @@ test "memfd_create" { |
| 531 | 531 | else => return error.SkipZigTest, |
| 532 | 532 | } |
| 533 | 533 | |
| 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) { | |
| 535 | 535 | // Related: https://github.com/ziglang/zig/issues/4019 |
| 536 | 536 | error.SystemOutdated => return error.SkipZigTest, |
| 537 | 537 | else => |e| return e, |
| 538 | 538 | }; |
| 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); | |
| 542 | 542 | |
| 543 | 543 | var buf: [10]u8 = undefined; |
| 544 | const bytes_read = try std.os.read(fd, &buf); | |
| 544 | const bytes_read = try os.read(fd, &buf); | |
| 545 | 545 | try expect(bytes_read == 4); |
| 546 | 546 | try expect(mem.eql(u8, buf[0..4], "test")); |
| 547 | 547 | } |
| ... | ... | @@ -688,7 +688,7 @@ test "signalfd" { |
| 688 | 688 | .linux, .solaris => {}, |
| 689 | 689 | else => return error.SkipZigTest, |
| 690 | 690 | } |
| 691 | _ = std.os.signalfd; | |
| 691 | _ = os.signalfd; | |
| 692 | 692 | } |
| 693 | 693 | |
| 694 | 694 | test "sync" { |
| ... | ... | @@ -757,11 +757,11 @@ test "shutdown socket" { |
| 757 | 757 | if (native_os == .wasi) |
| 758 | 758 | return error.SkipZigTest; |
| 759 | 759 | if (native_os == .windows) { |
| 760 | _ = try std.os.windows.WSAStartup(2, 2); | |
| 760 | _ = try os.windows.WSAStartup(2, 2); | |
| 761 | 761 | } |
| 762 | 762 | defer { |
| 763 | 763 | if (native_os == .windows) { |
| 764 | std.os.windows.WSACleanup() catch unreachable; | |
| 764 | os.windows.WSACleanup() catch unreachable; | |
| 765 | 765 | } |
| 766 | 766 | } |
| 767 | 767 | const sock = try os.socket(os.AF.INET, os.SOCK.STREAM, 0); |
| ... | ... | @@ -855,13 +855,13 @@ test "dup & dup2" { |
| 855 | 855 | var file = try tmp.dir.createFile("os_dup_test", .{}); |
| 856 | 856 | defer file.close(); |
| 857 | 857 | |
| 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) }; | |
| 859 | 859 | defer duped.close(); |
| 860 | 860 | try duped.writeAll("dup"); |
| 861 | 861 | |
| 862 | 862 | // Tests aren't run in parallel so using the next fd shouldn't be an issue. |
| 863 | 863 | const new_fd = duped.handle + 1; |
| 864 | try std.os.dup2(file.handle, new_fd); | |
| 864 | try os.dup2(file.handle, new_fd); | |
| 865 | 865 | var dup2ed = std.fs.File{ .handle = new_fd }; |
| 866 | 866 | defer dup2ed.close(); |
| 867 | 867 | try dup2ed.writeAll("dup2"); |
| ... | ... | @@ -909,46 +909,46 @@ test "POSIX file locking with fcntl" { |
| 909 | 909 | const fd = file.handle; |
| 910 | 910 | |
| 911 | 911 | // 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)); | |
| 914 | 914 | 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)); | |
| 917 | 917 | |
| 918 | 918 | // Check the locks in a child process: |
| 919 | const pid = try std.os.fork(); | |
| 919 | const pid = try os.fork(); | |
| 920 | 920 | if (pid == 0) { |
| 921 | 921 | // child expects be denied the exclusive lock: |
| 922 | 922 | 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))); | |
| 925 | 925 | // child expects to get the shared lock: |
| 926 | 926 | 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)); | |
| 929 | 929 | // child waits for the exclusive lock in order to test deadlock: |
| 930 | 930 | 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)); | |
| 933 | 933 | // child exits without continuing: |
| 934 | std.os.exit(0); | |
| 934 | os.exit(0); | |
| 935 | 935 | } else { |
| 936 | 936 | // parent waits for child to get shared lock: |
| 937 | 937 | std.time.sleep(1 * std.time.ns_per_ms); |
| 938 | 938 | // parent expects deadlock when attempting to upgrade the shared lock to exclusive: |
| 939 | 939 | 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))); | |
| 942 | 942 | // parent releases exclusive lock: |
| 943 | 943 | 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)); | |
| 946 | 946 | // parent releases shared lock: |
| 947 | 947 | 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)); | |
| 950 | 950 | // parent waits for child: |
| 951 | const result = std.os.waitpid(pid, 0); | |
| 951 | const result = os.waitpid(pid, 0); | |
| 952 | 952 | try expect(result.status == 0 * 256); |
| 953 | 953 | } |
| 954 | 954 | } |
| ... | ... | @@ -1182,3 +1182,17 @@ test "pwrite with empty buffer" { |
| 1182 | 1182 | |
| 1183 | 1183 | _ = try os.pwrite(file.handle, bytes, 0); |
| 1184 | 1184 | } |
| 1185 | ||
| 1186 | test "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: |
| 2068 | 2068 | ws2_32.SIO_GET_EXTENSION_FUNCTION_POINTER, |
| 2069 | 2069 | @ptrCast(*const anyopaque, &guid), |
| 2070 | 2070 | @sizeOf(GUID), |
| 2071 | @intToPtr(?*anyopaque, @ptrToInt(function)), | |
| 2071 | @intToPtr(?*anyopaque, @ptrToInt(&function)), | |
| 2072 | 2072 | @sizeOf(T), |
| 2073 | 2073 | &num_bytes, |
| 2074 | 2074 | null, |
lib/std/os/windows/test.zig+25| ... | ... | @@ -63,3 +63,28 @@ test "removeDotDirs" { |
| 63 | 63 | try testRemoveDotDirs("a\\b\\..\\", "a\\"); |
| 64 | 64 | try testRemoveDotDirs("a\\b\\..\\c", "a\\c"); |
| 65 | 65 | } |
| 66 | ||
| 67 | test "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; |
| 9 | 9 | const testing = std.testing; |
| 10 | 10 | const child_process = @import("child_process.zig"); |
| 11 | 11 | |
| 12 | pub const Child = child_process.ChildProcess; | |
| 12 | 13 | pub const abort = os.abort; |
| 13 | 14 | pub const exit = os.exit; |
| 14 | 15 | pub const changeCurDir = os.chdir; |
lib/std/std.zig+1| ... | ... | @@ -12,6 +12,7 @@ pub const BoundedArray = @import("bounded_array.zig").BoundedArray; |
| 12 | 12 | pub const Build = @import("Build.zig"); |
| 13 | 13 | pub const BufMap = @import("buf_map.zig").BufMap; |
| 14 | 14 | pub const BufSet = @import("buf_set.zig").BufSet; |
| 15 | /// Deprecated: use `process.Child`. | |
| 15 | 16 | pub const ChildProcess = @import("child_process.zig").ChildProcess; |
| 16 | 17 | pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringMap; |
| 17 | 18 | pub 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 |
| 2342 | 2342 | |
| 2343 | 2343 | .while_simple, |
| 2344 | 2344 | .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), | |
| 2346 | 2346 | |
| 2347 | 2347 | .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), | |
| 2349 | 2349 | |
| 2350 | 2350 | else => noreturn_src_node = try unusedResultExpr(gz, scope, inner_node), |
| 2351 | 2351 | // zig fmt: on |
src/Package.zig+26-8| ... | ... | @@ -225,6 +225,7 @@ pub fn fetchAndAddDependencies( |
| 225 | 225 | build_roots_source: *std.ArrayList(u8), |
| 226 | 226 | name_prefix: []const u8, |
| 227 | 227 | color: main.Color, |
| 228 | all_modules: *AllModules, | |
| 228 | 229 | ) !void { |
| 229 | 230 | const max_bytes = 10 * 1024 * 1024; |
| 230 | 231 | const gpa = thread_pool.allocator; |
| ... | ... | @@ -291,6 +292,7 @@ pub fn fetchAndAddDependencies( |
| 291 | 292 | report, |
| 292 | 293 | build_roots_source, |
| 293 | 294 | fqn, |
| 295 | all_modules, | |
| 294 | 296 | ); |
| 295 | 297 | |
| 296 | 298 | try pkg.fetchAndAddDependencies( |
| ... | ... | @@ -304,6 +306,7 @@ pub fn fetchAndAddDependencies( |
| 304 | 306 | build_roots_source, |
| 305 | 307 | sub_prefix, |
| 306 | 308 | color, |
| 309 | all_modules, | |
| 307 | 310 | ); |
| 308 | 311 | |
| 309 | 312 | try add(pkg, gpa, fqn, sub_pkg); |
| ... | ... | @@ -402,6 +405,11 @@ const Report = struct { |
| 402 | 405 | } |
| 403 | 406 | }; |
| 404 | 407 | |
| 408 | const hex_multihash_len = 2 * Manifest.multihash_len; | |
| 409 | const MultiHashHexDigest = [hex_multihash_len]u8; | |
| 410 | /// This is to avoid creating multiple modules for the same build.zig file. | |
| 411 | pub const AllModules = std.AutoHashMapUnmanaged(MultiHashHexDigest, *Package); | |
| 412 | ||
| 405 | 413 | fn fetchAndUnpack( |
| 406 | 414 | thread_pool: *ThreadPool, |
| 407 | 415 | http_client: *std.http.Client, |
| ... | ... | @@ -410,6 +418,7 @@ fn fetchAndUnpack( |
| 410 | 418 | report: Report, |
| 411 | 419 | build_roots_source: *std.ArrayList(u8), |
| 412 | 420 | fqn: []const u8, |
| 421 | all_modules: *AllModules, | |
| 413 | 422 | ) !*Package { |
| 414 | 423 | const gpa = http_client.allocator; |
| 415 | 424 | const s = fs.path.sep_str; |
| ... | ... | @@ -417,9 +426,24 @@ fn fetchAndUnpack( |
| 417 | 426 | // Check if the expected_hash is already present in the global package |
| 418 | 427 | // cache, and thereby avoid both fetching and unpacking. |
| 419 | 428 | if (dep.hash) |h| cached: { |
| 420 | const hex_multihash_len = 2 * Manifest.multihash_len; | |
| 421 | 429 | const hex_digest = h[0..hex_multihash_len]; |
| 422 | 430 | 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 | ||
| 423 | 447 | var pkg_dir = global_cache_directory.handle.openDir(pkg_dir_sub_path, .{}) catch |err| switch (err) { |
| 424 | 448 | error.FileNotFound => break :cached, |
| 425 | 449 | else => |e| return e, |
| ... | ... | @@ -432,13 +456,6 @@ fn fetchAndUnpack( |
| 432 | 456 | const owned_src_path = try gpa.dupe(u8, build_zig_basename); |
| 433 | 457 | errdefer gpa.free(owned_src_path); |
| 434 | 458 | |
| 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 | ||
| 442 | 459 | ptr.* = .{ |
| 443 | 460 | .root_src_directory = .{ |
| 444 | 461 | .path = build_root, |
| ... | ... | @@ -448,6 +465,7 @@ fn fetchAndUnpack( |
| 448 | 465 | .root_src_path = owned_src_path, |
| 449 | 466 | }; |
| 450 | 467 | |
| 468 | gop.value_ptr.* = ptr; | |
| 451 | 469 | return ptr; |
| 452 | 470 | } |
| 453 | 471 |
src/Sema.zig+2-2| ... | ... | @@ -17328,11 +17328,11 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 17328 | 17328 | break :blk abi_align; |
| 17329 | 17329 | } else 0; |
| 17330 | 17330 | |
| 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: { | |
| 17332 | 17332 | const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]); |
| 17333 | 17333 | extra_i += 1; |
| 17334 | 17334 | 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; | |
| 17336 | 17336 | |
| 17337 | 17337 | const bit_offset = if (inst_data.flags.has_bit_range) blk: { |
| 17338 | 17338 | 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 { |
| 4177 | 4177 | } |
| 4178 | 4178 | |
| 4179 | 4179 | fn 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; | |
| 4182 | 4184 | |
| 4183 | 4185 | const ty = self.air.typeOfIndex(inst); |
| 4184 | 4186 | const tag = self.air.instructions.items(.tag)[inst]; |
src/arch/arm/CodeGen.zig+4-2| ... | ... | @@ -4125,8 +4125,10 @@ fn genInlineMemsetCode( |
| 4125 | 4125 | } |
| 4126 | 4126 | |
| 4127 | 4127 | fn 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; | |
| 4130 | 4132 | |
| 4131 | 4133 | const ty = self.air.typeOfIndex(inst); |
| 4132 | 4134 | 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 |
| 3827 | 3827 | } |
| 3828 | 3828 | |
| 3829 | 3829 | fn 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; | |
| 3832 | 3834 | |
| 3833 | 3835 | const ty = self.air.typeOfIndex(inst); |
| 3834 | 3836 | const mcv = self.args[arg_index]; |
src/codegen/llvm.zig+7-2| ... | ... | @@ -2219,11 +2219,16 @@ pub const Object = struct { |
| 2219 | 2219 | )); |
| 2220 | 2220 | } |
| 2221 | 2221 | |
| 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 | }; | |
| 2223 | 2228 | |
| 2224 | 2229 | const union_di_ty = dib.createUnionType( |
| 2225 | 2230 | compile_unit_scope, |
| 2226 | union_name, | |
| 2231 | union_name.ptr, | |
| 2227 | 2232 | null, // file |
| 2228 | 2233 | 0, // line |
| 2229 | 2234 | 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 |
| 345 | 345 | } |
| 346 | 346 | |
| 347 | 347 | // 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 | }); | |
| 349 | 359 | wasm_bin.base.file = file; |
| 350 | 360 | wasm_bin.name = sub_path; |
| 351 | 361 | |
| ... | ... | @@ -3750,10 +3760,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! |
| 3750 | 3760 | if (wasm.base.options.import_symbols) { |
| 3751 | 3761 | try argv.append("--allow-undefined"); |
| 3752 | 3762 | } |
| 3753 | try argv.appendSlice(&[_][]const u8{ | |
| 3754 | "-o", | |
| 3755 | full_out_path, | |
| 3756 | }); | |
| 3763 | try argv.appendSlice(&.{ "-o", full_out_path }); | |
| 3757 | 3764 | |
| 3758 | 3765 | if (target.cpu.arch == .wasm64) { |
| 3759 | 3766 | try argv.append("-mwasm64"); |
| ... | ... | @@ -3889,6 +3896,21 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! |
| 3889 | 3896 | } |
| 3890 | 3897 | } |
| 3891 | 3898 | } |
| 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 | } | |
| 3892 | 3914 | } |
| 3893 | 3915 | |
| 3894 | 3916 | if (!wasm.base.options.disable_lld_caching) { |
src/main.zig+34-4| ... | ... | @@ -4013,6 +4013,7 @@ pub const usage_build = |
| 4013 | 4013 | \\ --cache-dir [path] Override path to local Zig cache directory |
| 4014 | 4014 | \\ --global-cache-dir [path] Override path to global Zig cache directory |
| 4015 | 4015 | \\ --zig-lib-dir [arg] Override path to Zig lib directory |
| 4016 | \\ --build-runner [file] Override path to build runner | |
| 4016 | 4017 | \\ --prominent-compile-errors Output compile errors formatted for a human to read |
| 4017 | 4018 | \\ -h, --help Print this help and exit |
| 4018 | 4019 | \\ |
| ... | ... | @@ -4031,6 +4032,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4031 | 4032 | var override_lib_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIB_DIR"); |
| 4032 | 4033 | var override_global_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_GLOBAL_CACHE_DIR"); |
| 4033 | 4034 | 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"); | |
| 4034 | 4036 | var child_argv = std.ArrayList([]const u8).init(arena); |
| 4035 | 4037 | var reference_trace: ?u32 = null; |
| 4036 | 4038 | var debug_compile_errors = false; |
| ... | ... | @@ -4065,6 +4067,11 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4065 | 4067 | override_lib_dir = args[i]; |
| 4066 | 4068 | try child_argv.appendSlice(&[_][]const u8{ arg, args[i] }); |
| 4067 | 4069 | 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; | |
| 4068 | 4075 | } else if (mem.eql(u8, arg, "--cache-dir")) { |
| 4069 | 4076 | if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg}); |
| 4070 | 4077 | i += 1; |
| ... | ... | @@ -4197,10 +4204,29 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4197 | 4204 | try thread_pool.init(gpa); |
| 4198 | 4205 | defer thread_pool.deinit(); |
| 4199 | 4206 | |
| 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 | }; | |
| 4204 | 4230 | |
| 4205 | 4231 | if (!build_options.omit_pkg_fetching_code) { |
| 4206 | 4232 | var http_client: std.http.Client = .{ .allocator = gpa }; |
| ... | ... | @@ -4218,6 +4244,9 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4218 | 4244 | var build_roots_source = std.ArrayList(u8).init(gpa); |
| 4219 | 4245 | defer build_roots_source.deinit(); |
| 4220 | 4246 | |
| 4247 | var all_modules: Package.AllModules = .{}; | |
| 4248 | defer all_modules.deinit(gpa); | |
| 4249 | ||
| 4221 | 4250 | // Here we borrow main package's table and will replace it with a fresh |
| 4222 | 4251 | // one after this process completes. |
| 4223 | 4252 | main_pkg.fetchAndAddDependencies( |
| ... | ... | @@ -4231,6 +4260,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4231 | 4260 | &build_roots_source, |
| 4232 | 4261 | "", |
| 4233 | 4262 | color, |
| 4263 | &all_modules, | |
| 4234 | 4264 | ) catch |err| switch (err) { |
| 4235 | 4265 | error.PackageFetchFailed => process.exit(1), |
| 4236 | 4266 | else => |e| return e, |
src/target.zig+3-2| ... | ... | @@ -648,8 +648,9 @@ pub fn defaultAddressSpace( |
| 648 | 648 | function, |
| 649 | 649 | }, |
| 650 | 650 | ) 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; | |
| 653 | 654 | return .generic; |
| 654 | 655 | } |
| 655 | 656 |
src/type.zig+30| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | |
| 2 | 3 | const Value = @import("value.zig").Value; |
| 3 | 4 | const assert = std.debug.assert; |
| 4 | 5 | const Allocator = std.mem.Allocator; |
| ... | ... | @@ -6694,4 +6695,33 @@ pub const Type = extern union { |
| 6694 | 6695 | /// This is only used for comptime asserts. Bump this number when you make a change |
| 6695 | 6696 | /// to packed struct layout to find out all the places in the codebase you need to edit! |
| 6696 | 6697 | 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 | } | |
| 6697 | 6727 | }; |
src/value.zig+30| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | |
| 2 | 3 | const Type = @import("type.zig").Type; |
| 3 | 4 | const log2 = std.math.log2; |
| 4 | 5 | const assert = std.debug.assert; |
| ... | ... | @@ -5584,6 +5585,35 @@ pub const Value = extern union { |
| 5584 | 5585 | ri.* = @intToEnum(RuntimeIndex, @enumToInt(ri.*) + 1); |
| 5585 | 5586 | } |
| 5586 | 5587 | }; |
| 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 | } | |
| 5587 | 5617 | }; |
| 5588 | 5618 | |
| 5589 | 5619 | var negative_one_payload: Value.Payload.I64 = .{ |
test/cases/compile_errors/for_loop_break_value_ignored.zig created+15| ... | ... | @@ -0,0 +1,15 @@ |
| 1 | fn returns() usize { | |
| 2 | return 2; | |
| 3 | } | |
| 4 | ||
| 5 | export 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 @@ |
| 1 | fn returns() usize { | |
| 2 | return 2; | |
| 3 | } | |
| 4 | ||
| 5 | export fn f1() void { | |
| 6 | var a: bool = true; | |
| 7 | while (a) { | |
| 8 | break returns(); | |
| 9 | } | |
| 10 | } | |
| 11 | ||
| 12 | export 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 | |
| 7 | import lldb | |
| 8 | import re | |
| 9 | ||
| 10 | page_size = 1 << 12 | |
| 11 | ||
| 12 | def log2_int(i): return i.bit_length() - 1 | |
| 13 | ||
| 14 | # Define Zig Language | |
| 15 | ||
| 16 | zig_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 | } | |
| 67 | zig_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 | } | |
| 98 | zig_integer_type = re.compile('[iu][1-9][0-9]+') | |
| 99 | zig_identifier_regex = re.compile('[A-Z_a-z][0-9A-Z_a-z]*') | |
| 100 | def 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) | |
| 101 | def zig_IsFieldName(string): return string not in zig_keywords and zig_identifier_regex.fullmatch(string) | |
| 102 | ||
| 103 | class 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 | ||
| 122 | def 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 | |
| 136 | def zig_String_SummaryProvider(value, _=None): return '"%s"' % zig_String_decode(value) | |
| 137 | def zig_String_AsIdentifier(value, pred): | |
| 138 | string = zig_String_decode(value) | |
| 139 | return string if pred(string) else '@"%s"' % string | |
| 140 | ||
| 141 | class 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 | |
| 151 | def zig_Optional_SummaryProvider(value, _=None): | |
| 152 | child = value.GetChildMemberWithName('child') | |
| 153 | return child or 'null' | |
| 154 | ||
| 155 | class 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 | ||
| 169 | class 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 | ||
| 196 | class 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 | ||
| 231 | class 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 | |
| 269 | def 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 | |
| 275 | class 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 | ||
| 289 | class 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 | ||
| 304 | def 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 | ||
| 308 | class 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 | ||
| 323 | class 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 | ||
| 351 | def Module_Decl_name(decl): | |
| 352 | error = lldb.SBError() | |
| 353 | return decl.process.ReadCStringFromMemory(decl.GetChildMemberWithName('name').deref.load_addr, 256, error) | |
| 354 | ||
| 355 | def 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 | ||
| 360 | def Module_Decl_RenderFullyQualifiedName(decl): return '.'.join((Module_Namespace_RenderFullyQualifiedName(decl.GetChildMemberWithName('src_namespace')), Module_Decl_name(decl))) | |
| 361 | ||
| 362 | def OwnerDecl_RenderFullyQualifiedName(payload): return Module_Decl_RenderFullyQualifiedName(payload.GetChildMemberWithName('owner_decl').GetChildMemberWithName('decl')) | |
| 363 | ||
| 364 | def 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 | ||
| 391 | def 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 | ||
| 402 | def 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 | ||
| 406 | type_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 | ||
| 476 | def 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 | ||
| 483 | def 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 | ||
| 487 | value_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 | ||
| 541 | def 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 | ||
| 546 | def MultiArrayList_Entry(type): return '^multi_array_list\\.MultiArrayList\\(%s\\)\\.Entry__struct_[1-9][0-9]*$' % type | |
| 547 | ||
| 548 | def __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 @@ |
| 3 | 3 | import re |
| 4 | 4 | import gdb.printing |
| 5 | 5 | |
| 6 | import sys | |
| 7 | from pathlib import Path | |
| 8 | sys.path.insert(0, str(Path(__file__).parent)) | |
| 9 | import stage2_pretty_printers_common as common | |
| 10 | ||
| 11 | ||
| 12 | 6 | class 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 | ||
| 13 | 55 | def __init__(self, val): |
| 14 | 56 | self.val = val |
| 15 | 57 | |
| ... | ... | @@ -17,7 +59,7 @@ class TypePrinter: |
| 17 | 59 | tag_if_small_enough = self.val['tag_if_small_enough'] |
| 18 | 60 | tag_type = tag_if_small_enough.type |
| 19 | 61 | |
| 20 | if tag_if_small_enough < common.Type.no_payload_count: | |
| 62 | if tag_if_small_enough < TypePrinter.no_payload_count: | |
| 21 | 63 | return tag_if_small_enough |
| 22 | 64 | else: |
| 23 | 65 | return self.val['ptr_otherwise'].dereference()['tag'] |
| ... | ... | @@ -27,7 +69,7 @@ class TypePrinter: |
| 27 | 69 | if tag is None: |
| 28 | 70 | return None |
| 29 | 71 | |
| 30 | type_name = common.Type.payload_type_names.get(str(tag)) | |
| 72 | type_name = TypePrinter.payload_type_names.get(str(tag)) | |
| 31 | 73 | if type_name is None: |
| 32 | 74 | return None |
| 33 | 75 | return gdb.lookup_type('struct type.%s' % type_name) |
| ... | ... | @@ -36,12 +78,12 @@ class TypePrinter: |
| 36 | 78 | tag = self.tag() |
| 37 | 79 | if tag is None: |
| 38 | 80 | 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: | |
| 40 | 82 | return '.%s' % str(tag) |
| 41 | 83 | return None |
| 42 | 84 | |
| 43 | 85 | 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: | |
| 45 | 87 | return |
| 46 | 88 | |
| 47 | 89 | yield ('tag', '.%s' % str(self.tag())) |
| ... | ... | @@ -51,6 +93,55 @@ class TypePrinter: |
| 51 | 93 | yield ('payload', self.val['ptr_otherwise'].cast(payload_type.pointer()).dereference()['data']) |
| 52 | 94 | |
| 53 | 95 | class 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 | ||
| 54 | 145 | def __init__(self, val): |
| 55 | 146 | self.val = val |
| 56 | 147 | |
| ... | ... | @@ -58,7 +149,7 @@ class ValuePrinter: |
| 58 | 149 | tag_if_small_enough = self.val['tag_if_small_enough'] |
| 59 | 150 | tag_type = tag_if_small_enough.type |
| 60 | 151 | |
| 61 | if tag_if_small_enough < common.Value.no_payload_count: | |
| 152 | if tag_if_small_enough < ValuePrinter.no_payload_count: | |
| 62 | 153 | return tag_if_small_enough |
| 63 | 154 | else: |
| 64 | 155 | return self.val['ptr_otherwise'].dereference()['tag'] |
| ... | ... | @@ -68,7 +159,7 @@ class ValuePrinter: |
| 68 | 159 | if tag is None: |
| 69 | 160 | return None |
| 70 | 161 | |
| 71 | type_name = Comman.Value.payload_type_names.get(str(tag)) | |
| 162 | type_name = ValuePrinter.payload_type_names.get(str(tag)) | |
| 72 | 163 | if type_name is None: |
| 73 | 164 | return None |
| 74 | 165 | return gdb.lookup_type('struct value.%s' % type_name) |
| ... | ... | @@ -77,12 +168,12 @@ class ValuePrinter: |
| 77 | 168 | tag = self.tag() |
| 78 | 169 | if tag is None: |
| 79 | 170 | 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: | |
| 81 | 172 | return '.%s' % str(tag) |
| 82 | 173 | return None |
| 83 | 174 | |
| 84 | 175 | 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: | |
| 86 | 177 | return |
| 87 | 178 | |
| 88 | 179 | 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. | |
| 3 | import lldb | |
| 4 | import stage2_pretty_printers_common as common | |
| 5 | ||
| 6 | category = 'stage2' | |
| 7 | module = category + '_lldb_pretty_printers' | |
| 8 | ||
| 9 | class 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 | ||
| 31 | class 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 | ||
| 53 | def 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 | ||
| 57 | def __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 @@ |
| 1 | class 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 | ||
| 50 | class 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: |
| 26 | 26 | self.val = val |
| 27 | 27 | |
| 28 | 28 | 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) | |
| 30 | 30 | return helper_fn.type.fields()[1].type.target() |
| 31 | 31 | |
| 32 | 32 | def to_string(self): |
| ... | ... | @@ -65,7 +65,7 @@ class HashMapPrinter: |
| 65 | 65 | self.val = val['unmanaged'] if is_managed else val |
| 66 | 66 | |
| 67 | 67 | 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) | |
| 69 | 69 | return helper_fn.type.fields()[1].type |
| 70 | 70 | |
| 71 | 71 | def header(self): |