| author | |
| committer | |
| log | 03f1ad5007fd747bf386058222f9dfb9a925ef02 |
| tree | 15add24f509ca3c5b2efc08d62419858f2d79c4c |
| parent | 5456eb11078a630afc21d52ebb515ac753764a84 |
| parent | e839250c5156d438f76e7b08e7053e9087fae77c |
53 files changed, 1983 insertions(+), 822 deletions(-)
CMakeLists.txt+5| ... | @@ -46,6 +46,7 @@ message("Configuring zig version ${ZIG_VERSION}") | ... | @@ -46,6 +46,7 @@ message("Configuring zig version ${ZIG_VERSION}") |
| 46 | set(ZIG_STATIC off CACHE BOOL "Attempt to build a static zig executable (not compatible with glibc)") | 46 | set(ZIG_STATIC off CACHE BOOL "Attempt to build a static zig executable (not compatible with glibc)") |
| 47 | set(ZIG_STATIC_LLVM off CACHE BOOL "Prefer linking against static LLVM libraries") | 47 | set(ZIG_STATIC_LLVM off CACHE BOOL "Prefer linking against static LLVM libraries") |
| 48 | set(ZIG_SKIP_INSTALL_LIB_FILES off CACHE BOOL "Disable copying lib/ files to install prefix") | 48 | set(ZIG_SKIP_INSTALL_LIB_FILES off CACHE BOOL "Disable copying lib/ files to install prefix") |
| 49 | set(ZIG_ENABLE_MEM_PROFILE off CACHE BOOL "Activate memory usage instrumentation") | ||
| 49 | 50 | ||
| 50 | if(ZIG_STATIC) | 51 | if(ZIG_STATIC) |
| 51 | set(ZIG_STATIC_LLVM "on") | 52 | set(ZIG_STATIC_LLVM "on") |
| ... | @@ -455,6 +456,7 @@ set(ZIG_SOURCES | ... | @@ -455,6 +456,7 @@ set(ZIG_SOURCES |
| 455 | "${CMAKE_SOURCE_DIR}/src/ir_print.cpp" | 456 | "${CMAKE_SOURCE_DIR}/src/ir_print.cpp" |
| 456 | "${CMAKE_SOURCE_DIR}/src/libc_installation.cpp" | 457 | "${CMAKE_SOURCE_DIR}/src/libc_installation.cpp" |
| 457 | "${CMAKE_SOURCE_DIR}/src/link.cpp" | 458 | "${CMAKE_SOURCE_DIR}/src/link.cpp" |
| 459 | "${CMAKE_SOURCE_DIR}/src/memory_profiling.cpp" | ||
| 458 | "${CMAKE_SOURCE_DIR}/src/os.cpp" | 460 | "${CMAKE_SOURCE_DIR}/src/os.cpp" |
| 459 | "${CMAKE_SOURCE_DIR}/src/parser.cpp" | 461 | "${CMAKE_SOURCE_DIR}/src/parser.cpp" |
| 460 | "${CMAKE_SOURCE_DIR}/src/range_set.cpp" | 462 | "${CMAKE_SOURCE_DIR}/src/range_set.cpp" |
| ... | @@ -628,5 +630,8 @@ set_target_properties(zig PROPERTIES | ... | @@ -628,5 +630,8 @@ set_target_properties(zig PROPERTIES |
| 628 | LINK_FLAGS ${EXE_LDFLAGS} | 630 | LINK_FLAGS ${EXE_LDFLAGS} |
| 629 | ) | 631 | ) |
| 630 | target_link_libraries(zig compiler "${LIBUSERLAND}") | 632 | target_link_libraries(zig compiler "${LIBUSERLAND}") |
| 633 | if(MSVC) | ||
| 634 | target_link_libraries(zig ntdll.lib) | ||
| 635 | endif() | ||
| 631 | add_dependencies(zig zig_build_libuserland) | 636 | add_dependencies(zig zig_build_libuserland) |
| 632 | install(TARGETS zig DESTINATION bin) | 637 | install(TARGETS zig DESTINATION bin) |
doc/docgen.zig+1-1| ... | @@ -51,7 +51,7 @@ pub fn main() !void { | ... | @@ -51,7 +51,7 @@ pub fn main() !void { |
| 51 | var toc = try genToc(allocator, &tokenizer); | 51 | var toc = try genToc(allocator, &tokenizer); |
| 52 | 52 | ||
| 53 | try fs.makePath(allocator, tmp_dir_name); | 53 | try fs.makePath(allocator, tmp_dir_name); |
| 54 | defer fs.deleteTree(allocator, tmp_dir_name) catch {}; | 54 | defer fs.deleteTree(tmp_dir_name) catch {}; |
| 55 | 55 | ||
| 56 | try genHtml(allocator, &tokenizer, &toc, &buffered_out_stream.stream, zig_exe); | 56 | try genHtml(allocator, &tokenizer, &toc, &buffered_out_stream.stream, zig_exe); |
| 57 | try buffered_out_stream.flush(); | 57 | try buffered_out_stream.flush(); |
doc/langref.html.in+2-2| ... | @@ -10086,8 +10086,8 @@ ContainerMembers | ... | @@ -10086,8 +10086,8 @@ ContainerMembers |
| 10086 | <- TestDecl ContainerMembers | 10086 | <- TestDecl ContainerMembers |
| 10087 | / TopLevelComptime ContainerMembers | 10087 | / TopLevelComptime ContainerMembers |
| 10088 | / KEYWORD_pub? TopLevelDecl ContainerMembers | 10088 | / KEYWORD_pub? TopLevelDecl ContainerMembers |
| 10089 | / KEYWORD_pub? ContainerField COMMA ContainerMembers | 10089 | / ContainerField COMMA ContainerMembers |
| 10090 | / KEYWORD_pub? ContainerField | 10090 | / ContainerField |
| 10091 | / | 10091 | / |
| 10092 | 10092 | ||
| 10093 | TestDecl <- KEYWORD_test STRINGLITERAL Block | 10093 | TestDecl <- KEYWORD_test STRINGLITERAL Block |
lib/std/build.zig+18-2| ... | @@ -331,7 +331,7 @@ pub const Builder = struct { | ... | @@ -331,7 +331,7 @@ pub const Builder = struct { |
| 331 | if (self.verbose) { | 331 | if (self.verbose) { |
| 332 | warn("rm {}\n", full_path); | 332 | warn("rm {}\n", full_path); |
| 333 | } | 333 | } |
| 334 | fs.deleteTree(self.allocator, full_path) catch {}; | 334 | fs.deleteTree(full_path) catch {}; |
| 335 | } | 335 | } |
| 336 | 336 | ||
| 337 | // TODO remove empty directories | 337 | // TODO remove empty directories |
| ... | @@ -1491,6 +1491,8 @@ pub const LibExeObjStep = struct { | ... | @@ -1491,6 +1491,8 @@ pub const LibExeObjStep = struct { |
| 1491 | /// Position Independent Code | 1491 | /// Position Independent Code |
| 1492 | force_pic: ?bool = null, | 1492 | force_pic: ?bool = null, |
| 1493 | 1493 | ||
| 1494 | subsystem: ?builtin.SubSystem = null, | ||
| 1495 | |||
| 1494 | const LinkObject = union(enum) { | 1496 | const LinkObject = union(enum) { |
| 1495 | StaticPath: []const u8, | 1497 | StaticPath: []const u8, |
| 1496 | OtherStep: *LibExeObjStep, | 1498 | OtherStep: *LibExeObjStep, |
| ... | @@ -2325,6 +2327,20 @@ pub const LibExeObjStep = struct { | ... | @@ -2325,6 +2327,20 @@ pub const LibExeObjStep = struct { |
| 2325 | } | 2327 | } |
| 2326 | } | 2328 | } |
| 2327 | 2329 | ||
| 2330 | if (self.subsystem) |subsystem| { | ||
| 2331 | try zig_args.append("--subsystem"); | ||
| 2332 | try zig_args.append(switch (subsystem) { | ||
| 2333 | .Console => "console", | ||
| 2334 | .Windows => "windows", | ||
| 2335 | .Posix => "posix", | ||
| 2336 | .Native => "native", | ||
| 2337 | .EfiApplication => "efi_application", | ||
| 2338 | .EfiBootServiceDriver => "efi_boot_service_driver", | ||
| 2339 | .EfiRom => "efi_rom", | ||
| 2340 | .EfiRuntimeDriver => "efi_runtime_driver", | ||
| 2341 | }); | ||
| 2342 | } | ||
| 2343 | |||
| 2328 | if (self.kind == Kind.Test) { | 2344 | if (self.kind == Kind.Test) { |
| 2329 | try builder.spawnChild(zig_args.toSliceConst()); | 2345 | try builder.spawnChild(zig_args.toSliceConst()); |
| 2330 | } else { | 2346 | } else { |
| ... | @@ -2671,7 +2687,7 @@ pub const RemoveDirStep = struct { | ... | @@ -2671,7 +2687,7 @@ pub const RemoveDirStep = struct { |
| 2671 | const self = @fieldParentPtr(RemoveDirStep, "step", step); | 2687 | const self = @fieldParentPtr(RemoveDirStep, "step", step); |
| 2672 | 2688 | ||
| 2673 | const full_path = self.builder.pathFromRoot(self.dir_path); | 2689 | const full_path = self.builder.pathFromRoot(self.dir_path); |
| 2674 | fs.deleteTree(self.builder.allocator, full_path) catch |err| { | 2690 | fs.deleteTree(full_path) catch |err| { |
| 2675 | warn("Unable to remove {}: {}\n", full_path, @errorName(err)); | 2691 | warn("Unable to remove {}: {}\n", full_path, @errorName(err)); |
| 2676 | return err; | 2692 | return err; |
| 2677 | }; | 2693 | }; |
lib/std/c.zig+1| ... | @@ -80,6 +80,7 @@ pub extern "c" fn mmap(addr: ?*align(page_size) c_void, len: usize, prot: c_uint | ... | @@ -80,6 +80,7 @@ pub extern "c" fn mmap(addr: ?*align(page_size) c_void, len: usize, prot: c_uint |
| 80 | pub extern "c" fn munmap(addr: *align(page_size) c_void, len: usize) c_int; | 80 | pub extern "c" fn munmap(addr: *align(page_size) c_void, len: usize) c_int; |
| 81 | pub extern "c" fn mprotect(addr: *align(page_size) c_void, len: usize, prot: c_uint) c_int; | 81 | pub extern "c" fn mprotect(addr: *align(page_size) c_void, len: usize, prot: c_uint) c_int; |
| 82 | pub extern "c" fn unlink(path: [*]const u8) c_int; | 82 | pub extern "c" fn unlink(path: [*]const u8) c_int; |
| 83 | pub extern "c" fn unlinkat(dirfd: fd_t, path: [*]const u8, flags: c_uint) c_int; | ||
| 83 | pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; | 84 | pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; |
| 84 | pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_uint, options: c_uint) c_int; | 85 | pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_uint, options: c_uint) c_int; |
| 85 | pub extern "c" fn fork() c_int; | 86 | pub extern "c" fn fork() c_int; |
lib/std/child_process.zig+16-16| ... | @@ -17,35 +17,35 @@ const TailQueue = std.TailQueue; | ... | @@ -17,35 +17,35 @@ const TailQueue = std.TailQueue; |
| 17 | const maxInt = std.math.maxInt; | 17 | const maxInt = std.math.maxInt; |
| 18 | 18 | ||
| 19 | pub const ChildProcess = struct { | 19 | pub const ChildProcess = struct { |
| 20 | pub pid: if (os.windows.is_the_target) void else i32, | 20 | pid: if (os.windows.is_the_target) void else i32, |
| 21 | pub handle: if (os.windows.is_the_target) windows.HANDLE else void, | 21 | handle: if (os.windows.is_the_target) windows.HANDLE else void, |
| 22 | pub thread_handle: if (os.windows.is_the_target) windows.HANDLE else void, | 22 | thread_handle: if (os.windows.is_the_target) windows.HANDLE else void, |
| 23 | 23 | ||
| 24 | pub allocator: *mem.Allocator, | 24 | allocator: *mem.Allocator, |
| 25 | 25 | ||
| 26 | pub stdin: ?File, | 26 | stdin: ?File, |
| 27 | pub stdout: ?File, | 27 | stdout: ?File, |
| 28 | pub stderr: ?File, | 28 | stderr: ?File, |
| 29 | 29 | ||
| 30 | pub term: ?(SpawnError!Term), | 30 | term: ?(SpawnError!Term), |
| 31 | 31 | ||
| 32 | pub argv: []const []const u8, | 32 | argv: []const []const u8, |
| 33 | 33 | ||
| 34 | /// Leave as null to use the current env map using the supplied allocator. | 34 | /// Leave as null to use the current env map using the supplied allocator. |
| 35 | pub env_map: ?*const BufMap, | 35 | env_map: ?*const BufMap, |
| 36 | 36 | ||
| 37 | pub stdin_behavior: StdIo, | 37 | stdin_behavior: StdIo, |
| 38 | pub stdout_behavior: StdIo, | 38 | stdout_behavior: StdIo, |
| 39 | pub stderr_behavior: StdIo, | 39 | stderr_behavior: StdIo, |
| 40 | 40 | ||
| 41 | /// Set to change the user id when spawning the child process. | 41 | /// Set to change the user id when spawning the child process. |
| 42 | pub uid: if (os.windows.is_the_target) void else ?u32, | 42 | uid: if (os.windows.is_the_target) void else ?u32, |
| 43 | 43 | ||
| 44 | /// Set to change the group id when spawning the child process. | 44 | /// Set to change the group id when spawning the child process. |
| 45 | pub gid: if (os.windows.is_the_target) void else ?u32, | 45 | gid: if (os.windows.is_the_target) void else ?u32, |
| 46 | 46 | ||
| 47 | /// Set to change the current working directory when spawning the child process. | 47 | /// Set to change the current working directory when spawning the child process. |
| 48 | pub cwd: ?[]const u8, | 48 | cwd: ?[]const u8, |
| 49 | 49 | ||
| 50 | err_pipe: if (os.windows.is_the_target) void else [2]os.fd_t, | 50 | err_pipe: if (os.windows.is_the_target) void else [2]os.fd_t, |
| 51 | llnode: if (os.windows.is_the_target) void else TailQueue(*ChildProcess).Node, | 51 | llnode: if (os.windows.is_the_target) void else TailQueue(*ChildProcess).Node, |
lib/std/event/fs.zig+1-1| ... | @@ -1312,7 +1312,7 @@ const test_tmp_dir = "std_event_fs_test"; | ... | @@ -1312,7 +1312,7 @@ const test_tmp_dir = "std_event_fs_test"; |
| 1312 | // | 1312 | // |
| 1313 | // // TODO move this into event loop too | 1313 | // // TODO move this into event loop too |
| 1314 | // try os.makePath(allocator, test_tmp_dir); | 1314 | // try os.makePath(allocator, test_tmp_dir); |
| 1315 | // defer os.deleteTree(allocator, test_tmp_dir) catch {}; | 1315 | // defer os.deleteTree(test_tmp_dir) catch {}; |
| 1316 | // | 1316 | // |
| 1317 | // var loop: Loop = undefined; | 1317 | // var loop: Loop = undefined; |
| 1318 | // try loop.initMultiThreaded(allocator); | 1318 | // try loop.initMultiThreaded(allocator); |
lib/std/fs.zig+653-383| ... | @@ -335,444 +335,708 @@ pub fn deleteDirW(dir_path: [*]const u16) !void { | ... | @@ -335,444 +335,708 @@ pub fn deleteDirW(dir_path: [*]const u16) !void { |
| 335 | return os.rmdirW(dir_path); | 335 | return os.rmdirW(dir_path); |
| 336 | } | 336 | } |
| 337 | 337 | ||
| 338 | const DeleteTreeError = error{ | 338 | /// Removes a symlink, file, or directory. |
| 339 | OutOfMemory, | 339 | /// If `full_path` is relative, this is equivalent to `Dir.deleteTree` with the |
| 340 | AccessDenied, | 340 | /// current working directory as the open directory handle. |
| 341 | FileTooBig, | 341 | /// If `full_path` is absolute, this is equivalent to `Dir.deleteTree` with the |
| 342 | IsDir, | 342 | /// base directory. |
| 343 | SymLinkLoop, | 343 | pub fn deleteTree(full_path: []const u8) !void { |
| 344 | ProcessFdQuotaExceeded, | 344 | if (path.isAbsolute(full_path)) { |
| 345 | NameTooLong, | 345 | const dirname = path.dirname(full_path) orelse return error{ |
| 346 | SystemFdQuotaExceeded, | 346 | /// Attempt to remove the root file system path. |
| 347 | NoDevice, | 347 | /// This error is unreachable if `full_path` is relative. |
| 348 | SystemResources, | 348 | CannotDeleteRootDirectory, |
| 349 | NoSpaceLeft, | 349 | }.CannotDeleteRootDirectory; |
| 350 | PathAlreadyExists, | 350 | |
| 351 | ReadOnlyFileSystem, | 351 | var dir = try Dir.open(dirname); |
| 352 | NotDir, | 352 | defer dir.close(); |
| 353 | FileNotFound, | 353 | |
| 354 | FileSystem, | 354 | return dir.deleteTree(path.basename(full_path)); |
| 355 | FileBusy, | 355 | } else { |
| 356 | DirNotEmpty, | 356 | return Dir.cwd().deleteTree(full_path); |
| 357 | DeviceBusy, | ||
| 358 | |||
| 359 | /// On Windows, file paths must be valid Unicode. | ||
| 360 | InvalidUtf8, | ||
| 361 | |||
| 362 | /// On Windows, file paths cannot contain these characters: | ||
| 363 | /// '/', '*', '?', '"', '<', '>', '|' | ||
| 364 | BadPathName, | ||
| 365 | |||
| 366 | Unexpected, | ||
| 367 | }; | ||
| 368 | |||
| 369 | /// Whether `full_path` describes a symlink, file, or directory, this function | ||
| 370 | /// removes it. If it cannot be removed because it is a non-empty directory, | ||
| 371 | /// this function recursively removes its entries and then tries again. | ||
| 372 | /// TODO determine if we can remove the allocator requirement | ||
| 373 | /// https://github.com/ziglang/zig/issues/2886 | ||
| 374 | pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!void { | ||
| 375 | start_over: while (true) { | ||
| 376 | var got_access_denied = false; | ||
| 377 | // First, try deleting the item as a file. This way we don't follow sym links. | ||
| 378 | if (deleteFile(full_path)) { | ||
| 379 | return; | ||
| 380 | } else |err| switch (err) { | ||
| 381 | error.FileNotFound => return, | ||
| 382 | error.IsDir => {}, | ||
| 383 | error.AccessDenied => got_access_denied = true, | ||
| 384 | |||
| 385 | error.InvalidUtf8, | ||
| 386 | error.SymLinkLoop, | ||
| 387 | error.NameTooLong, | ||
| 388 | error.SystemResources, | ||
| 389 | error.ReadOnlyFileSystem, | ||
| 390 | error.NotDir, | ||
| 391 | error.FileSystem, | ||
| 392 | error.FileBusy, | ||
| 393 | error.BadPathName, | ||
| 394 | error.Unexpected, | ||
| 395 | => return err, | ||
| 396 | } | ||
| 397 | { | ||
| 398 | var dir = Dir.open(allocator, full_path) catch |err| switch (err) { | ||
| 399 | error.NotDir => { | ||
| 400 | if (got_access_denied) { | ||
| 401 | return error.AccessDenied; | ||
| 402 | } | ||
| 403 | continue :start_over; | ||
| 404 | }, | ||
| 405 | |||
| 406 | error.OutOfMemory, | ||
| 407 | error.AccessDenied, | ||
| 408 | error.FileTooBig, | ||
| 409 | error.IsDir, | ||
| 410 | error.SymLinkLoop, | ||
| 411 | error.ProcessFdQuotaExceeded, | ||
| 412 | error.NameTooLong, | ||
| 413 | error.SystemFdQuotaExceeded, | ||
| 414 | error.NoDevice, | ||
| 415 | error.FileNotFound, | ||
| 416 | error.SystemResources, | ||
| 417 | error.NoSpaceLeft, | ||
| 418 | error.PathAlreadyExists, | ||
| 419 | error.Unexpected, | ||
| 420 | error.InvalidUtf8, | ||
| 421 | error.BadPathName, | ||
| 422 | error.DeviceBusy, | ||
| 423 | => return err, | ||
| 424 | }; | ||
| 425 | defer dir.close(); | ||
| 426 | |||
| 427 | var full_entry_buf = std.ArrayList(u8).init(allocator); | ||
| 428 | defer full_entry_buf.deinit(); | ||
| 429 | |||
| 430 | while (try dir.next()) |entry| { | ||
| 431 | try full_entry_buf.resize(full_path.len + entry.name.len + 1); | ||
| 432 | const full_entry_path = full_entry_buf.toSlice(); | ||
| 433 | mem.copy(u8, full_entry_path, full_path); | ||
| 434 | full_entry_path[full_path.len] = path.sep; | ||
| 435 | mem.copy(u8, full_entry_path[full_path.len + 1 ..], entry.name); | ||
| 436 | |||
| 437 | try deleteTree(allocator, full_entry_path); | ||
| 438 | } | ||
| 439 | } | ||
| 440 | return deleteDir(full_path); | ||
| 441 | } | 357 | } |
| 442 | } | 358 | } |
| 443 | 359 | ||
| 444 | /// TODO: separate this API into the one that opens directory handles to then subsequently open | ||
| 445 | /// files, and into the one that reads files from an open directory handle. | ||
| 446 | pub const Dir = struct { | 360 | pub const Dir = struct { |
| 447 | handle: Handle, | 361 | fd: os.fd_t, |
| 448 | allocator: *Allocator, | 362 | |
| 363 | pub const Entry = struct { | ||
| 364 | name: []const u8, | ||
| 365 | kind: Kind, | ||
| 449 | 366 | ||
| 450 | pub const Handle = switch (builtin.os) { | 367 | pub const Kind = enum { |
| 368 | BlockDevice, | ||
| 369 | CharacterDevice, | ||
| 370 | Directory, | ||
| 371 | NamedPipe, | ||
| 372 | SymLink, | ||
| 373 | File, | ||
| 374 | UnixDomainSocket, | ||
| 375 | Whiteout, | ||
| 376 | Unknown, | ||
| 377 | }; | ||
| 378 | }; | ||
| 379 | |||
| 380 | const IteratorError = error{AccessDenied} || os.UnexpectedError; | ||
| 381 | |||
| 382 | pub const Iterator = switch (builtin.os) { | ||
| 451 | .macosx, .ios, .freebsd, .netbsd => struct { | 383 | .macosx, .ios, .freebsd, .netbsd => struct { |
| 452 | fd: i32, | 384 | dir: Dir, |
| 453 | seek: i64, | 385 | seek: i64, |
| 454 | buf: []u8, | 386 | buf: [8192]u8, // TODO align(@alignOf(os.dirent)), |
| 455 | index: usize, | 387 | index: usize, |
| 456 | end_index: usize, | 388 | end_index: usize, |
| 389 | |||
| 390 | const Self = @This(); | ||
| 391 | |||
| 392 | pub const Error = IteratorError; | ||
| 393 | |||
| 394 | /// Memory such as file names referenced in this returned entry becomes invalid | ||
| 395 | /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized. | ||
| 396 | pub fn next(self: *Self) Error!?Entry { | ||
| 397 | switch (builtin.os) { | ||
| 398 | .macosx, .ios => return self.nextDarwin(), | ||
| 399 | .freebsd, .netbsd => return self.nextBsd(), | ||
| 400 | else => @compileError("unimplemented"), | ||
| 401 | } | ||
| 402 | } | ||
| 403 | |||
| 404 | fn nextDarwin(self: *Self) !?Entry { | ||
| 405 | start_over: while (true) { | ||
| 406 | if (self.index >= self.end_index) { | ||
| 407 | const rc = os.system.__getdirentries64( | ||
| 408 | self.dir.fd, | ||
| 409 | &self.buf, | ||
| 410 | self.buf.len, | ||
| 411 | &self.seek, | ||
| 412 | ); | ||
| 413 | if (rc == 0) return null; | ||
| 414 | if (rc < 0) { | ||
| 415 | switch (os.errno(rc)) { | ||
| 416 | os.EBADF => unreachable, | ||
| 417 | os.EFAULT => unreachable, | ||
| 418 | os.ENOTDIR => unreachable, | ||
| 419 | os.EINVAL => unreachable, | ||
| 420 | else => |err| return os.unexpectedErrno(err), | ||
| 421 | } | ||
| 422 | } | ||
| 423 | self.index = 0; | ||
| 424 | self.end_index = @intCast(usize, rc); | ||
| 425 | } | ||
| 426 | const darwin_entry = @ptrCast(*align(1) os.dirent, &self.buf[self.index]); | ||
| 427 | const next_index = self.index + darwin_entry.d_reclen; | ||
| 428 | self.index = next_index; | ||
| 429 | |||
| 430 | const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen]; | ||
| 431 | |||
| 432 | if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { | ||
| 433 | continue :start_over; | ||
| 434 | } | ||
| 435 | |||
| 436 | const entry_kind = switch (darwin_entry.d_type) { | ||
| 437 | os.DT_BLK => Entry.Kind.BlockDevice, | ||
| 438 | os.DT_CHR => Entry.Kind.CharacterDevice, | ||
| 439 | os.DT_DIR => Entry.Kind.Directory, | ||
| 440 | os.DT_FIFO => Entry.Kind.NamedPipe, | ||
| 441 | os.DT_LNK => Entry.Kind.SymLink, | ||
| 442 | os.DT_REG => Entry.Kind.File, | ||
| 443 | os.DT_SOCK => Entry.Kind.UnixDomainSocket, | ||
| 444 | os.DT_WHT => Entry.Kind.Whiteout, | ||
| 445 | else => Entry.Kind.Unknown, | ||
| 446 | }; | ||
| 447 | return Entry{ | ||
| 448 | .name = name, | ||
| 449 | .kind = entry_kind, | ||
| 450 | }; | ||
| 451 | } | ||
| 452 | } | ||
| 453 | |||
| 454 | fn nextBsd(self: *Self) !?Entry { | ||
| 455 | start_over: while (true) { | ||
| 456 | if (self.index >= self.end_index) { | ||
| 457 | const rc = os.system.getdirentries( | ||
| 458 | self.dir.fd, | ||
| 459 | self.buf[0..].ptr, | ||
| 460 | self.buf.len, | ||
| 461 | &self.seek, | ||
| 462 | ); | ||
| 463 | switch (os.errno(rc)) { | ||
| 464 | 0 => {}, | ||
| 465 | os.EBADF => unreachable, | ||
| 466 | os.EFAULT => unreachable, | ||
| 467 | os.ENOTDIR => unreachable, | ||
| 468 | os.EINVAL => unreachable, | ||
| 469 | else => |err| return os.unexpectedErrno(err), | ||
| 470 | } | ||
| 471 | if (rc == 0) return null; | ||
| 472 | self.index = 0; | ||
| 473 | self.end_index = @intCast(usize, rc); | ||
| 474 | } | ||
| 475 | const freebsd_entry = @ptrCast(*align(1) os.dirent, &self.buf[self.index]); | ||
| 476 | const next_index = self.index + freebsd_entry.d_reclen; | ||
| 477 | self.index = next_index; | ||
| 478 | |||
| 479 | const name = @ptrCast([*]u8, &freebsd_entry.d_name)[0..freebsd_entry.d_namlen]; | ||
| 480 | |||
| 481 | if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { | ||
| 482 | continue :start_over; | ||
| 483 | } | ||
| 484 | |||
| 485 | const entry_kind = switch (freebsd_entry.d_type) { | ||
| 486 | os.DT_BLK => Entry.Kind.BlockDevice, | ||
| 487 | os.DT_CHR => Entry.Kind.CharacterDevice, | ||
| 488 | os.DT_DIR => Entry.Kind.Directory, | ||
| 489 | os.DT_FIFO => Entry.Kind.NamedPipe, | ||
| 490 | os.DT_LNK => Entry.Kind.SymLink, | ||
| 491 | os.DT_REG => Entry.Kind.File, | ||
| 492 | os.DT_SOCK => Entry.Kind.UnixDomainSocket, | ||
| 493 | os.DT_WHT => Entry.Kind.Whiteout, | ||
| 494 | else => Entry.Kind.Unknown, | ||
| 495 | }; | ||
| 496 | return Entry{ | ||
| 497 | .name = name, | ||
| 498 | .kind = entry_kind, | ||
| 499 | }; | ||
| 500 | } | ||
| 501 | } | ||
| 457 | }, | 502 | }, |
| 458 | .linux => struct { | 503 | .linux => struct { |
| 459 | fd: i32, | 504 | dir: Dir, |
| 460 | buf: []u8, | 505 | buf: [8192]u8, // TODO align(@alignOf(os.dirent64)), |
| 461 | index: usize, | 506 | index: usize, |
| 462 | end_index: usize, | 507 | end_index: usize, |
| 508 | |||
| 509 | const Self = @This(); | ||
| 510 | |||
| 511 | pub const Error = IteratorError; | ||
| 512 | |||
| 513 | /// Memory such as file names referenced in this returned entry becomes invalid | ||
| 514 | /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized. | ||
| 515 | pub fn next(self: *Self) Error!?Entry { | ||
| 516 | start_over: while (true) { | ||
| 517 | if (self.index >= self.end_index) { | ||
| 518 | const rc = os.linux.getdents64(self.dir.fd, &self.buf, self.buf.len); | ||
| 519 | switch (os.linux.getErrno(rc)) { | ||
| 520 | 0 => {}, | ||
| 521 | os.EBADF => unreachable, | ||
| 522 | os.EFAULT => unreachable, | ||
| 523 | os.ENOTDIR => unreachable, | ||
| 524 | os.EINVAL => unreachable, | ||
| 525 | else => |err| return os.unexpectedErrno(err), | ||
| 526 | } | ||
| 527 | if (rc == 0) return null; | ||
| 528 | self.index = 0; | ||
| 529 | self.end_index = rc; | ||
| 530 | } | ||
| 531 | const linux_entry = @ptrCast(*align(1) os.dirent64, &self.buf[self.index]); | ||
| 532 | const next_index = self.index + linux_entry.d_reclen; | ||
| 533 | self.index = next_index; | ||
| 534 | |||
| 535 | const name = mem.toSlice(u8, @ptrCast([*]u8, &linux_entry.d_name)); | ||
| 536 | |||
| 537 | // skip . and .. entries | ||
| 538 | if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { | ||
| 539 | continue :start_over; | ||
| 540 | } | ||
| 541 | |||
| 542 | const entry_kind = switch (linux_entry.d_type) { | ||
| 543 | os.DT_BLK => Entry.Kind.BlockDevice, | ||
| 544 | os.DT_CHR => Entry.Kind.CharacterDevice, | ||
| 545 | os.DT_DIR => Entry.Kind.Directory, | ||
| 546 | os.DT_FIFO => Entry.Kind.NamedPipe, | ||
| 547 | os.DT_LNK => Entry.Kind.SymLink, | ||
| 548 | os.DT_REG => Entry.Kind.File, | ||
| 549 | os.DT_SOCK => Entry.Kind.UnixDomainSocket, | ||
| 550 | else => Entry.Kind.Unknown, | ||
| 551 | }; | ||
| 552 | return Entry{ | ||
| 553 | .name = name, | ||
| 554 | .kind = entry_kind, | ||
| 555 | }; | ||
| 556 | } | ||
| 557 | } | ||
| 463 | }, | 558 | }, |
| 464 | .windows => struct { | 559 | .windows => struct { |
| 465 | handle: os.windows.HANDLE, | 560 | dir: Dir, |
| 466 | find_file_data: os.windows.WIN32_FIND_DATAW, | 561 | buf: [8192]u8 align(@alignOf(os.windows.FILE_BOTH_DIR_INFORMATION)), |
| 562 | index: usize, | ||
| 563 | end_index: usize, | ||
| 467 | first: bool, | 564 | first: bool, |
| 468 | name_data: [256]u8, | 565 | name_data: [256]u8, |
| 566 | |||
| 567 | const Self = @This(); | ||
| 568 | |||
| 569 | pub const Error = IteratorError; | ||
| 570 | |||
| 571 | pub fn next(self: *Self) Error!?Entry { | ||
| 572 | start_over: while (true) { | ||
| 573 | const w = os.windows; | ||
| 574 | if (self.index >= self.end_index) { | ||
| 575 | var io: w.IO_STATUS_BLOCK = undefined; | ||
| 576 | const rc = w.ntdll.NtQueryDirectoryFile( | ||
| 577 | self.dir.fd, | ||
| 578 | null, | ||
| 579 | null, | ||
| 580 | null, | ||
| 581 | &io, | ||
| 582 | &self.buf, | ||
| 583 | self.buf.len, | ||
| 584 | .FileBothDirectoryInformation, | ||
| 585 | w.FALSE, | ||
| 586 | null, | ||
| 587 | if (self.first) w.BOOLEAN(w.TRUE) else w.BOOLEAN(w.FALSE), | ||
| 588 | ); | ||
| 589 | self.first = false; | ||
| 590 | if (io.Information == 0) return null; | ||
| 591 | self.index = 0; | ||
| 592 | self.end_index = io.Information; | ||
| 593 | switch (rc) { | ||
| 594 | w.STATUS.SUCCESS => {}, | ||
| 595 | w.STATUS.ACCESS_DENIED => return error.AccessDenied, | ||
| 596 | else => return w.unexpectedStatus(rc), | ||
| 597 | } | ||
| 598 | } | ||
| 599 | |||
| 600 | const aligned_ptr = @alignCast(@alignOf(w.FILE_BOTH_DIR_INFORMATION), &self.buf[self.index]); | ||
| 601 | const dir_info = @ptrCast(*w.FILE_BOTH_DIR_INFORMATION, aligned_ptr); | ||
| 602 | if (dir_info.NextEntryOffset != 0) { | ||
| 603 | self.index += dir_info.NextEntryOffset; | ||
| 604 | } else { | ||
| 605 | self.index = self.buf.len; | ||
| 606 | } | ||
| 607 | |||
| 608 | const name_utf16le = @ptrCast([*]u16, &dir_info.FileName)[0 .. dir_info.FileNameLength / 2]; | ||
| 609 | |||
| 610 | if (mem.eql(u16, name_utf16le, [_]u16{'.'}) or mem.eql(u16, name_utf16le, [_]u16{ '.', '.' })) | ||
| 611 | continue; | ||
| 612 | // Trust that Windows gives us valid UTF-16LE | ||
| 613 | const name_utf8_len = std.unicode.utf16leToUtf8(self.name_data[0..], name_utf16le) catch unreachable; | ||
| 614 | const name_utf8 = self.name_data[0..name_utf8_len]; | ||
| 615 | const kind = blk: { | ||
| 616 | const attrs = dir_info.FileAttributes; | ||
| 617 | if (attrs & w.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory; | ||
| 618 | if (attrs & w.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk Entry.Kind.SymLink; | ||
| 619 | break :blk Entry.Kind.File; | ||
| 620 | }; | ||
| 621 | return Entry{ | ||
| 622 | .name = name_utf8, | ||
| 623 | .kind = kind, | ||
| 624 | }; | ||
| 625 | } | ||
| 626 | } | ||
| 469 | }, | 627 | }, |
| 470 | else => @compileError("unimplemented"), | 628 | else => @compileError("unimplemented"), |
| 471 | }; | 629 | }; |
| 472 | 630 | ||
| 473 | pub const Entry = struct { | 631 | pub fn iterate(self: Dir) Iterator { |
| 474 | name: []const u8, | 632 | switch (builtin.os) { |
| 475 | kind: Kind, | 633 | .macosx, .ios, .freebsd, .netbsd => return Iterator{ |
| 634 | .dir = self, | ||
| 635 | .seek = 0, | ||
| 636 | .index = 0, | ||
| 637 | .end_index = 0, | ||
| 638 | .buf = undefined, | ||
| 639 | }, | ||
| 640 | .linux => return Iterator{ | ||
| 641 | .dir = self, | ||
| 642 | .index = 0, | ||
| 643 | .end_index = 0, | ||
| 644 | .buf = undefined, | ||
| 645 | }, | ||
| 646 | .windows => return Iterator{ | ||
| 647 | .dir = self, | ||
| 648 | .index = 0, | ||
| 649 | .end_index = 0, | ||
| 650 | .first = true, | ||
| 651 | .buf = undefined, | ||
| 652 | .name_data = undefined, | ||
| 653 | }, | ||
| 654 | else => @compileError("unimplemented"), | ||
| 655 | } | ||
| 656 | } | ||
| 476 | 657 | ||
| 477 | pub const Kind = enum { | 658 | /// Returns an open handle to the current working directory. |
| 478 | BlockDevice, | 659 | /// Closing the returned `Dir` is checked illegal behavior. |
| 479 | CharacterDevice, | 660 | /// On POSIX targets, this function is comptime-callable. |
| 480 | Directory, | 661 | pub fn cwd() Dir { |
| 481 | NamedPipe, | 662 | if (os.windows.is_the_target) { |
| 482 | SymLink, | 663 | return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle }; |
| 483 | File, | 664 | } else { |
| 484 | UnixDomainSocket, | 665 | return Dir{ .fd = os.AT_FDCWD }; |
| 485 | Whiteout, | 666 | } |
| 486 | Unknown, | 667 | } |
| 487 | }; | ||
| 488 | }; | ||
| 489 | 668 | ||
| 490 | pub const OpenError = error{ | 669 | pub const OpenError = error{ |
| 491 | FileNotFound, | 670 | FileNotFound, |
| 492 | NotDir, | 671 | NotDir, |
| 493 | AccessDenied, | 672 | AccessDenied, |
| 494 | FileTooBig, | ||
| 495 | IsDir, | ||
| 496 | SymLinkLoop, | 673 | SymLinkLoop, |
| 497 | ProcessFdQuotaExceeded, | 674 | ProcessFdQuotaExceeded, |
| 498 | NameTooLong, | 675 | NameTooLong, |
| 499 | SystemFdQuotaExceeded, | 676 | SystemFdQuotaExceeded, |
| 500 | NoDevice, | 677 | NoDevice, |
| 501 | SystemResources, | 678 | SystemResources, |
| 502 | NoSpaceLeft, | ||
| 503 | PathAlreadyExists, | ||
| 504 | OutOfMemory, | ||
| 505 | InvalidUtf8, | 679 | InvalidUtf8, |
| 506 | BadPathName, | 680 | BadPathName, |
| 507 | DeviceBusy, | 681 | DeviceBusy, |
| 682 | } || os.UnexpectedError; | ||
| 508 | 683 | ||
| 509 | Unexpected, | 684 | /// Call `close` to free the directory handle. |
| 510 | }; | 685 | pub fn open(dir_path: []const u8) OpenError!Dir { |
| 511 | 686 | return cwd().openDir(dir_path); | |
| 512 | /// Call close when done. | ||
| 513 | /// TODO remove the allocator requirement from this API | ||
| 514 | /// https://github.com/ziglang/zig/issues/2885 | ||
| 515 | pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir { | ||
| 516 | return Dir{ | ||
| 517 | .allocator = allocator, | ||
| 518 | .handle = switch (builtin.os) { | ||
| 519 | .windows => blk: { | ||
| 520 | var find_file_data: os.windows.WIN32_FIND_DATAW = undefined; | ||
| 521 | const handle = try os.windows.FindFirstFile(dir_path, &find_file_data); | ||
| 522 | break :blk Handle{ | ||
| 523 | .handle = handle, | ||
| 524 | .find_file_data = find_file_data, // TODO guaranteed copy elision | ||
| 525 | .first = true, | ||
| 526 | .name_data = undefined, | ||
| 527 | }; | ||
| 528 | }, | ||
| 529 | .macosx, .ios, .freebsd, .netbsd => Handle{ | ||
| 530 | .fd = try os.open(dir_path, os.O_RDONLY | os.O_NONBLOCK | os.O_DIRECTORY | os.O_CLOEXEC, 0), | ||
| 531 | .seek = 0, | ||
| 532 | .index = 0, | ||
| 533 | .end_index = 0, | ||
| 534 | .buf = [_]u8{}, | ||
| 535 | }, | ||
| 536 | .linux => Handle{ | ||
| 537 | .fd = try os.open(dir_path, os.O_RDONLY | os.O_DIRECTORY | os.O_CLOEXEC, 0), | ||
| 538 | .index = 0, | ||
| 539 | .end_index = 0, | ||
| 540 | .buf = [_]u8{}, | ||
| 541 | }, | ||
| 542 | else => @compileError("unimplemented"), | ||
| 543 | }, | ||
| 544 | }; | ||
| 545 | } | 687 | } |
| 546 | 688 | ||
| 547 | pub fn close(self: *Dir) void { | 689 | /// Same as `open` except the parameter is null-terminated. |
| 548 | if (os.windows.is_the_target) { | 690 | pub fn openC(dir_path_c: [*]const u8) OpenError!Dir { |
| 549 | return os.windows.FindClose(self.handle.handle); | 691 | return cwd().openDirC(dir_path_c); |
| 550 | } | ||
| 551 | self.allocator.free(self.handle.buf); | ||
| 552 | os.close(self.handle.fd); | ||
| 553 | } | 692 | } |
| 554 | 693 | ||
| 555 | /// Memory such as file names referenced in this returned entry becomes invalid | 694 | pub fn close(self: *Dir) void { |
| 556 | /// with subsequent calls to next, as well as when this `Dir` is deinitialized. | 695 | os.close(self.fd); |
| 557 | pub fn next(self: *Dir) !?Entry { | 696 | self.* = undefined; |
| 558 | switch (builtin.os) { | ||
| 559 | .linux => return self.nextLinux(), | ||
| 560 | .macosx, .ios => return self.nextDarwin(), | ||
| 561 | .windows => return self.nextWindows(), | ||
| 562 | .freebsd => return self.nextBsd(), | ||
| 563 | .netbsd => return self.nextBsd(), | ||
| 564 | else => @compileError("unimplemented"), | ||
| 565 | } | ||
| 566 | } | 697 | } |
| 567 | 698 | ||
| 568 | pub fn openRead(self: Dir, file_path: []const u8) os.OpenError!File { | 699 | /// Call `File.close` on the result when done. |
| 569 | const path_c = try os.toPosixPath(file_path); | 700 | pub fn openRead(self: Dir, sub_path: []const u8) File.OpenError!File { |
| 701 | const path_c = try os.toPosixPath(sub_path); | ||
| 570 | return self.openReadC(&path_c); | 702 | return self.openReadC(&path_c); |
| 571 | } | 703 | } |
| 572 | 704 | ||
| 573 | pub fn openReadC(self: Dir, file_path: [*]const u8) OpenError!File { | 705 | /// Call `File.close` on the result when done. |
| 706 | pub fn openReadC(self: Dir, sub_path: [*]const u8) File.OpenError!File { | ||
| 574 | const flags = os.O_LARGEFILE | os.O_RDONLY; | 707 | const flags = os.O_LARGEFILE | os.O_RDONLY; |
| 575 | const fd = try os.openatC(self.handle.fd, file_path, flags, 0); | 708 | const fd = try os.openatC(self.fd, sub_path, flags, 0); |
| 576 | return File.openHandle(fd); | 709 | return File.openHandle(fd); |
| 577 | } | 710 | } |
| 578 | 711 | ||
| 579 | fn nextDarwin(self: *Dir) !?Entry { | 712 | /// Call `close` on the result when done. |
| 580 | start_over: while (true) { | 713 | pub fn openDir(self: Dir, sub_path: []const u8) OpenError!Dir { |
| 581 | if (self.handle.index >= self.handle.end_index) { | 714 | if (os.windows.is_the_target) { |
| 582 | if (self.handle.buf.len == 0) { | 715 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); |
| 583 | self.handle.buf = try self.allocator.alloc(u8, mem.page_size); | 716 | return self.openDirW(&sub_path_w); |
| 584 | } | 717 | } |
| 585 | |||
| 586 | while (true) { | ||
| 587 | const rc = os.system.__getdirentries64( | ||
| 588 | self.handle.fd, | ||
| 589 | self.handle.buf.ptr, | ||
| 590 | self.handle.buf.len, | ||
| 591 | &self.handle.seek, | ||
| 592 | ); | ||
| 593 | if (rc == 0) return null; | ||
| 594 | if (rc < 0) { | ||
| 595 | switch (os.errno(rc)) { | ||
| 596 | os.EBADF => unreachable, | ||
| 597 | os.EFAULT => unreachable, | ||
| 598 | os.ENOTDIR => unreachable, | ||
| 599 | os.EINVAL => { | ||
| 600 | self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2); | ||
| 601 | continue; | ||
| 602 | }, | ||
| 603 | else => |err| return os.unexpectedErrno(err), | ||
| 604 | } | ||
| 605 | } | ||
| 606 | self.handle.index = 0; | ||
| 607 | self.handle.end_index = @intCast(usize, rc); | ||
| 608 | break; | ||
| 609 | } | ||
| 610 | } | ||
| 611 | const darwin_entry = @ptrCast(*align(1) os.dirent, &self.handle.buf[self.handle.index]); | ||
| 612 | const next_index = self.handle.index + darwin_entry.d_reclen; | ||
| 613 | self.handle.index = next_index; | ||
| 614 | |||
| 615 | const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen]; | ||
| 616 | 718 | ||
| 617 | if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { | 719 | const sub_path_c = try os.toPosixPath(sub_path); |
| 618 | continue :start_over; | 720 | return self.openDirC(&sub_path_c); |
| 619 | } | 721 | } |
| 620 | 722 | ||
| 621 | const entry_kind = switch (darwin_entry.d_type) { | 723 | /// Same as `openDir` except the parameter is null-terminated. |
| 622 | os.DT_BLK => Entry.Kind.BlockDevice, | 724 | pub fn openDirC(self: Dir, sub_path_c: [*]const u8) OpenError!Dir { |
| 623 | os.DT_CHR => Entry.Kind.CharacterDevice, | 725 | if (os.windows.is_the_target) { |
| 624 | os.DT_DIR => Entry.Kind.Directory, | 726 | const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); |
| 625 | os.DT_FIFO => Entry.Kind.NamedPipe, | 727 | return self.openDirW(&sub_path_w); |
| 626 | os.DT_LNK => Entry.Kind.SymLink, | ||
| 627 | os.DT_REG => Entry.Kind.File, | ||
| 628 | os.DT_SOCK => Entry.Kind.UnixDomainSocket, | ||
| 629 | os.DT_WHT => Entry.Kind.Whiteout, | ||
| 630 | else => Entry.Kind.Unknown, | ||
| 631 | }; | ||
| 632 | return Entry{ | ||
| 633 | .name = name, | ||
| 634 | .kind = entry_kind, | ||
| 635 | }; | ||
| 636 | } | 728 | } |
| 729 | |||
| 730 | const flags = os.O_RDONLY | os.O_DIRECTORY | os.O_CLOEXEC; | ||
| 731 | const fd = os.openatC(self.fd, sub_path_c, flags, 0) catch |err| switch (err) { | ||
| 732 | error.FileTooBig => unreachable, // can't happen for directories | ||
| 733 | error.IsDir => unreachable, // we're providing O_DIRECTORY | ||
| 734 | error.NoSpaceLeft => unreachable, // not providing O_CREAT | ||
| 735 | error.PathAlreadyExists => unreachable, // not providing O_CREAT | ||
| 736 | else => |e| return e, | ||
| 737 | }; | ||
| 738 | return Dir{ .fd = fd }; | ||
| 637 | } | 739 | } |
| 638 | 740 | ||
| 639 | fn nextWindows(self: *Dir) !?Entry { | 741 | /// Same as `openDir` except the path parameter is UTF16LE, NT-prefixed. |
| 640 | while (true) { | 742 | /// This function is Windows-only. |
| 641 | if (self.handle.first) { | 743 | pub fn openDirW(self: Dir, sub_path_w: [*]const u16) OpenError!Dir { |
| 642 | self.handle.first = false; | 744 | const w = os.windows; |
| 643 | } else { | 745 | |
| 644 | if (!try os.windows.FindNextFile(self.handle.handle, &self.handle.find_file_data)) | 746 | var result = Dir{ |
| 645 | return null; | 747 | .fd = undefined, |
| 646 | } | 748 | }; |
| 647 | const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr); | 749 | |
| 648 | if (mem.eql(u16, name_utf16le, [_]u16{'.'}) or mem.eql(u16, name_utf16le, [_]u16{ '.', '.' })) | 750 | const path_len_bytes = @intCast(u16, mem.toSliceConst(u16, sub_path_w).len * 2); |
| 649 | continue; | 751 | var nt_name = w.UNICODE_STRING{ |
| 650 | // Trust that Windows gives us valid UTF-16LE | 752 | .Length = path_len_bytes, |
| 651 | const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable; | 753 | .MaximumLength = path_len_bytes, |
| 652 | const name_utf8 = self.handle.name_data[0..name_utf8_len]; | 754 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)), |
| 653 | const kind = blk: { | 755 | }; |
| 654 | const attrs = self.handle.find_file_data.dwFileAttributes; | 756 | var attr = w.OBJECT_ATTRIBUTES{ |
| 655 | if (attrs & os.windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory; | 757 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), |
| 656 | if (attrs & os.windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk Entry.Kind.SymLink; | 758 | .RootDirectory = if (path.isAbsoluteW(sub_path_w)) null else self.fd, |
| 657 | break :blk Entry.Kind.File; | 759 | .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. |
| 658 | }; | 760 | .ObjectName = &nt_name, |
| 659 | return Entry{ | 761 | .SecurityDescriptor = null, |
| 660 | .name = name_utf8, | 762 | .SecurityQualityOfService = null, |
| 661 | .kind = kind, | 763 | }; |
| 662 | }; | 764 | if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { |
| 765 | // Windows does not recognize this, but it does work with empty string. | ||
| 766 | nt_name.Length = 0; | ||
| 767 | } | ||
| 768 | if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) { | ||
| 769 | // If you're looking to contribute to zig and fix this, see here for an example of how to | ||
| 770 | // implement this: https://git.midipix.org/ntapi/tree/src/fs/ntapi_tt_open_physical_parent_directory.c | ||
| 771 | @panic("TODO opening '..' with a relative directory handle is not yet implemented on Windows"); | ||
| 772 | } | ||
| 773 | var io: w.IO_STATUS_BLOCK = undefined; | ||
| 774 | const rc = w.ntdll.NtCreateFile( | ||
| 775 | &result.fd, | ||
| 776 | w.GENERIC_READ | w.SYNCHRONIZE, | ||
| 777 | &attr, | ||
| 778 | &io, | ||
| 779 | null, | ||
| 780 | 0, | ||
| 781 | w.FILE_SHARE_READ | w.FILE_SHARE_WRITE, | ||
| 782 | w.FILE_OPEN, | ||
| 783 | w.FILE_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT | w.FILE_OPEN_FOR_BACKUP_INTENT, | ||
| 784 | null, | ||
| 785 | 0, | ||
| 786 | ); | ||
| 787 | switch (rc) { | ||
| 788 | w.STATUS.SUCCESS => return result, | ||
| 789 | w.STATUS.OBJECT_NAME_INVALID => unreachable, | ||
| 790 | w.STATUS.OBJECT_NAME_NOT_FOUND => return error.FileNotFound, | ||
| 791 | w.STATUS.OBJECT_PATH_NOT_FOUND => return error.FileNotFound, | ||
| 792 | w.STATUS.INVALID_PARAMETER => unreachable, | ||
| 793 | else => return w.unexpectedStatus(rc), | ||
| 663 | } | 794 | } |
| 664 | } | 795 | } |
| 665 | 796 | ||
| 666 | fn nextLinux(self: *Dir) !?Entry { | 797 | pub const DeleteFileError = os.UnlinkError; |
| 667 | start_over: while (true) { | ||
| 668 | if (self.handle.index >= self.handle.end_index) { | ||
| 669 | if (self.handle.buf.len == 0) { | ||
| 670 | self.handle.buf = try self.allocator.alloc(u8, mem.page_size); | ||
| 671 | } | ||
| 672 | 798 | ||
| 673 | while (true) { | 799 | /// Delete a file name and possibly the file it refers to, based on an open directory handle. |
| 674 | const rc = os.linux.getdents64(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len); | 800 | pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void { |
| 675 | switch (os.linux.getErrno(rc)) { | 801 | const sub_path_c = try os.toPosixPath(sub_path); |
| 676 | 0 => {}, | 802 | return self.deleteFileC(&sub_path_c); |
| 677 | os.EBADF => unreachable, | 803 | } |
| 678 | os.EFAULT => unreachable, | ||
| 679 | os.ENOTDIR => unreachable, | ||
| 680 | os.EINVAL => { | ||
| 681 | self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2); | ||
| 682 | continue; | ||
| 683 | }, | ||
| 684 | else => |err| return os.unexpectedErrno(err), | ||
| 685 | } | ||
| 686 | if (rc == 0) return null; | ||
| 687 | self.handle.index = 0; | ||
| 688 | self.handle.end_index = rc; | ||
| 689 | break; | ||
| 690 | } | ||
| 691 | } | ||
| 692 | const linux_entry = @ptrCast(*align(1) os.dirent64, &self.handle.buf[self.handle.index]); | ||
| 693 | const next_index = self.handle.index + linux_entry.d_reclen; | ||
| 694 | self.handle.index = next_index; | ||
| 695 | 804 | ||
| 696 | const name = mem.toSlice(u8, @ptrCast([*]u8, &linux_entry.d_name)); | 805 | /// Same as `deleteFile` except the parameter is null-terminated. |
| 806 | pub fn deleteFileC(self: Dir, sub_path_c: [*]const u8) DeleteFileError!void { | ||
| 807 | os.unlinkatC(self.fd, sub_path_c, 0) catch |err| switch (err) { | ||
| 808 | error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR | ||
| 809 | else => |e| return e, | ||
| 810 | }; | ||
| 811 | } | ||
| 697 | 812 | ||
| 698 | // skip . and .. entries | 813 | pub const DeleteDirError = error{ |
| 699 | if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { | 814 | DirNotEmpty, |
| 700 | continue :start_over; | 815 | FileNotFound, |
| 701 | } | 816 | AccessDenied, |
| 817 | FileBusy, | ||
| 818 | FileSystem, | ||
| 819 | SymLinkLoop, | ||
| 820 | NameTooLong, | ||
| 821 | NotDir, | ||
| 822 | SystemResources, | ||
| 823 | ReadOnlyFileSystem, | ||
| 824 | InvalidUtf8, | ||
| 825 | BadPathName, | ||
| 826 | Unexpected, | ||
| 827 | }; | ||
| 702 | 828 | ||
| 703 | const entry_kind = switch (linux_entry.d_type) { | 829 | /// Returns `error.DirNotEmpty` if the directory is not empty. |
| 704 | os.DT_BLK => Entry.Kind.BlockDevice, | 830 | /// To delete a directory recursively, see `deleteTree`. |
| 705 | os.DT_CHR => Entry.Kind.CharacterDevice, | 831 | pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void { |
| 706 | os.DT_DIR => Entry.Kind.Directory, | 832 | if (os.windows.is_the_target) { |
| 707 | os.DT_FIFO => Entry.Kind.NamedPipe, | 833 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); |
| 708 | os.DT_LNK => Entry.Kind.SymLink, | 834 | return self.deleteDirW(&sub_path_w); |
| 709 | os.DT_REG => Entry.Kind.File, | ||
| 710 | os.DT_SOCK => Entry.Kind.UnixDomainSocket, | ||
| 711 | else => Entry.Kind.Unknown, | ||
| 712 | }; | ||
| 713 | return Entry{ | ||
| 714 | .name = name, | ||
| 715 | .kind = entry_kind, | ||
| 716 | }; | ||
| 717 | } | 835 | } |
| 836 | const sub_path_c = try os.toPosixPath(sub_path); | ||
| 837 | return self.deleteDirC(&sub_path_c); | ||
| 718 | } | 838 | } |
| 719 | 839 | ||
| 720 | fn nextBsd(self: *Dir) !?Entry { | 840 | /// Same as `deleteDir` except the parameter is null-terminated. |
| 721 | start_over: while (true) { | 841 | pub fn deleteDirC(self: Dir, sub_path_c: [*]const u8) DeleteDirError!void { |
| 722 | if (self.handle.index >= self.handle.end_index) { | 842 | os.unlinkatC(self.fd, sub_path_c, os.AT_REMOVEDIR) catch |err| switch (err) { |
| 723 | if (self.handle.buf.len == 0) { | 843 | error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR |
| 724 | self.handle.buf = try self.allocator.alloc(u8, mem.page_size); | 844 | else => |e| return e, |
| 725 | } | 845 | }; |
| 846 | } | ||
| 726 | 847 | ||
| 727 | while (true) { | 848 | /// Same as `deleteDir` except the parameter is UTF16LE, NT prefixed. |
| 728 | const rc = os.system.getdirentries( | 849 | /// This function is Windows-only. |
| 729 | self.handle.fd, | 850 | pub fn deleteDirW(self: Dir, sub_path_w: [*]const u16) DeleteDirError!void { |
| 730 | self.handle.buf.ptr, | 851 | os.unlinkatW(self.fd, sub_path_w, os.AT_REMOVEDIR) catch |err| switch (err) { |
| 731 | self.handle.buf.len, | 852 | error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR |
| 732 | &self.handle.seek, | 853 | else => |e| return e, |
| 733 | ); | 854 | }; |
| 734 | switch (os.errno(rc)) { | 855 | } |
| 735 | 0 => {}, | ||
| 736 | os.EBADF => unreachable, | ||
| 737 | os.EFAULT => unreachable, | ||
| 738 | os.ENOTDIR => unreachable, | ||
| 739 | os.EINVAL => { | ||
| 740 | self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2); | ||
| 741 | continue; | ||
| 742 | }, | ||
| 743 | else => |err| return os.unexpectedErrno(err), | ||
| 744 | } | ||
| 745 | if (rc == 0) return null; | ||
| 746 | self.handle.index = 0; | ||
| 747 | self.handle.end_index = @intCast(usize, rc); | ||
| 748 | break; | ||
| 749 | } | ||
| 750 | } | ||
| 751 | const freebsd_entry = @ptrCast(*align(1) os.dirent, &self.handle.buf[self.handle.index]); | ||
| 752 | const next_index = self.handle.index + freebsd_entry.d_reclen; | ||
| 753 | self.handle.index = next_index; | ||
| 754 | 856 | ||
| 755 | const name = @ptrCast([*]u8, &freebsd_entry.d_name)[0..freebsd_entry.d_namlen]; | 857 | /// Read value of a symbolic link. |
| 858 | /// The return value is a slice of `buffer`, from index `0`. | ||
| 859 | pub fn readLink(self: Dir, sub_path: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { | ||
| 860 | const sub_path_c = try os.toPosixPath(sub_path); | ||
| 861 | return self.readLinkC(&sub_path_c, buffer); | ||
| 862 | } | ||
| 756 | 863 | ||
| 757 | if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { | 864 | /// Same as `readLink`, except the `pathname` parameter is null-terminated. |
| 758 | continue :start_over; | 865 | pub fn readLinkC(self: Dir, sub_path_c: [*]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { |
| 866 | return os.readlinkatC(self.fd, sub_path_c, buffer); | ||
| 867 | } | ||
| 868 | |||
| 869 | pub const DeleteTreeError = error{ | ||
| 870 | AccessDenied, | ||
| 871 | FileTooBig, | ||
| 872 | SymLinkLoop, | ||
| 873 | ProcessFdQuotaExceeded, | ||
| 874 | NameTooLong, | ||
| 875 | SystemFdQuotaExceeded, | ||
| 876 | NoDevice, | ||
| 877 | SystemResources, | ||
| 878 | ReadOnlyFileSystem, | ||
| 879 | FileSystem, | ||
| 880 | FileBusy, | ||
| 881 | DeviceBusy, | ||
| 882 | |||
| 883 | /// One of the path components was not a directory. | ||
| 884 | /// This error is unreachable if `sub_path` does not contain a path separator. | ||
| 885 | NotDir, | ||
| 886 | |||
| 887 | /// On Windows, file paths must be valid Unicode. | ||
| 888 | InvalidUtf8, | ||
| 889 | |||
| 890 | /// On Windows, file paths cannot contain these characters: | ||
| 891 | /// '/', '*', '?', '"', '<', '>', '|' | ||
| 892 | BadPathName, | ||
| 893 | } || os.UnexpectedError; | ||
| 894 | |||
| 895 | /// Whether `full_path` describes a symlink, file, or directory, this function | ||
| 896 | /// removes it. If it cannot be removed because it is a non-empty directory, | ||
| 897 | /// this function recursively removes its entries and then tries again. | ||
| 898 | /// This operation is not atomic on most file systems. | ||
| 899 | pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void { | ||
| 900 | start_over: while (true) { | ||
| 901 | var got_access_denied = false; | ||
| 902 | // First, try deleting the item as a file. This way we don't follow sym links. | ||
| 903 | if (self.deleteFile(sub_path)) { | ||
| 904 | return; | ||
| 905 | } else |err| switch (err) { | ||
| 906 | error.FileNotFound => return, | ||
| 907 | error.IsDir => {}, | ||
| 908 | error.AccessDenied => got_access_denied = true, | ||
| 909 | |||
| 910 | error.InvalidUtf8, | ||
| 911 | error.SymLinkLoop, | ||
| 912 | error.NameTooLong, | ||
| 913 | error.SystemResources, | ||
| 914 | error.ReadOnlyFileSystem, | ||
| 915 | error.NotDir, | ||
| 916 | error.FileSystem, | ||
| 917 | error.FileBusy, | ||
| 918 | error.BadPathName, | ||
| 919 | error.Unexpected, | ||
| 920 | => |e| return e, | ||
| 759 | } | 921 | } |
| 922 | var dir = self.openDir(sub_path) catch |err| switch (err) { | ||
| 923 | error.NotDir => { | ||
| 924 | if (got_access_denied) { | ||
| 925 | return error.AccessDenied; | ||
| 926 | } | ||
| 927 | continue :start_over; | ||
| 928 | }, | ||
| 929 | error.FileNotFound => { | ||
| 930 | // That's fine, we were trying to remove this directory anyway. | ||
| 931 | continue :start_over; | ||
| 932 | }, | ||
| 760 | 933 | ||
| 761 | const entry_kind = switch (freebsd_entry.d_type) { | 934 | error.AccessDenied, |
| 762 | os.DT_BLK => Entry.Kind.BlockDevice, | 935 | error.SymLinkLoop, |
| 763 | os.DT_CHR => Entry.Kind.CharacterDevice, | 936 | error.ProcessFdQuotaExceeded, |
| 764 | os.DT_DIR => Entry.Kind.Directory, | 937 | error.NameTooLong, |
| 765 | os.DT_FIFO => Entry.Kind.NamedPipe, | 938 | error.SystemFdQuotaExceeded, |
| 766 | os.DT_LNK => Entry.Kind.SymLink, | 939 | error.NoDevice, |
| 767 | os.DT_REG => Entry.Kind.File, | 940 | error.SystemResources, |
| 768 | os.DT_SOCK => Entry.Kind.UnixDomainSocket, | 941 | error.Unexpected, |
| 769 | os.DT_WHT => Entry.Kind.Whiteout, | 942 | error.InvalidUtf8, |
| 770 | else => Entry.Kind.Unknown, | 943 | error.BadPathName, |
| 771 | }; | 944 | error.DeviceBusy, |
| 772 | return Entry{ | 945 | => |e| return e, |
| 773 | .name = name, | ||
| 774 | .kind = entry_kind, | ||
| 775 | }; | 946 | }; |
| 947 | var cleanup_dir_parent: ?Dir = null; | ||
| 948 | defer if (cleanup_dir_parent) |*d| d.close(); | ||
| 949 | |||
| 950 | var cleanup_dir = true; | ||
| 951 | defer if (cleanup_dir) dir.close(); | ||
| 952 | |||
| 953 | var dir_name_buf: [MAX_PATH_BYTES]u8 = undefined; | ||
| 954 | var dir_name: []const u8 = sub_path; | ||
| 955 | var parent_dir = self; | ||
| 956 | |||
| 957 | // Here we must avoid recursion, in order to provide O(1) memory guarantee of this function. | ||
| 958 | // Go through each entry and if it is not a directory, delete it. If it is a directory, | ||
| 959 | // open it, and close the original directory. Repeat. Then start the entire operation over. | ||
| 960 | |||
| 961 | scan_dir: while (true) { | ||
| 962 | var dir_it = dir.iterate(); | ||
| 963 | while (try dir_it.next()) |entry| { | ||
| 964 | if (dir.deleteFile(entry.name)) { | ||
| 965 | continue; | ||
| 966 | } else |err| switch (err) { | ||
| 967 | error.FileNotFound => continue, | ||
| 968 | |||
| 969 | // Impossible because we do not pass any path separators. | ||
| 970 | error.NotDir => unreachable, | ||
| 971 | |||
| 972 | error.IsDir => {}, | ||
| 973 | error.AccessDenied => got_access_denied = true, | ||
| 974 | |||
| 975 | error.InvalidUtf8, | ||
| 976 | error.SymLinkLoop, | ||
| 977 | error.NameTooLong, | ||
| 978 | error.SystemResources, | ||
| 979 | error.ReadOnlyFileSystem, | ||
| 980 | error.FileSystem, | ||
| 981 | error.FileBusy, | ||
| 982 | error.BadPathName, | ||
| 983 | error.Unexpected, | ||
| 984 | => |e| return e, | ||
| 985 | } | ||
| 986 | |||
| 987 | const new_dir = dir.openDir(entry.name) catch |err| switch (err) { | ||
| 988 | error.NotDir => { | ||
| 989 | if (got_access_denied) { | ||
| 990 | return error.AccessDenied; | ||
| 991 | } | ||
| 992 | continue :scan_dir; | ||
| 993 | }, | ||
| 994 | error.FileNotFound => { | ||
| 995 | // That's fine, we were trying to remove this directory anyway. | ||
| 996 | continue :scan_dir; | ||
| 997 | }, | ||
| 998 | |||
| 999 | error.AccessDenied, | ||
| 1000 | error.SymLinkLoop, | ||
| 1001 | error.ProcessFdQuotaExceeded, | ||
| 1002 | error.NameTooLong, | ||
| 1003 | error.SystemFdQuotaExceeded, | ||
| 1004 | error.NoDevice, | ||
| 1005 | error.SystemResources, | ||
| 1006 | error.Unexpected, | ||
| 1007 | error.InvalidUtf8, | ||
| 1008 | error.BadPathName, | ||
| 1009 | error.DeviceBusy, | ||
| 1010 | => |e| return e, | ||
| 1011 | }; | ||
| 1012 | if (cleanup_dir_parent) |*d| d.close(); | ||
| 1013 | cleanup_dir_parent = dir; | ||
| 1014 | dir = new_dir; | ||
| 1015 | mem.copy(u8, &dir_name_buf, entry.name); | ||
| 1016 | dir_name = dir_name_buf[0..entry.name.len]; | ||
| 1017 | continue :scan_dir; | ||
| 1018 | } | ||
| 1019 | // Reached the end of the directory entries, which means we successfully deleted all of them. | ||
| 1020 | // Now to remove the directory itself. | ||
| 1021 | dir.close(); | ||
| 1022 | cleanup_dir = false; | ||
| 1023 | |||
| 1024 | if (cleanup_dir_parent) |d| { | ||
| 1025 | d.deleteDir(dir_name) catch |err| switch (err) { | ||
| 1026 | // These two things can happen due to file system race conditions. | ||
| 1027 | error.FileNotFound, error.DirNotEmpty => continue :start_over, | ||
| 1028 | else => |e| return e, | ||
| 1029 | }; | ||
| 1030 | continue :start_over; | ||
| 1031 | } else { | ||
| 1032 | self.deleteDir(sub_path) catch |err| switch (err) { | ||
| 1033 | error.FileNotFound => return, | ||
| 1034 | error.DirNotEmpty => continue :start_over, | ||
| 1035 | else => |e| return e, | ||
| 1036 | }; | ||
| 1037 | return; | ||
| 1038 | } | ||
| 1039 | } | ||
| 776 | } | 1040 | } |
| 777 | } | 1041 | } |
| 778 | }; | 1042 | }; |
| ... | @@ -782,13 +1046,18 @@ pub const Walker = struct { | ... | @@ -782,13 +1046,18 @@ pub const Walker = struct { |
| 782 | name_buffer: std.Buffer, | 1046 | name_buffer: std.Buffer, |
| 783 | 1047 | ||
| 784 | pub const Entry = struct { | 1048 | pub const Entry = struct { |
| 785 | path: []const u8, | 1049 | /// The containing directory. This can be used to operate directly on `basename` |
| 1050 | /// rather than `path`, avoiding `error.NameTooLong` for deeply nested paths. | ||
| 1051 | /// The directory remains open until `next` or `deinit` is called. | ||
| 1052 | dir: Dir, | ||
| 786 | basename: []const u8, | 1053 | basename: []const u8, |
| 1054 | |||
| 1055 | path: []const u8, | ||
| 787 | kind: Dir.Entry.Kind, | 1056 | kind: Dir.Entry.Kind, |
| 788 | }; | 1057 | }; |
| 789 | 1058 | ||
| 790 | const StackItem = struct { | 1059 | const StackItem = struct { |
| 791 | dir_it: Dir, | 1060 | dir_it: Dir.Iterator, |
| 792 | dirname_len: usize, | 1061 | dirname_len: usize, |
| 793 | }; | 1062 | }; |
| 794 | 1063 | ||
| ... | @@ -806,23 +1075,26 @@ pub const Walker = struct { | ... | @@ -806,23 +1075,26 @@ pub const Walker = struct { |
| 806 | try self.name_buffer.appendByte(path.sep); | 1075 | try self.name_buffer.appendByte(path.sep); |
| 807 | try self.name_buffer.append(base.name); | 1076 | try self.name_buffer.append(base.name); |
| 808 | if (base.kind == .Directory) { | 1077 | if (base.kind == .Directory) { |
| 809 | // TODO https://github.com/ziglang/zig/issues/2888 | 1078 | var new_dir = top.dir_it.dir.openDir(base.name) catch |err| switch (err) { |
| 810 | var new_dir = try Dir.open(self.stack.allocator, self.name_buffer.toSliceConst()); | 1079 | error.NameTooLong => unreachable, // no path sep in base.name |
| 1080 | else => |e| return e, | ||
| 1081 | }; | ||
| 811 | { | 1082 | { |
| 812 | errdefer new_dir.close(); | 1083 | errdefer new_dir.close(); |
| 813 | try self.stack.append(StackItem{ | 1084 | try self.stack.append(StackItem{ |
| 814 | .dir_it = new_dir, | 1085 | .dir_it = new_dir.iterate(), |
| 815 | .dirname_len = self.name_buffer.len(), | 1086 | .dirname_len = self.name_buffer.len(), |
| 816 | }); | 1087 | }); |
| 817 | } | 1088 | } |
| 818 | } | 1089 | } |
| 819 | return Entry{ | 1090 | return Entry{ |
| 1091 | .dir = top.dir_it.dir, | ||
| 820 | .basename = self.name_buffer.toSliceConst()[dirname_len + 1 ..], | 1092 | .basename = self.name_buffer.toSliceConst()[dirname_len + 1 ..], |
| 821 | .path = self.name_buffer.toSliceConst(), | 1093 | .path = self.name_buffer.toSliceConst(), |
| 822 | .kind = base.kind, | 1094 | .kind = base.kind, |
| 823 | }; | 1095 | }; |
| 824 | } else { | 1096 | } else { |
| 825 | self.stack.pop().dir_it.close(); | 1097 | self.stack.pop().dir_it.dir.close(); |
| 826 | } | 1098 | } |
| 827 | } | 1099 | } |
| 828 | } | 1100 | } |
| ... | @@ -837,12 +1109,12 @@ pub const Walker = struct { | ... | @@ -837,12 +1109,12 @@ pub const Walker = struct { |
| 837 | /// Recursively iterates over a directory. | 1109 | /// Recursively iterates over a directory. |
| 838 | /// Must call `Walker.deinit` when done. | 1110 | /// Must call `Walker.deinit` when done. |
| 839 | /// `dir_path` must not end in a path separator. | 1111 | /// `dir_path` must not end in a path separator. |
| 840 | /// TODO: https://github.com/ziglang/zig/issues/2888 | 1112 | /// The order of returned file system entries is undefined. |
| 841 | pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker { | 1113 | pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker { |
| 842 | assert(!mem.endsWith(u8, dir_path, path.sep_str)); | 1114 | assert(!mem.endsWith(u8, dir_path, path.sep_str)); |
| 843 | 1115 | ||
| 844 | var dir_it = try Dir.open(allocator, dir_path); | 1116 | var dir = try Dir.open(dir_path); |
| 845 | errdefer dir_it.close(); | 1117 | errdefer dir.close(); |
| 846 | 1118 | ||
| 847 | var name_buffer = try std.Buffer.init(allocator, dir_path); | 1119 | var name_buffer = try std.Buffer.init(allocator, dir_path); |
| 848 | errdefer name_buffer.deinit(); | 1120 | errdefer name_buffer.deinit(); |
| ... | @@ -853,7 +1125,7 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker { | ... | @@ -853,7 +1125,7 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker { |
| 853 | }; | 1125 | }; |
| 854 | 1126 | ||
| 855 | try walker.stack.append(Walker.StackItem{ | 1127 | try walker.stack.append(Walker.StackItem{ |
| 856 | .dir_it = dir_it, | 1128 | .dir_it = dir.iterate(), |
| 857 | .dirname_len = dir_path.len, | 1129 | .dirname_len = dir_path.len, |
| 858 | }); | 1130 | }); |
| 859 | 1131 | ||
| ... | @@ -862,15 +1134,13 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker { | ... | @@ -862,15 +1134,13 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker { |
| 862 | 1134 | ||
| 863 | /// Read value of a symbolic link. | 1135 | /// Read value of a symbolic link. |
| 864 | /// The return value is a slice of buffer, from index `0`. | 1136 | /// The return value is a slice of buffer, from index `0`. |
| 865 | /// TODO https://github.com/ziglang/zig/issues/2888 | 1137 | pub fn readLink(pathname: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { |
| 866 | pub fn readLink(pathname: []const u8, buffer: *[os.PATH_MAX]u8) ![]u8 { | ||
| 867 | return os.readlink(pathname, buffer); | 1138 | return os.readlink(pathname, buffer); |
| 868 | } | 1139 | } |
| 869 | 1140 | ||
| 870 | /// Same as `readLink`, except the `pathname` parameter is null-terminated. | 1141 | /// Same as `readLink`, except the parameter is null-terminated. |
| 871 | /// TODO https://github.com/ziglang/zig/issues/2888 | 1142 | pub fn readLinkC(pathname_c: [*]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { |
| 872 | pub fn readLinkC(pathname: [*]const u8, buffer: *[os.PATH_MAX]u8) ![]u8 { | 1143 | return os.readlinkC(pathname_c, buffer); |
| 873 | return os.readlinkC(pathname, buffer); | ||
| 874 | } | 1144 | } |
| 875 | 1145 | ||
| 876 | pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError; | 1146 | pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError; |
lib/std/fs/file.zig+1| ... | @@ -243,6 +243,7 @@ pub const File = struct { | ... | @@ -243,6 +243,7 @@ pub const File = struct { |
| 243 | switch (rc) { | 243 | switch (rc) { |
| 244 | windows.STATUS.SUCCESS => {}, | 244 | windows.STATUS.SUCCESS => {}, |
| 245 | windows.STATUS.BUFFER_OVERFLOW => {}, | 245 | windows.STATUS.BUFFER_OVERFLOW => {}, |
| 246 | windows.STATUS.INVALID_PARAMETER => unreachable, | ||
| 246 | else => return windows.unexpectedStatus(rc), | 247 | else => return windows.unexpectedStatus(rc), |
| 247 | } | 248 | } |
| 248 | return Stat{ | 249 | return Stat{ |
lib/std/fs/path.zig+19| ... | @@ -136,6 +136,25 @@ pub fn isAbsolute(path: []const u8) bool { | ... | @@ -136,6 +136,25 @@ pub fn isAbsolute(path: []const u8) bool { |
| 136 | } | 136 | } |
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | pub fn isAbsoluteW(path_w: [*]const u16) bool { | ||
| 140 | if (path_w[0] == '/') | ||
| 141 | return true; | ||
| 142 | |||
| 143 | if (path_w[0] == '\\') { | ||
| 144 | return true; | ||
| 145 | } | ||
| 146 | if (path_w[0] == 0 or path_w[1] == 0 or path_w[2] == 0) { | ||
| 147 | return false; | ||
| 148 | } | ||
| 149 | if (path_w[1] == ':') { | ||
| 150 | if (path_w[2] == '/') | ||
| 151 | return true; | ||
| 152 | if (path_w[2] == '\\') | ||
| 153 | return true; | ||
| 154 | } | ||
| 155 | return false; | ||
| 156 | } | ||
| 157 | |||
| 139 | pub fn isAbsoluteWindows(path: []const u8) bool { | 158 | pub fn isAbsoluteWindows(path: []const u8) bool { |
| 140 | if (path[0] == '/') | 159 | if (path[0] == '/') |
| 141 | return true; | 160 | return true; |
lib/std/heap.zig+1-1| ... | @@ -338,7 +338,7 @@ pub const HeapAllocator = switch (builtin.os) { | ... | @@ -338,7 +338,7 @@ pub const HeapAllocator = switch (builtin.os) { |
| 338 | /// This allocator takes an existing allocator, wraps it, and provides an interface | 338 | /// This allocator takes an existing allocator, wraps it, and provides an interface |
| 339 | /// where you can allocate without freeing, and then free it all together. | 339 | /// where you can allocate without freeing, and then free it all together. |
| 340 | pub const ArenaAllocator = struct { | 340 | pub const ArenaAllocator = struct { |
| 341 | pub allocator: Allocator, | 341 | allocator: Allocator, |
| 342 | 342 | ||
| 343 | child_allocator: *Allocator, | 343 | child_allocator: *Allocator, |
| 344 | buffer_list: std.SinglyLinkedList([]u8), | 344 | buffer_list: std.SinglyLinkedList([]u8), |
lib/std/http/headers.zig+3-3| ... | @@ -28,9 +28,9 @@ fn never_index_default(name: []const u8) bool { | ... | @@ -28,9 +28,9 @@ fn never_index_default(name: []const u8) bool { |
| 28 | 28 | ||
| 29 | const HeaderEntry = struct { | 29 | const HeaderEntry = struct { |
| 30 | allocator: *Allocator, | 30 | allocator: *Allocator, |
| 31 | pub name: []const u8, | 31 | name: []const u8, |
| 32 | pub value: []u8, | 32 | value: []u8, |
| 33 | pub never_index: bool, | 33 | never_index: bool, |
| 34 | 34 | ||
| 35 | const Self = @This(); | 35 | const Self = @This(); |
| 36 | 36 |
lib/std/io.zig+13-10| ... | @@ -127,6 +127,7 @@ pub fn OutStream(comptime WriteError: type) type { | ... | @@ -127,6 +127,7 @@ pub fn OutStream(comptime WriteError: type) type { |
| 127 | }; | 127 | }; |
| 128 | } | 128 | } |
| 129 | 129 | ||
| 130 | /// TODO move this to `std.fs` and add a version to `std.fs.Dir`. | ||
| 130 | pub fn writeFile(path: []const u8, data: []const u8) !void { | 131 | pub fn writeFile(path: []const u8, data: []const u8) !void { |
| 131 | var file = try File.openWrite(path); | 132 | var file = try File.openWrite(path); |
| 132 | defer file.close(); | 133 | defer file.close(); |
| ... | @@ -134,11 +135,13 @@ pub fn writeFile(path: []const u8, data: []const u8) !void { | ... | @@ -134,11 +135,13 @@ pub fn writeFile(path: []const u8, data: []const u8) !void { |
| 134 | } | 135 | } |
| 135 | 136 | ||
| 136 | /// On success, caller owns returned buffer. | 137 | /// On success, caller owns returned buffer. |
| 138 | /// TODO move this to `std.fs` and add a version to `std.fs.Dir`. | ||
| 137 | pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 { | 139 | pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 { |
| 138 | return readFileAllocAligned(allocator, path, @alignOf(u8)); | 140 | return readFileAllocAligned(allocator, path, @alignOf(u8)); |
| 139 | } | 141 | } |
| 140 | 142 | ||
| 141 | /// On success, caller owns returned buffer. | 143 | /// On success, caller owns returned buffer. |
| 144 | /// TODO move this to `std.fs` and add a version to `std.fs.Dir`. | ||
| 142 | pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptime A: u29) ![]align(A) u8 { | 145 | pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptime A: u29) ![]align(A) u8 { |
| 143 | var file = try File.openRead(path); | 146 | var file = try File.openRead(path); |
| 144 | defer file.close(); | 147 | defer file.close(); |
| ... | @@ -161,7 +164,7 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) | ... | @@ -161,7 +164,7 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) |
| 161 | const Self = @This(); | 164 | const Self = @This(); |
| 162 | const Stream = InStream(Error); | 165 | const Stream = InStream(Error); |
| 163 | 166 | ||
| 164 | pub stream: Stream, | 167 | stream: Stream, |
| 165 | 168 | ||
| 166 | unbuffered_in_stream: *Stream, | 169 | unbuffered_in_stream: *Stream, |
| 167 | 170 | ||
| ... | @@ -273,7 +276,7 @@ pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) typ | ... | @@ -273,7 +276,7 @@ pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) typ |
| 273 | pub const Error = InStreamError; | 276 | pub const Error = InStreamError; |
| 274 | pub const Stream = InStream(Error); | 277 | pub const Stream = InStream(Error); |
| 275 | 278 | ||
| 276 | pub stream: Stream, | 279 | stream: Stream, |
| 277 | base: *Stream, | 280 | base: *Stream, |
| 278 | 281 | ||
| 279 | // Right now the look-ahead space is statically allocated, but a version with dynamic allocation | 282 | // Right now the look-ahead space is statically allocated, but a version with dynamic allocation |
| ... | @@ -336,7 +339,7 @@ pub const SliceInStream = struct { | ... | @@ -336,7 +339,7 @@ pub const SliceInStream = struct { |
| 336 | pub const Error = error{}; | 339 | pub const Error = error{}; |
| 337 | pub const Stream = InStream(Error); | 340 | pub const Stream = InStream(Error); |
| 338 | 341 | ||
| 339 | pub stream: Stream, | 342 | stream: Stream, |
| 340 | 343 | ||
| 341 | pos: usize, | 344 | pos: usize, |
| 342 | slice: []const u8, | 345 | slice: []const u8, |
| ... | @@ -514,9 +517,9 @@ pub const SliceOutStream = struct { | ... | @@ -514,9 +517,9 @@ pub const SliceOutStream = struct { |
| 514 | pub const Error = error{OutOfSpace}; | 517 | pub const Error = error{OutOfSpace}; |
| 515 | pub const Stream = OutStream(Error); | 518 | pub const Stream = OutStream(Error); |
| 516 | 519 | ||
| 517 | pub stream: Stream, | 520 | stream: Stream, |
| 518 | 521 | ||
| 519 | pub pos: usize, | 522 | pos: usize, |
| 520 | slice: []u8, | 523 | slice: []u8, |
| 521 | 524 | ||
| 522 | pub fn init(slice: []u8) SliceOutStream { | 525 | pub fn init(slice: []u8) SliceOutStream { |
| ... | @@ -571,7 +574,7 @@ pub const NullOutStream = struct { | ... | @@ -571,7 +574,7 @@ pub const NullOutStream = struct { |
| 571 | pub const Error = error{}; | 574 | pub const Error = error{}; |
| 572 | pub const Stream = OutStream(Error); | 575 | pub const Stream = OutStream(Error); |
| 573 | 576 | ||
| 574 | pub stream: Stream, | 577 | stream: Stream, |
| 575 | 578 | ||
| 576 | pub fn init() NullOutStream { | 579 | pub fn init() NullOutStream { |
| 577 | return NullOutStream{ | 580 | return NullOutStream{ |
| ... | @@ -595,8 +598,8 @@ pub fn CountingOutStream(comptime OutStreamError: type) type { | ... | @@ -595,8 +598,8 @@ pub fn CountingOutStream(comptime OutStreamError: type) type { |
| 595 | pub const Stream = OutStream(Error); | 598 | pub const Stream = OutStream(Error); |
| 596 | pub const Error = OutStreamError; | 599 | pub const Error = OutStreamError; |
| 597 | 600 | ||
| 598 | pub stream: Stream, | 601 | stream: Stream, |
| 599 | pub bytes_written: u64, | 602 | bytes_written: u64, |
| 600 | child_stream: *Stream, | 603 | child_stream: *Stream, |
| 601 | 604 | ||
| 602 | pub fn init(child_stream: *Stream) Self { | 605 | pub fn init(child_stream: *Stream) Self { |
| ... | @@ -635,7 +638,7 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamEr | ... | @@ -635,7 +638,7 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamEr |
| 635 | pub const Stream = OutStream(Error); | 638 | pub const Stream = OutStream(Error); |
| 636 | pub const Error = OutStreamError; | 639 | pub const Error = OutStreamError; |
| 637 | 640 | ||
| 638 | pub stream: Stream, | 641 | stream: Stream, |
| 639 | 642 | ||
| 640 | unbuffered_out_stream: *Stream, | 643 | unbuffered_out_stream: *Stream, |
| 641 | 644 | ||
| ... | @@ -1084,7 +1087,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, | ... | @@ -1084,7 +1087,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, |
| 1084 | // safety. If it is bad, it will be caught anyway. | 1087 | // safety. If it is bad, it will be caught anyway. |
| 1085 | const TagInt = @TagType(TagType); | 1088 | const TagInt = @TagType(TagType); |
| 1086 | const tag = try self.deserializeInt(TagInt); | 1089 | const tag = try self.deserializeInt(TagInt); |
| 1087 | 1090 | ||
| 1088 | inline for (info.fields) |field_info| { | 1091 | inline for (info.fields) |field_info| { |
| 1089 | if (field_info.enum_field.?.value == tag) { | 1092 | if (field_info.enum_field.?.value == tag) { |
| 1090 | const name = field_info.name; | 1093 | const name = field_info.name; |
lib/std/io/seekable_stream.zig+2-2| ... | @@ -39,8 +39,8 @@ pub const SliceSeekableInStream = struct { | ... | @@ -39,8 +39,8 @@ pub const SliceSeekableInStream = struct { |
| 39 | pub const Stream = InStream(Error); | 39 | pub const Stream = InStream(Error); |
| 40 | pub const SeekableInStream = SeekableStream(SeekError, GetSeekPosError); | 40 | pub const SeekableInStream = SeekableStream(SeekError, GetSeekPosError); |
| 41 | 41 | ||
| 42 | pub stream: Stream, | 42 | stream: Stream, |
| 43 | pub seekable_stream: SeekableInStream, | 43 | seekable_stream: SeekableInStream, |
| 44 | 44 | ||
| 45 | pos: usize, | 45 | pos: usize, |
| 46 | slice: []const u8, | 46 | slice: []const u8, |
lib/std/os.zig+181-36| ... | @@ -529,22 +529,36 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void | ... | @@ -529,22 +529,36 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void |
| 529 | 529 | ||
| 530 | pub const OpenError = error{ | 530 | pub const OpenError = error{ |
| 531 | AccessDenied, | 531 | AccessDenied, |
| 532 | FileTooBig, | ||
| 533 | IsDir, | ||
| 534 | SymLinkLoop, | 532 | SymLinkLoop, |
| 535 | ProcessFdQuotaExceeded, | 533 | ProcessFdQuotaExceeded, |
| 536 | NameTooLong, | ||
| 537 | SystemFdQuotaExceeded, | 534 | SystemFdQuotaExceeded, |
| 538 | NoDevice, | 535 | NoDevice, |
| 539 | FileNotFound, | 536 | FileNotFound, |
| 540 | 537 | ||
| 538 | /// The path exceeded `MAX_PATH_BYTES` bytes. | ||
| 539 | NameTooLong, | ||
| 540 | |||
| 541 | /// Insufficient kernel memory was available, or | 541 | /// Insufficient kernel memory was available, or |
| 542 | /// the named file is a FIFO and per-user hard limit on | 542 | /// the named file is a FIFO and per-user hard limit on |
| 543 | /// memory allocation for pipes has been reached. | 543 | /// memory allocation for pipes has been reached. |
| 544 | SystemResources, | 544 | SystemResources, |
| 545 | 545 | ||
| 546 | /// The file is too large to be opened. This error is unreachable | ||
| 547 | /// for 64-bit targets, as well as when opening directories. | ||
| 548 | FileTooBig, | ||
| 549 | |||
| 550 | /// The path refers to directory but the `O_DIRECTORY` flag was not provided. | ||
| 551 | IsDir, | ||
| 552 | |||
| 553 | /// A new path cannot be created because the device has no room for the new file. | ||
| 554 | /// This error is only reachable when the `O_CREAT` flag is provided. | ||
| 546 | NoSpaceLeft, | 555 | NoSpaceLeft, |
| 556 | |||
| 557 | /// A component used as a directory in the path was not, in fact, a directory, or | ||
| 558 | /// `O_DIRECTORY` was specified and the path was not a directory. | ||
| 547 | NotDir, | 559 | NotDir, |
| 560 | |||
| 561 | /// The path already exists and the `O_CREAT` and `O_EXCL` flags were provided. | ||
| 548 | PathAlreadyExists, | 562 | PathAlreadyExists, |
| 549 | DeviceBusy, | 563 | DeviceBusy, |
| 550 | } || UnexpectedError; | 564 | } || UnexpectedError; |
| ... | @@ -978,6 +992,114 @@ pub fn unlinkC(file_path: [*]const u8) UnlinkError!void { | ... | @@ -978,6 +992,114 @@ pub fn unlinkC(file_path: [*]const u8) UnlinkError!void { |
| 978 | } | 992 | } |
| 979 | } | 993 | } |
| 980 | 994 | ||
| 995 | pub const UnlinkatError = UnlinkError || error{ | ||
| 996 | /// When passing `AT_REMOVEDIR`, this error occurs when the named directory is not empty. | ||
| 997 | DirNotEmpty, | ||
| 998 | }; | ||
| 999 | |||
| 1000 | /// Delete a file name and possibly the file it refers to, based on an open directory handle. | ||
| 1001 | pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void { | ||
| 1002 | if (windows.is_the_target) { | ||
| 1003 | const file_path_w = try windows.sliceToPrefixedFileW(file_path); | ||
| 1004 | return unlinkatW(dirfd, &file_path_w, flags); | ||
| 1005 | } | ||
| 1006 | const file_path_c = try toPosixPath(file_path); | ||
| 1007 | return unlinkatC(dirfd, &file_path_c, flags); | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | /// Same as `unlinkat` but `file_path` is a null-terminated string. | ||
| 1011 | pub fn unlinkatC(dirfd: fd_t, file_path_c: [*]const u8, flags: u32) UnlinkatError!void { | ||
| 1012 | if (windows.is_the_target) { | ||
| 1013 | const file_path_w = try windows.cStrToPrefixedFileW(file_path_c); | ||
| 1014 | return unlinkatW(dirfd, &file_path_w, flags); | ||
| 1015 | } | ||
| 1016 | switch (errno(system.unlinkat(dirfd, file_path_c, flags))) { | ||
| 1017 | 0 => return, | ||
| 1018 | EACCES => return error.AccessDenied, | ||
| 1019 | EPERM => return error.AccessDenied, | ||
| 1020 | EBUSY => return error.FileBusy, | ||
| 1021 | EFAULT => unreachable, | ||
| 1022 | EIO => return error.FileSystem, | ||
| 1023 | EISDIR => return error.IsDir, | ||
| 1024 | ELOOP => return error.SymLinkLoop, | ||
| 1025 | ENAMETOOLONG => return error.NameTooLong, | ||
| 1026 | ENOENT => return error.FileNotFound, | ||
| 1027 | ENOTDIR => return error.NotDir, | ||
| 1028 | ENOMEM => return error.SystemResources, | ||
| 1029 | EROFS => return error.ReadOnlyFileSystem, | ||
| 1030 | ENOTEMPTY => return error.DirNotEmpty, | ||
| 1031 | |||
| 1032 | EINVAL => unreachable, // invalid flags, or pathname has . as last component | ||
| 1033 | EBADF => unreachable, // always a race condition | ||
| 1034 | |||
| 1035 | else => |err| return unexpectedErrno(err), | ||
| 1036 | } | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | /// Same as `unlinkat` but `sub_path_w` is UTF16LE, NT prefixed. Windows only. | ||
| 1040 | pub fn unlinkatW(dirfd: fd_t, sub_path_w: [*]const u16, flags: u32) UnlinkatError!void { | ||
| 1041 | const w = windows; | ||
| 1042 | |||
| 1043 | const want_rmdir_behavior = (flags & AT_REMOVEDIR) != 0; | ||
| 1044 | const create_options_flags = if (want_rmdir_behavior) | ||
| 1045 | w.ULONG(w.FILE_DELETE_ON_CLOSE) | ||
| 1046 | else | ||
| 1047 | w.ULONG(w.FILE_DELETE_ON_CLOSE | w.FILE_NON_DIRECTORY_FILE); | ||
| 1048 | |||
| 1049 | const path_len_bytes = @intCast(u16, mem.toSliceConst(u16, sub_path_w).len * 2); | ||
| 1050 | var nt_name = w.UNICODE_STRING{ | ||
| 1051 | .Length = path_len_bytes, | ||
| 1052 | .MaximumLength = path_len_bytes, | ||
| 1053 | // The Windows API makes this mutable, but it will not mutate here. | ||
| 1054 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)), | ||
| 1055 | }; | ||
| 1056 | |||
| 1057 | if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { | ||
| 1058 | // Windows does not recognize this, but it does work with empty string. | ||
| 1059 | nt_name.Length = 0; | ||
| 1060 | } | ||
| 1061 | if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) { | ||
| 1062 | // Can't remove the parent directory with an open handle. | ||
| 1063 | return error.FileBusy; | ||
| 1064 | } | ||
| 1065 | |||
| 1066 | |||
| 1067 | var attr = w.OBJECT_ATTRIBUTES{ | ||
| 1068 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), | ||
| 1069 | .RootDirectory = dirfd, | ||
| 1070 | .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. | ||
| 1071 | .ObjectName = &nt_name, | ||
| 1072 | .SecurityDescriptor = null, | ||
| 1073 | .SecurityQualityOfService = null, | ||
| 1074 | }; | ||
| 1075 | var io: w.IO_STATUS_BLOCK = undefined; | ||
| 1076 | var tmp_handle: w.HANDLE = undefined; | ||
| 1077 | var rc = w.ntdll.NtCreateFile( | ||
| 1078 | &tmp_handle, | ||
| 1079 | w.SYNCHRONIZE | w.DELETE, | ||
| 1080 | &attr, | ||
| 1081 | &io, | ||
| 1082 | null, | ||
| 1083 | 0, | ||
| 1084 | w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE, | ||
| 1085 | w.FILE_OPEN, | ||
| 1086 | create_options_flags, | ||
| 1087 | null, | ||
| 1088 | 0, | ||
| 1089 | ); | ||
| 1090 | if (rc == w.STATUS.SUCCESS) { | ||
| 1091 | rc = w.ntdll.NtClose(tmp_handle); | ||
| 1092 | } | ||
| 1093 | switch (rc) { | ||
| 1094 | w.STATUS.SUCCESS => return, | ||
| 1095 | w.STATUS.OBJECT_NAME_INVALID => unreachable, | ||
| 1096 | w.STATUS.OBJECT_NAME_NOT_FOUND => return error.FileNotFound, | ||
| 1097 | w.STATUS.INVALID_PARAMETER => unreachable, | ||
| 1098 | w.STATUS.FILE_IS_A_DIRECTORY => return error.IsDir, | ||
| 1099 | else => return w.unexpectedStatus(rc), | ||
| 1100 | } | ||
| 1101 | } | ||
| 1102 | |||
| 981 | const RenameError = error{ | 1103 | const RenameError = error{ |
| 982 | AccessDenied, | 1104 | AccessDenied, |
| 983 | FileBusy, | 1105 | FileBusy, |
| ... | @@ -1237,6 +1359,27 @@ pub fn readlinkC(file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 { | ... | @@ -1237,6 +1359,27 @@ pub fn readlinkC(file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 { |
| 1237 | } | 1359 | } |
| 1238 | } | 1360 | } |
| 1239 | 1361 | ||
| 1362 | pub fn readlinkatC(dirfd: fd_t, file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 { | ||
| 1363 | if (windows.is_the_target) { | ||
| 1364 | const file_path_w = try windows.cStrToPrefixedFileW(file_path); | ||
| 1365 | @compileError("TODO implement readlink for Windows"); | ||
| 1366 | } | ||
| 1367 | const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len); | ||
| 1368 | switch (errno(rc)) { | ||
| 1369 | 0 => return out_buffer[0..@bitCast(usize, rc)], | ||
| 1370 | EACCES => return error.AccessDenied, | ||
| 1371 | EFAULT => unreachable, | ||
| 1372 | EINVAL => unreachable, | ||
| 1373 | EIO => return error.FileSystem, | ||
| 1374 | ELOOP => return error.SymLinkLoop, | ||
| 1375 | ENAMETOOLONG => return error.NameTooLong, | ||
| 1376 | ENOENT => return error.FileNotFound, | ||
| 1377 | ENOMEM => return error.SystemResources, | ||
| 1378 | ENOTDIR => return error.NotDir, | ||
| 1379 | else => |err| return unexpectedErrno(err), | ||
| 1380 | } | ||
| 1381 | } | ||
| 1382 | |||
| 1240 | pub const SetIdError = error{ | 1383 | pub const SetIdError = error{ |
| 1241 | ResourceLimitReached, | 1384 | ResourceLimitReached, |
| 1242 | InvalidUserId, | 1385 | InvalidUserId, |
| ... | @@ -1476,18 +1619,46 @@ pub const AcceptError = error{ | ... | @@ -1476,18 +1619,46 @@ pub const AcceptError = error{ |
| 1476 | BlockedByFirewall, | 1619 | BlockedByFirewall, |
| 1477 | } || UnexpectedError; | 1620 | } || UnexpectedError; |
| 1478 | 1621 | ||
| 1479 | /// Accept a connection on a socket. `fd` must be opened in blocking mode. | 1622 | /// Accept a connection on a socket. |
| 1480 | /// See also `accept4_async`. | 1623 | /// If the application has a global event loop enabled, EAGAIN is handled |
| 1481 | pub fn accept4(fd: i32, addr: *sockaddr, flags: u32) AcceptError!i32 { | 1624 | /// via the event loop. Otherwise EAGAIN results in error.WouldBlock. |
| 1625 | pub fn accept4( | ||
| 1626 | /// This argument is a socket that has been created with `socket`, bound to a local address | ||
| 1627 | /// with `bind`, and is listening for connections after a `listen`. | ||
| 1628 | sockfd: i32, | ||
| 1629 | /// This argument is a pointer to a sockaddr structure. This structure is filled in with the | ||
| 1630 | /// address of the peer socket, as known to the communications layer. The exact format of the | ||
| 1631 | /// address returned addr is determined by the socket's address family (see `socket` and the | ||
| 1632 | /// respective protocol man pages). | ||
| 1633 | addr: *sockaddr, | ||
| 1634 | /// This argument is a value-result argument: the caller must initialize it to contain the | ||
| 1635 | /// size (in bytes) of the structure pointed to by addr; on return it will contain the actual size | ||
| 1636 | /// of the peer address. | ||
| 1637 | /// | ||
| 1638 | /// The returned address is truncated if the buffer provided is too small; in this case, `addr_size` | ||
| 1639 | /// will return a value greater than was supplied to the call. | ||
| 1640 | addr_size: *usize, | ||
| 1641 | /// If flags is 0, then `accept4` is the same as `accept`. The following values can be bitwise | ||
| 1642 | /// ORed in flags to obtain different behavior: | ||
| 1643 | /// * `SOCK_NONBLOCK` - Set the `O_NONBLOCK` file status flag on the open file description (see `open`) | ||
| 1644 | /// referred to by the new file descriptor. Using this flag saves extra calls to `fcntl` to achieve | ||
| 1645 | /// the same result. | ||
| 1646 | /// * `SOCK_CLOEXEC` - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor. See the | ||
| 1647 | /// description of the `O_CLOEXEC` flag in `open` for reasons why this may be useful. | ||
| 1648 | flags: u32, | ||
| 1649 | ) AcceptError!i32 { | ||
| 1482 | while (true) { | 1650 | while (true) { |
| 1483 | var sockaddr_size = u32(@sizeOf(sockaddr)); | 1651 | const rc = system.accept4(sockfd, addr, addr_size, flags); |
| 1484 | const rc = system.accept4(fd, addr, &sockaddr_size, flags); | ||
| 1485 | switch (errno(rc)) { | 1652 | switch (errno(rc)) { |
| 1486 | 0 => return @intCast(i32, rc), | 1653 | 0 => return @intCast(i32, rc), |
| 1487 | EINTR => continue, | 1654 | EINTR => continue, |
| 1488 | else => |err| return unexpectedErrno(err), | ||
| 1489 | 1655 | ||
| 1490 | EAGAIN => unreachable, // This function is for blocking only. | 1656 | EAGAIN => if (std.event.Loop.instance) |loop| { |
| 1657 | loop.waitUntilFdReadable(sockfd) catch return error.WouldBlock; | ||
| 1658 | continue; | ||
| 1659 | } else { | ||
| 1660 | return error.WouldBlock; | ||
| 1661 | }, | ||
| 1491 | EBADF => unreachable, // always a race condition | 1662 | EBADF => unreachable, // always a race condition |
| 1492 | ECONNABORTED => return error.ConnectionAborted, | 1663 | ECONNABORTED => return error.ConnectionAborted, |
| 1493 | EFAULT => unreachable, | 1664 | EFAULT => unreachable, |
| ... | @@ -1500,34 +1671,8 @@ pub fn accept4(fd: i32, addr: *sockaddr, flags: u32) AcceptError!i32 { | ... | @@ -1500,34 +1671,8 @@ pub fn accept4(fd: i32, addr: *sockaddr, flags: u32) AcceptError!i32 { |
| 1500 | EOPNOTSUPP => return error.OperationNotSupported, | 1671 | EOPNOTSUPP => return error.OperationNotSupported, |
| 1501 | EPROTO => return error.ProtocolFailure, | 1672 | EPROTO => return error.ProtocolFailure, |
| 1502 | EPERM => return error.BlockedByFirewall, | 1673 | EPERM => return error.BlockedByFirewall, |
| 1503 | } | ||
| 1504 | } | ||
| 1505 | } | ||
| 1506 | 1674 | ||
| 1507 | /// This is the same as `accept4` except `fd` is expected to be non-blocking. | ||
| 1508 | /// Returns -1 if would block. | ||
| 1509 | pub fn accept4_async(fd: i32, addr: *sockaddr, flags: u32) AcceptError!i32 { | ||
| 1510 | while (true) { | ||
| 1511 | var sockaddr_size = u32(@sizeOf(sockaddr)); | ||
| 1512 | const rc = system.accept4(fd, addr, &sockaddr_size, flags); | ||
| 1513 | switch (errno(rc)) { | ||
| 1514 | 0 => return @intCast(i32, rc), | ||
| 1515 | EINTR => continue, | ||
| 1516 | else => |err| return unexpectedErrno(err), | 1675 | else => |err| return unexpectedErrno(err), |
| 1517 | |||
| 1518 | EAGAIN => return -1, | ||
| 1519 | EBADF => unreachable, // always a race condition | ||
| 1520 | ECONNABORTED => return error.ConnectionAborted, | ||
| 1521 | EFAULT => unreachable, | ||
| 1522 | EINVAL => unreachable, | ||
| 1523 | EMFILE => return error.ProcessFdQuotaExceeded, | ||
| 1524 | ENFILE => return error.SystemFdQuotaExceeded, | ||
| 1525 | ENOBUFS => return error.SystemResources, | ||
| 1526 | ENOMEM => return error.SystemResources, | ||
| 1527 | ENOTSOCK => return error.FileDescriptorNotASocket, | ||
| 1528 | EOPNOTSUPP => return error.OperationNotSupported, | ||
| 1529 | EPROTO => return error.ProtocolFailure, | ||
| 1530 | EPERM => return error.BlockedByFirewall, | ||
| 1531 | } | 1676 | } |
| 1532 | } | 1677 | } |
| 1533 | } | 1678 | } |
lib/std/os/bits/darwin.zig+14| ... | @@ -1178,3 +1178,17 @@ pub fn S_IWHT(m: u32) bool { | ... | @@ -1178,3 +1178,17 @@ pub fn S_IWHT(m: u32) bool { |
| 1178 | return m & S_IFMT == S_IFWHT; | 1178 | return m & S_IFMT == S_IFWHT; |
| 1179 | } | 1179 | } |
| 1180 | pub const HOST_NAME_MAX = 72; | 1180 | pub const HOST_NAME_MAX = 72; |
| 1181 | |||
| 1182 | pub const AT_FDCWD = -2; | ||
| 1183 | |||
| 1184 | /// Use effective ids in access check | ||
| 1185 | pub const AT_EACCESS = 0x0010; | ||
| 1186 | |||
| 1187 | /// Act on the symlink itself not the target | ||
| 1188 | pub const AT_SYMLINK_NOFOLLOW = 0x0020; | ||
| 1189 | |||
| 1190 | /// Act on target of symlink | ||
| 1191 | pub const AT_SYMLINK_FOLLOW = 0x0040; | ||
| 1192 | |||
| 1193 | /// Path refers to directory | ||
| 1194 | pub const AT_REMOVEDIR = 0x0080; |
lib/std/os/bits/freebsd.zig+20| ... | @@ -939,3 +939,23 @@ pub fn S_IWHT(m: u32) bool { | ... | @@ -939,3 +939,23 @@ pub fn S_IWHT(m: u32) bool { |
| 939 | } | 939 | } |
| 940 | 940 | ||
| 941 | pub const HOST_NAME_MAX = 255; | 941 | pub const HOST_NAME_MAX = 255; |
| 942 | |||
| 943 | /// Magic value that specify the use of the current working directory | ||
| 944 | /// to determine the target of relative file paths in the openat() and | ||
| 945 | /// similar syscalls. | ||
| 946 | pub const AT_FDCWD = -100; | ||
| 947 | |||
| 948 | /// Check access using effective user and group ID | ||
| 949 | pub const AT_EACCESS = 0x0100; | ||
| 950 | |||
| 951 | /// Do not follow symbolic links | ||
| 952 | pub const AT_SYMLINK_NOFOLLOW = 0x0200; | ||
| 953 | |||
| 954 | /// Follow symbolic link | ||
| 955 | pub const AT_SYMLINK_FOLLOW = 0x0400; | ||
| 956 | |||
| 957 | /// Remove directory instead of file | ||
| 958 | pub const AT_REMOVEDIR = 0x0800; | ||
| 959 | |||
| 960 | /// Fail if not under dirfd | ||
| 961 | pub const AT_BENEATH = 0x1000; |
lib/std/os/bits/windows.zig+3| ... | @@ -158,3 +158,6 @@ pub const EWOULDBLOCK = 140; | ... | @@ -158,3 +158,6 @@ pub const EWOULDBLOCK = 140; |
| 158 | pub const EDQUOT = 10069; | 158 | pub const EDQUOT = 10069; |
| 159 | 159 | ||
| 160 | pub const F_OK = 0; | 160 | pub const F_OK = 0; |
| 161 | |||
| 162 | /// Remove directory instead of unlinking file | ||
| 163 | pub const AT_REMOVEDIR = 0x200; |
lib/std/os/test.zig+3-3| ... | @@ -19,8 +19,8 @@ test "makePath, put some files in it, deleteTree" { | ... | @@ -19,8 +19,8 @@ test "makePath, put some files in it, deleteTree" { |
| 19 | try fs.makePath(a, "os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c"); | 19 | try fs.makePath(a, "os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c"); |
| 20 | try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense"); | 20 | try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense"); |
| 21 | try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah"); | 21 | try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah"); |
| 22 | try fs.deleteTree(a, "os_test_tmp"); | 22 | try fs.deleteTree("os_test_tmp"); |
| 23 | if (fs.Dir.open(a, "os_test_tmp")) |dir| { | 23 | if (fs.Dir.open("os_test_tmp")) |dir| { |
| 24 | @panic("expected error"); | 24 | @panic("expected error"); |
| 25 | } else |err| { | 25 | } else |err| { |
| 26 | expect(err == error.FileNotFound); | 26 | expect(err == error.FileNotFound); |
| ... | @@ -37,7 +37,7 @@ test "access file" { | ... | @@ -37,7 +37,7 @@ test "access file" { |
| 37 | 37 | ||
| 38 | try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", ""); | 38 | try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", ""); |
| 39 | try os.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", os.F_OK); | 39 | try os.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", os.F_OK); |
| 40 | try fs.deleteTree(a, "os_test_tmp"); | 40 | try fs.deleteTree("os_test_tmp"); |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | fn testThreadIdFn(thread_id: *Thread.Id) void { | 43 | fn testThreadIdFn(thread_id: *Thread.Id) void { |
lib/std/os/windows.zig+24-4| ... | @@ -20,6 +20,8 @@ pub const shell32 = @import("windows/shell32.zig"); | ... | @@ -20,6 +20,8 @@ pub const shell32 = @import("windows/shell32.zig"); |
| 20 | 20 | ||
| 21 | pub usingnamespace @import("windows/bits.zig"); | 21 | pub usingnamespace @import("windows/bits.zig"); |
| 22 | 22 | ||
| 23 | pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize)); | ||
| 24 | |||
| 23 | /// `builtin` is missing `subsystem` when the subsystem is automatically detected, | 25 | /// `builtin` is missing `subsystem` when the subsystem is automatically detected, |
| 24 | /// so Zig standard library has the subsystem detection logic here. This should generally be | 26 | /// so Zig standard library has the subsystem detection logic here. This should generally be |
| 25 | /// used rather than `builtin.subsystem`. | 27 | /// used rather than `builtin.subsystem`. |
| ... | @@ -42,7 +44,6 @@ pub const subsystem: ?builtin.SubSystem = blk: { | ... | @@ -42,7 +44,6 @@ pub const subsystem: ?builtin.SubSystem = blk: { |
| 42 | break :blk builtin.SubSystem.Console; | 44 | break :blk builtin.SubSystem.Console; |
| 43 | } | 45 | } |
| 44 | }, | 46 | }, |
| 45 | .uefi => break :blk builtin.SubSystem.EfiApplication, | ||
| 46 | else => break :blk null, | 47 | else => break :blk null, |
| 47 | } | 48 | } |
| 48 | }; | 49 | }; |
| ... | @@ -792,6 +793,25 @@ pub fn SetFileTime( | ... | @@ -792,6 +793,25 @@ pub fn SetFileTime( |
| 792 | } | 793 | } |
| 793 | } | 794 | } |
| 794 | 795 | ||
| 796 | pub fn peb() *PEB { | ||
| 797 | switch (builtin.arch) { | ||
| 798 | .i386 => { | ||
| 799 | return asm ( | ||
| 800 | \\ mov %%fs:0x18, %[ptr] | ||
| 801 | \\ mov %%ds:0x30(%[ptr]), %[ptr] | ||
| 802 | : [ptr] "=r" (-> *PEB) | ||
| 803 | ); | ||
| 804 | }, | ||
| 805 | .x86_64 => { | ||
| 806 | return asm ( | ||
| 807 | \\ mov %%gs:0x60, %[ptr] | ||
| 808 | : [ptr] "=r" (-> *PEB) | ||
| 809 | ); | ||
| 810 | }, | ||
| 811 | else => @compileError("unsupported architecture"), | ||
| 812 | } | ||
| 813 | } | ||
| 814 | |||
| 795 | /// A file time is a 64-bit value that represents the number of 100-nanosecond | 815 | /// A file time is a 64-bit value that represents the number of 100-nanosecond |
| 796 | /// intervals that have elapsed since 12:00 A.M. January 1, 1601 Coordinated | 816 | /// intervals that have elapsed since 12:00 A.M. January 1, 1601 Coordinated |
| 797 | /// Universal Time (UTC). | 817 | /// Universal Time (UTC). |
| ... | @@ -844,8 +864,8 @@ pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) | ... | @@ -844,8 +864,8 @@ pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) |
| 844 | else => {}, | 864 | else => {}, |
| 845 | } | 865 | } |
| 846 | } | 866 | } |
| 847 | const start_index = if (mem.startsWith(u8, s, "\\\\") or !std.fs.path.isAbsolute(s)) 0 else blk: { | 867 | const start_index = if (mem.startsWith(u8, s, "\\?") or !std.fs.path.isAbsolute(s)) 0 else blk: { |
| 848 | const prefix = [_]u16{ '\\', '\\', '?', '\\' }; | 868 | const prefix = [_]u16{ '\\', '?', '?', '\\' }; |
| 849 | mem.copy(u16, result[0..], prefix); | 869 | mem.copy(u16, result[0..], prefix); |
| 850 | break :blk prefix.len; | 870 | break :blk prefix.len; |
| 851 | }; | 871 | }; |
| ... | @@ -879,7 +899,7 @@ pub fn unexpectedError(err: DWORD) std.os.UnexpectedError { | ... | @@ -879,7 +899,7 @@ pub fn unexpectedError(err: DWORD) std.os.UnexpectedError { |
| 879 | /// and you get an unexpected status. | 899 | /// and you get an unexpected status. |
| 880 | pub fn unexpectedStatus(status: NTSTATUS) std.os.UnexpectedError { | 900 | pub fn unexpectedStatus(status: NTSTATUS) std.os.UnexpectedError { |
| 881 | if (std.os.unexpected_error_tracing) { | 901 | if (std.os.unexpected_error_tracing) { |
| 882 | std.debug.warn("error.Unexpected NTSTATUS={}\n", status); | 902 | std.debug.warn("error.Unexpected NTSTATUS=0x{x}\n", status); |
| 883 | std.debug.dumpCurrentStackTrace(null); | 903 | std.debug.dumpCurrentStackTrace(null); |
| 884 | } | 904 | } |
| 885 | return error.Unexpected; | 905 | return error.Unexpected; |
lib/std/os/windows/bits.zig+145-3| ... | @@ -300,6 +300,44 @@ pub const FILE_SHARE_DELETE = 0x00000004; | ... | @@ -300,6 +300,44 @@ pub const FILE_SHARE_DELETE = 0x00000004; |
| 300 | pub const FILE_SHARE_READ = 0x00000001; | 300 | pub const FILE_SHARE_READ = 0x00000001; |
| 301 | pub const FILE_SHARE_WRITE = 0x00000002; | 301 | pub const FILE_SHARE_WRITE = 0x00000002; |
| 302 | 302 | ||
| 303 | pub const DELETE = 0x00010000; | ||
| 304 | pub const READ_CONTROL = 0x00020000; | ||
| 305 | pub const WRITE_DAC = 0x00040000; | ||
| 306 | pub const WRITE_OWNER = 0x00080000; | ||
| 307 | pub const SYNCHRONIZE = 0x00100000; | ||
| 308 | pub const STANDARD_RIGHTS_REQUIRED = 0x000f0000; | ||
| 309 | |||
| 310 | // disposition for NtCreateFile | ||
| 311 | pub const FILE_SUPERSEDE = 0; | ||
| 312 | pub const FILE_OPEN = 1; | ||
| 313 | pub const FILE_CREATE = 2; | ||
| 314 | pub const FILE_OPEN_IF = 3; | ||
| 315 | pub const FILE_OVERWRITE = 4; | ||
| 316 | pub const FILE_OVERWRITE_IF = 5; | ||
| 317 | pub const FILE_MAXIMUM_DISPOSITION = 5; | ||
| 318 | |||
| 319 | // flags for NtCreateFile and NtOpenFile | ||
| 320 | pub const FILE_DIRECTORY_FILE = 0x00000001; | ||
| 321 | pub const FILE_WRITE_THROUGH = 0x00000002; | ||
| 322 | pub const FILE_SEQUENTIAL_ONLY = 0x00000004; | ||
| 323 | pub const FILE_NO_INTERMEDIATE_BUFFERING = 0x00000008; | ||
| 324 | pub const FILE_SYNCHRONOUS_IO_ALERT = 0x00000010; | ||
| 325 | pub const FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020; | ||
| 326 | pub const FILE_NON_DIRECTORY_FILE = 0x00000040; | ||
| 327 | pub const FILE_CREATE_TREE_CONNECTION = 0x00000080; | ||
| 328 | pub const FILE_COMPLETE_IF_OPLOCKED = 0x00000100; | ||
| 329 | pub const FILE_NO_EA_KNOWLEDGE = 0x00000200; | ||
| 330 | pub const FILE_OPEN_FOR_RECOVERY = 0x00000400; | ||
| 331 | pub const FILE_RANDOM_ACCESS = 0x00000800; | ||
| 332 | pub const FILE_DELETE_ON_CLOSE = 0x00001000; | ||
| 333 | pub const FILE_OPEN_BY_FILE_ID = 0x00002000; | ||
| 334 | pub const FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000; | ||
| 335 | pub const FILE_NO_COMPRESSION = 0x00008000; | ||
| 336 | pub const FILE_RESERVE_OPFILTER = 0x00100000; | ||
| 337 | pub const FILE_TRANSACTED_MODE = 0x00200000; | ||
| 338 | pub const FILE_OPEN_OFFLINE_FILE = 0x00400000; | ||
| 339 | pub const FILE_OPEN_FOR_FREE_SPACE_QUERY = 0x00800000; | ||
| 340 | |||
| 303 | pub const CREATE_ALWAYS = 2; | 341 | pub const CREATE_ALWAYS = 2; |
| 304 | pub const CREATE_NEW = 1; | 342 | pub const CREATE_NEW = 1; |
| 305 | pub const OPEN_ALWAYS = 4; | 343 | pub const OPEN_ALWAYS = 4; |
| ... | @@ -720,15 +758,119 @@ pub const VECTORED_EXCEPTION_HANDLER = stdcallcc fn (ExceptionInfo: *EXCEPTION_P | ... | @@ -720,15 +758,119 @@ pub const VECTORED_EXCEPTION_HANDLER = stdcallcc fn (ExceptionInfo: *EXCEPTION_P |
| 720 | 758 | ||
| 721 | pub const OBJECT_ATTRIBUTES = extern struct { | 759 | pub const OBJECT_ATTRIBUTES = extern struct { |
| 722 | Length: ULONG, | 760 | Length: ULONG, |
| 723 | RootDirectory: HANDLE, | 761 | RootDirectory: ?HANDLE, |
| 724 | ObjectName: *UNICODE_STRING, | 762 | ObjectName: *UNICODE_STRING, |
| 725 | Attributes: ULONG, | 763 | Attributes: ULONG, |
| 726 | SecurityDescriptor: ?*c_void, | 764 | SecurityDescriptor: ?*c_void, |
| 727 | SecurityQualityOfService: ?*c_void, | 765 | SecurityQualityOfService: ?*c_void, |
| 728 | }; | 766 | }; |
| 729 | 767 | ||
| 768 | pub const OBJ_INHERIT = 0x00000002; | ||
| 769 | pub const OBJ_PERMANENT = 0x00000010; | ||
| 770 | pub const OBJ_EXCLUSIVE = 0x00000020; | ||
| 771 | pub const OBJ_CASE_INSENSITIVE = 0x00000040; | ||
| 772 | pub const OBJ_OPENIF = 0x00000080; | ||
| 773 | pub const OBJ_OPENLINK = 0x00000100; | ||
| 774 | pub const OBJ_KERNEL_HANDLE = 0x00000200; | ||
| 775 | pub const OBJ_VALID_ATTRIBUTES = 0x000003F2; | ||
| 776 | |||
| 730 | pub const UNICODE_STRING = extern struct { | 777 | pub const UNICODE_STRING = extern struct { |
| 731 | Length: USHORT, | 778 | Length: c_ushort, |
| 732 | MaximumLength: USHORT, | 779 | MaximumLength: c_ushort, |
| 733 | Buffer: [*]WCHAR, | 780 | Buffer: [*]WCHAR, |
| 734 | }; | 781 | }; |
| 782 | |||
| 783 | pub const PEB = extern struct { | ||
| 784 | Reserved1: [2]BYTE, | ||
| 785 | BeingDebugged: BYTE, | ||
| 786 | Reserved2: [1]BYTE, | ||
| 787 | Reserved3: [2]PVOID, | ||
| 788 | Ldr: *PEB_LDR_DATA, | ||
| 789 | ProcessParameters: *RTL_USER_PROCESS_PARAMETERS, | ||
| 790 | Reserved4: [3]PVOID, | ||
| 791 | AtlThunkSListPtr: PVOID, | ||
| 792 | Reserved5: PVOID, | ||
| 793 | Reserved6: ULONG, | ||
| 794 | Reserved7: PVOID, | ||
| 795 | Reserved8: ULONG, | ||
| 796 | AtlThunkSListPtr32: ULONG, | ||
| 797 | Reserved9: [45]PVOID, | ||
| 798 | Reserved10: [96]BYTE, | ||
| 799 | PostProcessInitRoutine: PPS_POST_PROCESS_INIT_ROUTINE, | ||
| 800 | Reserved11: [128]BYTE, | ||
| 801 | Reserved12: [1]PVOID, | ||
| 802 | SessionId: ULONG, | ||
| 803 | }; | ||
| 804 | |||
| 805 | pub const PEB_LDR_DATA = extern struct { | ||
| 806 | Reserved1: [8]BYTE, | ||
| 807 | Reserved2: [3]PVOID, | ||
| 808 | InMemoryOrderModuleList: LIST_ENTRY, | ||
| 809 | }; | ||
| 810 | |||
| 811 | pub const RTL_USER_PROCESS_PARAMETERS = extern struct { | ||
| 812 | AllocationSize: ULONG, | ||
| 813 | Size: ULONG, | ||
| 814 | Flags: ULONG, | ||
| 815 | DebugFlags: ULONG, | ||
| 816 | ConsoleHandle: HANDLE, | ||
| 817 | ConsoleFlags: ULONG, | ||
| 818 | hStdInput: HANDLE, | ||
| 819 | hStdOutput: HANDLE, | ||
| 820 | hStdError: HANDLE, | ||
| 821 | CurrentDirectory: CURDIR, | ||
| 822 | DllPath: UNICODE_STRING, | ||
| 823 | ImagePathName: UNICODE_STRING, | ||
| 824 | CommandLine: UNICODE_STRING, | ||
| 825 | Environment: [*]WCHAR, | ||
| 826 | dwX: ULONG, | ||
| 827 | dwY: ULONG, | ||
| 828 | dwXSize: ULONG, | ||
| 829 | dwYSize: ULONG, | ||
| 830 | dwXCountChars: ULONG, | ||
| 831 | dwYCountChars: ULONG, | ||
| 832 | dwFillAttribute: ULONG, | ||
| 833 | dwFlags: ULONG, | ||
| 834 | dwShowWindow: ULONG, | ||
| 835 | WindowTitle: UNICODE_STRING, | ||
| 836 | Desktop: UNICODE_STRING, | ||
| 837 | ShellInfo: UNICODE_STRING, | ||
| 838 | RuntimeInfo: UNICODE_STRING, | ||
| 839 | DLCurrentDirectory: [0x20]RTL_DRIVE_LETTER_CURDIR, | ||
| 840 | }; | ||
| 841 | |||
| 842 | pub const RTL_DRIVE_LETTER_CURDIR = extern struct { | ||
| 843 | Flags: c_ushort, | ||
| 844 | Length: c_ushort, | ||
| 845 | TimeStamp: ULONG, | ||
| 846 | DosPath: UNICODE_STRING, | ||
| 847 | }; | ||
| 848 | |||
| 849 | pub const PPS_POST_PROCESS_INIT_ROUTINE = ?extern fn () void; | ||
| 850 | |||
| 851 | pub const FILE_BOTH_DIR_INFORMATION = extern struct { | ||
| 852 | NextEntryOffset: ULONG, | ||
| 853 | FileIndex: ULONG, | ||
| 854 | CreationTime: LARGE_INTEGER, | ||
| 855 | LastAccessTime: LARGE_INTEGER, | ||
| 856 | LastWriteTime: LARGE_INTEGER, | ||
| 857 | ChangeTime: LARGE_INTEGER, | ||
| 858 | EndOfFile: LARGE_INTEGER, | ||
| 859 | AllocationSize: LARGE_INTEGER, | ||
| 860 | FileAttributes: ULONG, | ||
| 861 | FileNameLength: ULONG, | ||
| 862 | EaSize: ULONG, | ||
| 863 | ShortNameLength: CHAR, | ||
| 864 | ShortName: [12]WCHAR, | ||
| 865 | FileName: [1]WCHAR, | ||
| 866 | }; | ||
| 867 | pub const FILE_BOTH_DIRECTORY_INFORMATION = FILE_BOTH_DIR_INFORMATION; | ||
| 868 | |||
| 869 | pub const IO_APC_ROUTINE = extern fn (PVOID, *IO_STATUS_BLOCK, ULONG) void; | ||
| 870 | |||
| 871 | pub const CURDIR = extern struct { | ||
| 872 | DosPath: UNICODE_STRING, | ||
| 873 | Handle: HANDLE, | ||
| 874 | }; | ||
| 875 | |||
| 876 | pub const DUPLICATE_SAME_ACCESS = 2; | ||
| \ No newline at end of file | |||
lib/std/os/windows/kernel32.zig+2| ... | @@ -47,6 +47,8 @@ pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ | ... | @@ -47,6 +47,8 @@ pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ |
| 47 | 47 | ||
| 48 | pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL; | 48 | pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL; |
| 49 | 49 | ||
| 50 | pub extern "kernel32" stdcallcc fn DuplicateHandle(hSourceProcessHandle: HANDLE, hSourceHandle: HANDLE, hTargetProcessHandle: HANDLE, lpTargetHandle: *HANDLE, dwDesiredAccess: DWORD, bInheritHandle: BOOL, dwOptions: DWORD) BOOL; | ||
| 51 | |||
| 50 | pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn; | 52 | pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn; |
| 51 | 53 | ||
| 52 | pub extern "kernel32" stdcallcc fn FindFirstFileW(lpFileName: [*]const u16, lpFindFileData: *WIN32_FIND_DATAW) HANDLE; | 54 | pub extern "kernel32" stdcallcc fn FindFirstFileW(lpFileName: [*]const u16, lpFindFileData: *WIN32_FIND_DATAW) HANDLE; |
lib/std/os/windows/ntdll.zig+23-2| ... | @@ -13,12 +13,33 @@ pub extern "NtDll" stdcallcc fn NtCreateFile( | ... | @@ -13,12 +13,33 @@ pub extern "NtDll" stdcallcc fn NtCreateFile( |
| 13 | DesiredAccess: ACCESS_MASK, | 13 | DesiredAccess: ACCESS_MASK, |
| 14 | ObjectAttributes: *OBJECT_ATTRIBUTES, | 14 | ObjectAttributes: *OBJECT_ATTRIBUTES, |
| 15 | IoStatusBlock: *IO_STATUS_BLOCK, | 15 | IoStatusBlock: *IO_STATUS_BLOCK, |
| 16 | AllocationSize: *LARGE_INTEGER, | 16 | AllocationSize: ?*LARGE_INTEGER, |
| 17 | FileAttributes: ULONG, | 17 | FileAttributes: ULONG, |
| 18 | ShareAccess: ULONG, | 18 | ShareAccess: ULONG, |
| 19 | CreateDisposition: ULONG, | 19 | CreateDisposition: ULONG, |
| 20 | CreateOptions: ULONG, | 20 | CreateOptions: ULONG, |
| 21 | EaBuffer: *c_void, | 21 | EaBuffer: ?*c_void, |
| 22 | EaLength: ULONG, | 22 | EaLength: ULONG, |
| 23 | ) NTSTATUS; | 23 | ) NTSTATUS; |
| 24 | pub extern "NtDll" stdcallcc fn NtClose(Handle: HANDLE) NTSTATUS; | 24 | pub extern "NtDll" stdcallcc fn NtClose(Handle: HANDLE) NTSTATUS; |
| 25 | pub extern "NtDll" stdcallcc fn RtlDosPathNameToNtPathName_U( | ||
| 26 | DosPathName: [*]const u16, | ||
| 27 | NtPathName: *UNICODE_STRING, | ||
| 28 | NtFileNamePart: ?*?[*]const u16, | ||
| 29 | DirectoryInfo: ?*CURDIR, | ||
| 30 | ) BOOL; | ||
| 31 | pub extern "NtDll" stdcallcc fn RtlFreeUnicodeString(UnicodeString: *UNICODE_STRING) void; | ||
| 32 | |||
| 33 | pub extern "NtDll" stdcallcc fn NtQueryDirectoryFile( | ||
| 34 | FileHandle: HANDLE, | ||
| 35 | Event: ?HANDLE, | ||
| 36 | ApcRoutine: ?IO_APC_ROUTINE, | ||
| 37 | ApcContext: ?*c_void, | ||
| 38 | IoStatusBlock: *IO_STATUS_BLOCK, | ||
| 39 | FileInformation: *c_void, | ||
| 40 | Length: ULONG, | ||
| 41 | FileInformationClass: FILE_INFORMATION_CLASS, | ||
| 42 | ReturnSingleEntry: BOOLEAN, | ||
| 43 | FileName: ?*UNICODE_STRING, | ||
| 44 | RestartScan: BOOLEAN, | ||
| 45 | ) NTSTATUS; |
lib/std/os/windows/status.zig+8| ... | @@ -3,3 +3,11 @@ pub const SUCCESS = 0x00000000; | ... | @@ -3,3 +3,11 @@ pub const SUCCESS = 0x00000000; |
| 3 | 3 | ||
| 4 | /// The data was too large to fit into the specified buffer. | 4 | /// The data was too large to fit into the specified buffer. |
| 5 | pub const BUFFER_OVERFLOW = 0x80000005; | 5 | pub const BUFFER_OVERFLOW = 0x80000005; |
| 6 | |||
| 7 | pub const INVALID_PARAMETER = 0xC000000D; | ||
| 8 | pub const ACCESS_DENIED = 0xC0000022; | ||
| 9 | pub const OBJECT_NAME_INVALID = 0xC0000033; | ||
| 10 | pub const OBJECT_NAME_NOT_FOUND = 0xC0000034; | ||
| 11 | pub const OBJECT_PATH_NOT_FOUND = 0xC000003A; | ||
| 12 | pub const OBJECT_PATH_SYNTAX_BAD = 0xC000003B; | ||
| 13 | pub const FILE_IS_A_DIRECTORY = 0xC00000BA; |
lib/std/progress.zig+2-2| ... | @@ -155,11 +155,11 @@ pub const Progress = struct { | ... | @@ -155,11 +155,11 @@ pub const Progress = struct { |
| 155 | } | 155 | } |
| 156 | if (node.estimated_total_items) |total| { | 156 | if (node.estimated_total_items) |total| { |
| 157 | if (need_ellipse) self.bufWrite(&end, " "); | 157 | if (need_ellipse) self.bufWrite(&end, " "); |
| 158 | self.bufWrite(&end, "[{}/{}] ", node.completed_items, total); | 158 | self.bufWrite(&end, "[{}/{}] ", node.completed_items + 1, total); |
| 159 | need_ellipse = false; | 159 | need_ellipse = false; |
| 160 | } else if (node.completed_items != 0) { | 160 | } else if (node.completed_items != 0) { |
| 161 | if (need_ellipse) self.bufWrite(&end, " "); | 161 | if (need_ellipse) self.bufWrite(&end, " "); |
| 162 | self.bufWrite(&end, "[{}] ", node.completed_items); | 162 | self.bufWrite(&end, "[{}] ", node.completed_items + 1); |
| 163 | need_ellipse = false; | 163 | need_ellipse = false; |
| 164 | } | 164 | } |
| 165 | } | 165 | } |
lib/std/special/docs/index.html+394-216| ... | @@ -5,62 +5,228 @@ | ... | @@ -5,62 +5,228 @@ |
| 5 | <title>Documentation - Zig</title> | 5 | <title>Documentation - Zig</title> |
| 6 | <link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAgklEQVR4AWMYWuD7EllJIM4G4g4g5oIJ/odhOJ8wToOxSTXgNxDHoeiBMfA4+wGShjyYOCkG/IGqWQziEzYAoUAeiF9D5U+DxEg14DRU7jWIT5IBIOdCxf+A+CQZAAoopEB7QJwBCBwHiip8UYmRdrAlDpIMgApwQZNnNii5Dq0MBgCxxycBnwEd+wAAAABJRU5ErkJggg=="> | 6 | <link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAgklEQVR4AWMYWuD7EllJIM4G4g4g5oIJ/odhOJ8wToOxSTXgNxDHoeiBMfA4+wGShjyYOCkG/IGqWQziEzYAoUAeiF9D5U+DxEg14DRU7jWIT5IBIOdCxf+A+CQZAAoopEB7QJwBCBwHiip8UYmRdrAlDpIMgApwQZNnNii5Dq0MBgCxxycBnwEd+wAAAABJRU5ErkJggg=="> |
| 7 | <style type="text/css"> | 7 | <style type="text/css"> |
| 8 | body { | 8 | :root { |
| 9 | font-family: system-ui, -apple-system, Roboto, "Segoe UI", sans-serif; | 9 | font-size: 1em; |
| 10 | max-width: 60em; | 10 | --ui: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; |
| 11 | --mono: "Source Code Pro", monospace; | ||
| 12 | --tx-color: #141414; | ||
| 13 | --bg-color: #ffffff; | ||
| 14 | --link-color: #2A6286; | ||
| 15 | --sidebar-sh-color: rgba(0, 0, 0, 0.09); | ||
| 16 | --sidebar-pkg-bg-color: #f1f1f1; | ||
| 17 | --sidebar-pkglnk-tx-color: #141414; | ||
| 18 | --sidebar-pkglnk-tx-color-hover: #fff; | ||
| 19 | --sidebar-pkglnk-tx-color-active: #000; | ||
| 20 | --sidebar-pkglnk-bg-color: transparent; | ||
| 21 | --sidebar-pkglnk-bg-color-hover: #555; | ||
| 22 | --sidebar-pkglnk-bg-color-active: #FFBB4D; | ||
| 23 | --search-bg-color: #f3f3f3; | ||
| 24 | --search-bg-color-focus: #ffffff; | ||
| 25 | --search-sh-color: rgba(0, 0, 0, 0.18); | ||
| 26 | --help-sh-color: rgba(0, 0, 0, 0.75); | ||
| 11 | } | 27 | } |
| 28 | |||
| 29 | a { | ||
| 30 | text-decoration: none; | ||
| 31 | } | ||
| 32 | |||
| 33 | a:hover { | ||
| 34 | text-decoration: underline; | ||
| 35 | } | ||
| 36 | |||
| 12 | .hidden { | 37 | .hidden { |
| 13 | display: none; | 38 | display: none; |
| 14 | } | 39 | } |
| 15 | a { | 40 | |
| 16 | color: #2A6286; | 41 | /* layout */ |
| 42 | .canvas { | ||
| 43 | width: 100vw; | ||
| 44 | height: 100vh; | ||
| 45 | overflow: hidden; | ||
| 46 | margin: 0; | ||
| 47 | padding: 0; | ||
| 48 | font-family: var(--ui); | ||
| 49 | color: var(--tx-color); | ||
| 50 | background-color: var(--bg-color); | ||
| 17 | } | 51 | } |
| 18 | pre{ | 52 | |
| 19 | font-family:"Source Code Pro",monospace; | 53 | .flex-main { |
| 20 | font-size:1em; | 54 | display: flex; |
| 21 | background-color:#F5F5F5; | 55 | width: 100%; |
| 22 | padding:1em; | 56 | height: 100%; |
| 23 | overflow-x: auto; | 57 | justify-content: center; |
| 58 | |||
| 59 | z-index: 100; | ||
| 24 | } | 60 | } |
| 25 | code { | 61 | |
| 26 | font-family:"Source Code Pro",monospace; | 62 | .flex-filler { |
| 27 | font-size:1em; | 63 | flex-grow: 1; |
| 64 | flex-shrink: 1; | ||
| 28 | } | 65 | } |
| 29 | nav { | 66 | |
| 30 | width: 10em; | 67 | .flex-left { |
| 31 | position: fixed; | 68 | width: 12rem; |
| 32 | left: 0; | 69 | max-width: 15vw; |
| 33 | top: 0; | 70 | min-width: 9.5rem; |
| 34 | height: 100vh; | ||
| 35 | overflow: auto; | 71 | overflow: auto; |
| 72 | overflow-wrap: break-word; | ||
| 73 | flex-shrink: 0; | ||
| 74 | flex-grow: 0; | ||
| 75 | |||
| 76 | z-index: 300; | ||
| 36 | } | 77 | } |
| 37 | nav h2 { | 78 | |
| 38 | font-size: 1.2em; | 79 | .flex-right { |
| 39 | text-decoration: underline; | 80 | display: flex; |
| 40 | margin: 0; | 81 | overflow: auto; |
| 41 | padding: 0.5em 0; | 82 | flex-grow: 1; |
| 42 | text-align: center; | 83 | flex-shrink: 1; |
| 84 | |||
| 85 | z-index: 200; | ||
| 86 | } | ||
| 87 | |||
| 88 | .flex-right > .wrap { | ||
| 89 | width: 60rem; | ||
| 90 | max-width: 85vw; | ||
| 91 | flex-shrink: 1; | ||
| 92 | } | ||
| 93 | |||
| 94 | .help-modal { | ||
| 95 | z-index: 400; | ||
| 96 | } | ||
| 97 | |||
| 98 | /* sidebar */ | ||
| 99 | .sidebar { | ||
| 100 | font-size: 1rem; | ||
| 101 | background-color: var(--bg-color); | ||
| 102 | box-shadow: 0 0 1rem var(--sidebar-sh-color); | ||
| 103 | } | ||
| 104 | |||
| 105 | .sidebar .logo { | ||
| 106 | padding: 1rem 0.35rem 0.35rem 0.35rem; | ||
| 107 | } | ||
| 108 | |||
| 109 | .sidebar .logo > svg { | ||
| 110 | display: block; | ||
| 111 | overflow: visible; | ||
| 112 | } | ||
| 113 | |||
| 114 | .sidebar h2 { | ||
| 115 | margin: 0.5rem; | ||
| 116 | padding: 0; | ||
| 117 | font-size: 1.2rem; | ||
| 43 | } | 118 | } |
| 44 | nav p { | 119 | |
| 120 | .sidebar h2 > span { | ||
| 121 | border-bottom: 0.125rem dotted var(--tx-color); | ||
| 122 | } | ||
| 123 | |||
| 124 | .sidebar .packages { | ||
| 125 | list-style-type: none; | ||
| 45 | margin: 0; | 126 | margin: 0; |
| 46 | padding: 0; | 127 | padding: 0; |
| 47 | text-align: center; | 128 | background-color: var(--sidebar-pkg-bg-color); |
| 129 | } | ||
| 130 | |||
| 131 | .sidebar .packages > li > a { | ||
| 132 | display: block; | ||
| 133 | padding: 0.5rem 1rem; | ||
| 134 | color: var(--sidebar-pkglnk-tx-color); | ||
| 135 | background-color: var(--sidebar-pkglnk-bg-color); | ||
| 136 | text-decoration: none; | ||
| 137 | } | ||
| 138 | |||
| 139 | .sidebar .packages > li > a:hover { | ||
| 140 | color: var(--sidebar-pkglnk-tx-color-hover); | ||
| 141 | background-color: var(--sidebar-pkglnk-bg-color-hover); | ||
| 142 | } | ||
| 143 | |||
| 144 | .sidebar .packages > li > a.active { | ||
| 145 | color: var(--sidebar-pkglnk-tx-color-active); | ||
| 146 | background-color: var(--sidebar-pkglnk-bg-color-active); | ||
| 147 | } | ||
| 148 | |||
| 149 | .sidebar p.str { | ||
| 150 | margin: 0.5rem; | ||
| 151 | font-family: var(--mono); | ||
| 152 | } | ||
| 153 | |||
| 154 | /* docs section */ | ||
| 155 | .docs { | ||
| 156 | padding: 1rem 0.7rem 2.4rem 1.4rem; | ||
| 157 | font-size: 1rem; | ||
| 158 | background-color: var(--bg-color); | ||
| 159 | overflow-wrap: break-word; | ||
| 160 | } | ||
| 161 | |||
| 162 | .docs .search { | ||
| 163 | width: 100%; | ||
| 164 | margin-bottom: 0.8rem; | ||
| 165 | padding: 0.5rem; | ||
| 166 | font-size: 1rem; | ||
| 167 | font-family: var(--ui); | ||
| 168 | color: var(--tx-color); | ||
| 169 | background-color: var(--search-bg-color); | ||
| 170 | border-top: 0; | ||
| 171 | border-left: 0; | ||
| 172 | border-right: 0; | ||
| 173 | border-bottom-width: 0.125rem; | ||
| 174 | border-bottom-style: solid; | ||
| 175 | border-bottom-color: var(--tx-color); | ||
| 176 | outline: none; | ||
| 177 | transition: border-bottom-color 0.35s, background 0.35s, box-shadow 0.35s; | ||
| 178 | } | ||
| 179 | |||
| 180 | .docs .search:focus { | ||
| 181 | background-color: var(--search-bg-color-focus); | ||
| 182 | border-bottom-color: #ffbb4d; | ||
| 183 | box-shadow: 0 0.3em 1em 0.125em var(--search-sh-color); | ||
| 184 | } | ||
| 185 | |||
| 186 | .docs .search::placeholder { | ||
| 187 | font-size: 1rem; | ||
| 188 | font-family: var(--ui); | ||
| 189 | color: var(--tx-color); | ||
| 190 | opacity: 0.5; | ||
| 191 | } | ||
| 192 | |||
| 193 | .docs a { | ||
| 194 | color: var(--link-color); | ||
| 195 | } | ||
| 196 | |||
| 197 | .docs p { | ||
| 198 | margin: 0.8rem 0; | ||
| 199 | } | ||
| 200 | |||
| 201 | .docs pre { | ||
| 202 | font-family: var(--mono); | ||
| 203 | font-size:1em; | ||
| 204 | background-color:#F5F5F5; | ||
| 205 | padding:1em; | ||
| 206 | overflow-x: auto; | ||
| 48 | } | 207 | } |
| 49 | section { | 208 | |
| 50 | margin-left: 10em; | 209 | .docs code { |
| 210 | font-family: var(--mono); | ||
| 211 | font-size: 1em; | ||
| 51 | } | 212 | } |
| 52 | section h1 { | 213 | |
| 53 | border-bottom: 1px dashed; | 214 | .docs h1 { |
| 215 | font-size: 1.4em; | ||
| 216 | margin: 0.8em 0; | ||
| 217 | padding: 0; | ||
| 218 | border-bottom: 0.0625rem dashed; | ||
| 54 | } | 219 | } |
| 55 | section h2 { | 220 | |
| 221 | .docs h2 { | ||
| 56 | font-size: 1.3em; | 222 | font-size: 1.3em; |
| 57 | margin: 0.5em 0; | 223 | margin: 0.5em 0; |
| 58 | padding: 0; | 224 | padding: 0; |
| 59 | border-bottom: 1px solid; | 225 | border-bottom: 0.0625rem solid; |
| 60 | } | 226 | } |
| 61 | #listNav { | 227 | #listNav { |
| 62 | list-style-type: none; | 228 | list-style-type: none; |
| 63 | margin: 0.5em 0 0 0; | 229 | margin: 0; |
| 64 | padding: 0; | 230 | padding: 0; |
| 65 | overflow: hidden; | 231 | overflow: hidden; |
| 66 | background-color: #f1f1f1; | 232 | background-color: #f1f1f1; |
| ... | @@ -84,26 +250,14 @@ | ... | @@ -84,26 +250,14 @@ |
| 84 | color: #000; | 250 | color: #000; |
| 85 | } | 251 | } |
| 86 | 252 | ||
| 87 | #listPkgs { | 253 | #listSearchResults li.selected { |
| 88 | list-style-type: none; | 254 | background-color: #93e196; |
| 89 | margin: 0; | ||
| 90 | padding: 0; | ||
| 91 | background-color: #f1f1f1; | ||
| 92 | } | ||
| 93 | #listPkgs li a { | ||
| 94 | display: block; | ||
| 95 | color: #000; | ||
| 96 | padding: 0.5em 1em; | ||
| 97 | text-decoration: none; | ||
| 98 | } | ||
| 99 | #listPkgs li a:hover { | ||
| 100 | background-color: #555; | ||
| 101 | color: #fff; | ||
| 102 | } | 255 | } |
| 103 | #listPkgs li a.active { | 256 | |
| 104 | background-color: #FFBB4D; | 257 | #tableFnErrors dt { |
| 105 | color: #000; | 258 | font-weight: bold; |
| 106 | } | 259 | } |
| 260 | |||
| 107 | #listFnExamples { | 261 | #listFnExamples { |
| 108 | list-style-type: none; | 262 | list-style-type: none; |
| 109 | margin: 0; | 263 | margin: 0; |
| ... | @@ -114,67 +268,76 @@ | ... | @@ -114,67 +268,76 @@ |
| 114 | white-space: nowrap; | 268 | white-space: nowrap; |
| 115 | overflow-x: auto; | 269 | overflow-x: auto; |
| 116 | } | 270 | } |
| 117 | #logo { | 271 | |
| 118 | width: 8em; | 272 | .docs td { |
| 119 | padding: 0.5em 1em; | 273 | vertical-align: top; |
| 274 | margin: 0; | ||
| 275 | padding: 0.5em; | ||
| 276 | max-width: 27em; | ||
| 277 | text-overflow: ellipsis; | ||
| 278 | overflow-x: hidden; | ||
| 120 | } | 279 | } |
| 121 | 280 | ||
| 122 | #search { | 281 | /* help dialog */ |
| 282 | .help-modal { | ||
| 283 | display: flex; | ||
| 123 | width: 100%; | 284 | width: 100%; |
| 124 | } | 285 | height: 100%; |
| 125 | |||
| 126 | #helpDialog { | ||
| 127 | width: 21em; | ||
| 128 | height: 19em; | ||
| 129 | position: fixed; | 286 | position: fixed; |
| 130 | top: 0; | 287 | top: 0; |
| 131 | left: 0; | 288 | left: 0; |
| 132 | background-color: #333; | 289 | justify-content: center; |
| 290 | align-items: center; | ||
| 291 | background-color: rgba(0, 0, 0, 0.15); | ||
| 292 | backdrop-filter: blur(0.3em); | ||
| 293 | } | ||
| 294 | |||
| 295 | .help-modal > .dialog { | ||
| 296 | max-width: 97vw; | ||
| 297 | max-height: 97vh; | ||
| 298 | overflow: auto; | ||
| 299 | font-size: 1rem; | ||
| 133 | color: #fff; | 300 | color: #fff; |
| 134 | border: 1px solid #fff; | 301 | background-color: #333; |
| 302 | border: 0.125rem solid #000; | ||
| 303 | box-shadow: 0 0.5rem 2.5rem 0.3rem var(--help-sh-color); | ||
| 135 | } | 304 | } |
| 136 | #helpDialog h1 { | 305 | |
| 137 | text-align: center; | 306 | .help-modal h1 { |
| 307 | margin: 0.75em 2.5em 1em 2.5em; | ||
| 138 | font-size: 1.5em; | 308 | font-size: 1.5em; |
| 309 | text-align: center; | ||
| 139 | } | 310 | } |
| 140 | #helpDialog dt, #helpDialog dd { | 311 | |
| 312 | .help-modal dt, .help-modal dd { | ||
| 141 | display: inline; | 313 | display: inline; |
| 142 | margin: 0 0.2em; | 314 | margin: 0 0.2em; |
| 143 | } | 315 | } |
| 144 | kbd { | 316 | |
| 317 | .help-modal dl { | ||
| 318 | margin-left: 0.5em; | ||
| 319 | margin-right: 0.5em; | ||
| 320 | } | ||
| 321 | |||
| 322 | .help-modal kbd { | ||
| 323 | display: inline-block; | ||
| 324 | padding: 0.3em 0.2em; | ||
| 325 | font-size: 1.2em; | ||
| 326 | font-size: var(--mono); | ||
| 327 | line-height: 0.8em; | ||
| 328 | vertical-align: middle; | ||
| 145 | color: #000; | 329 | color: #000; |
| 146 | background-color: #fafbfc; | 330 | background-color: #fafbfc; |
| 147 | border-color: #d1d5da; | 331 | border-color: #d1d5da; |
| 148 | border-bottom-color: #c6cbd1; | 332 | border-bottom-color: #c6cbd1; |
| 333 | border: solid 0.0625em; | ||
| 334 | border-radius: 0.1875em; | ||
| 149 | box-shadow-color: #c6cbd1; | 335 | box-shadow-color: #c6cbd1; |
| 150 | display: inline-block; | 336 | box-shadow: inset 0 -0.0625em 0; |
| 151 | padding: 0.3em 0.2em; | ||
| 152 | font: 1.2em monospace; | ||
| 153 | line-height: 0.8em; | ||
| 154 | vertical-align: middle; | ||
| 155 | border: solid 1px; | ||
| 156 | border-radius: 3px; | ||
| 157 | box-shadow: inset 0 -1px 0; | ||
| 158 | cursor: default; | 337 | cursor: default; |
| 159 | } | 338 | } |
| 160 | 339 | ||
| 161 | #listSearchResults li.selected { | 340 | /* tokens */ |
| 162 | background-color: #93e196; | ||
| 163 | } | ||
| 164 | |||
| 165 | #tableFnErrors dt { | ||
| 166 | font-weight: bold; | ||
| 167 | } | ||
| 168 | |||
| 169 | td { | ||
| 170 | vertical-align: top; | ||
| 171 | margin: 0; | ||
| 172 | padding: 0.5em; | ||
| 173 | max-width: 27em; | ||
| 174 | text-overflow: ellipsis; | ||
| 175 | overflow-x: hidden; | ||
| 176 | } | ||
| 177 | |||
| 178 | .tok-kw { | 341 | .tok-kw { |
| 179 | color: #333; | 342 | color: #333; |
| 180 | font-weight: bold; | 343 | font-weight: bold; |
| ... | @@ -203,16 +366,29 @@ | ... | @@ -203,16 +366,29 @@ |
| 203 | color: #458; | 366 | color: #458; |
| 204 | font-weight: bold; | 367 | font-weight: bold; |
| 205 | } | 368 | } |
| 206 | 369 | ||
| 370 | /* dark mode */ | ||
| 207 | @media (prefers-color-scheme: dark) { | 371 | @media (prefers-color-scheme: dark) { |
| 208 | body{ | 372 | |
| 209 | background-color: #111; | 373 | :root { |
| 210 | color: #bbb; | 374 | --tx-color: #bbb; |
| 375 | --bg-color: #111; | ||
| 376 | --link-color: #88f; | ||
| 377 | --sidebar-sh-color: rgba(128, 128, 128, 0.09); | ||
| 378 | --sidebar-pkg-bg-color: #333; | ||
| 379 | --sidebar-pkglnk-tx-color: #fff; | ||
| 380 | --sidebar-pkglnk-tx-color-hover: #fff; | ||
| 381 | --sidebar-pkglnk-tx-color-active: #000; | ||
| 382 | --sidebar-pkglnk-bg-color: transparent; | ||
| 383 | --sidebar-pkglnk-bg-color-hover: #555; | ||
| 384 | --sidebar-pkglnk-bg-color-active: #FFBB4D; | ||
| 385 | --search-bg-color: #3c3c3c; | ||
| 386 | --search-bg-color-focus: #000; | ||
| 387 | --search-sh-color: rgba(255, 255, 255, 0.28); | ||
| 388 | --help-sh-color: rgba(142, 142, 142, 0.5); | ||
| 211 | } | 389 | } |
| 212 | a { | 390 | |
| 213 | color: #88f; | 391 | .docs pre { |
| 214 | } | ||
| 215 | pre{ | ||
| 216 | background-color:#2A2A2A; | 392 | background-color:#2A2A2A; |
| 217 | } | 393 | } |
| 218 | #listNav { | 394 | #listNav { |
| ... | @@ -229,20 +405,6 @@ | ... | @@ -229,20 +405,6 @@ |
| 229 | background-color: #FFBB4D; | 405 | background-color: #FFBB4D; |
| 230 | color: #000; | 406 | color: #000; |
| 231 | } | 407 | } |
| 232 | #listPkgs { | ||
| 233 | background-color: #333; | ||
| 234 | } | ||
| 235 | #listPkgs li a { | ||
| 236 | color: #fff; | ||
| 237 | } | ||
| 238 | #listPkgs li a:hover { | ||
| 239 | background-color: #555; | ||
| 240 | color: #fff; | ||
| 241 | } | ||
| 242 | #listPkgs li a.active { | ||
| 243 | background-color: #FFBB4D; | ||
| 244 | color: #000; | ||
| 245 | } | ||
| 246 | #listSearchResults li.selected { | 408 | #listSearchResults li.selected { |
| 247 | background-color: #000; | 409 | background-color: #000; |
| 248 | } | 410 | } |
| ... | @@ -273,112 +435,128 @@ | ... | @@ -273,112 +435,128 @@ |
| 273 | .tok-type { | 435 | .tok-type { |
| 274 | color: #68f; | 436 | color: #68f; |
| 275 | } | 437 | } |
| 438 | |||
| 276 | } | 439 | } |
| 277 | </style> | 440 | </style> |
| 278 | </head> | 441 | </head> |
| 279 | <body> | 442 | <body class="canvas"> |
| 280 | <nav> | 443 | <div class="flex-main"> |
| 281 | <img alt="ZIG" id="logo" src="data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAxNTAgMTAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxnIGZpbGw9IiNmN2E0MWQiPjxwYXRoIGQ9Im0wIDEwdjgwaDE5bDYtMTAgMTItMTBoLTE3di00MGgxNXYtMjB6bTQwIDB2MjBoNjJ2LTIwem05MSAwLTYgMTAtMTIgMTBoMTd2NDBoLTE1djIwaDM1di04MHptLTgzIDYwdjIwaDYydi0yMHoiIHNoYXBlLXJlbmRlcmluZz0iY3Jpc3BFZGdlcyIvPjxwYXRoIGQ9Im0zNyA3MC0xOCAyMHYtMTV6Ii8+PHBhdGggZD0ibTExMyAzMCAxOC0yMHYxNXoiLz48cGF0aCBkPSJtOTYuOTggMTAuNjMgMzYuMjgtMTAuNC04MC4yOSA4OS4xNy0zNi4yOCAxMC40eiIvPjwvZz48L3N2Zz4K"></img> | 444 | <div class="flex-filler"></div> |
| 282 | <div id="sectPkgs" class="hidden"> | 445 | <div class="flex-left sidebar"> |
| 283 | <h2>Packages</h2> | 446 | <nav> |
| 284 | <ul id="listPkgs"> | 447 | <div class="logo"> |
| 285 | </ul> | 448 | <svg version="1.1" viewBox="0 0 150 80" xmlns="http://www.w3.org/2000/svg"> |
| 286 | </div> | 449 | <g fill="#f7a41d"> |
| 287 | <div id="sectInfo" class="hidden"> | 450 | <path d="m0,-0.08899l0,80l19,0l6,-10l12,-10l-17,0l0,-40l15,0l0,-20l-35,0zm40,0l0,20l62,0l0,-20l-62,0zm91,0l-6,10l-12,10l17,0l0,40l-15,0l0,20l35,0l0,-80l-19,0zm-83,60l0,20l62,0l0,-20l-62,0z" shape-rendering="crispEdges"></path> |
| 288 | <h2>Zig Version</h2> | 451 | <path d="m37,59.91101l-18,20l0,-15l18,-5z"></path> |
| 289 | <p id="tdZigVer"></p> | 452 | <path d="m113,19.91101l18,-20l0,15l-18,5z"></path> |
| 290 | <h2>Target</h2> | 453 | <path d="m96.98,0.54101l36.28,-10.4l-80.29,89.17l-36.28,10.4l80.29,-89.17z"></path> |
| 291 | <p id="tdTarget"></p> | 454 | </g> |
| 455 | </svg> | ||
| 456 | </div> | ||
| 457 | <div id="sectPkgs" class="hidden"> | ||
| 458 | <h2><span>Packages</span></h2> | ||
| 459 | <ul id="listPkgs" class="packages"></ul> | ||
| 460 | </div> | ||
| 461 | <div id="sectInfo" class="hidden"> | ||
| 462 | <h2><span>Zig Version</span></h2> | ||
| 463 | <p class="str" id="tdZigVer"></p> | ||
| 464 | <h2><span>Target</span></h2> | ||
| 465 | <p class="str" id="tdTarget"></p> | ||
| 466 | </div> | ||
| 467 | </nav> | ||
| 292 | </div> | 468 | </div> |
| 293 | </nav> | 469 | <div class="flex-right"> |
| 294 | <section> | 470 | <div class="wrap"> |
| 295 | <input type="search" id="search" autocomplete="off" spellcheck="false" placeholder="`s` to search, `?` to see more options"> | 471 | <section class="docs"> |
| 296 | <p id="status">Loading...</p> | 472 | <input type="search" class="search" id="search" autocomplete="off" spellcheck="false" placeholder="`s` to search, `?` to see more options"> |
| 297 | <div id="sectNav" class="hidden"><ul id="listNav"></ul></div> | 473 | <p id="status">Loading...</p> |
| 298 | <div id="fnProto" class="hidden"> | 474 | <div id="sectNav" class="hidden"><ul id="listNav"></ul></div> |
| 299 | <pre id="fnProtoCode"></pre> | 475 | <div id="fnProto" class="hidden"> |
| 300 | </div> | 476 | <pre id="fnProtoCode"></pre> |
| 301 | <h1 id="hdrName" class="hidden"></h1> | 477 | </div> |
| 302 | <div id="fnNoExamples" class="hidden"> | 478 | <h1 id="hdrName" class="hidden"></h1> |
| 303 | <p>This function is not tested or referenced.</p> | 479 | <div id="fnNoExamples" class="hidden"> |
| 304 | </div> | 480 | <p>This function is not tested or referenced.</p> |
| 305 | <div id="declNoRef" class="hidden"> | 481 | </div> |
| 306 | <p> | 482 | <div id="declNoRef" class="hidden"> |
| 307 | This declaration is not tested or referenced, and it has therefore not been included in | 483 | <p> |
| 308 | semantic analysis, which means the only documentation available is whatever is in the | 484 | This declaration is not tested or referenced, and it has therefore not been included in |
| 309 | doc comments. | 485 | semantic analysis, which means the only documentation available is whatever is in the |
| 310 | </p> | 486 | doc comments. |
| 311 | </div> | 487 | </p> |
| 312 | <div id="fnDocs" class="hidden"></div> | 488 | </div> |
| 313 | <div id="sectFnErrors" class="hidden"> | 489 | <div id="fnDocs" class="hidden"></div> |
| 314 | <h2>Errors</h2> | 490 | <div id="sectFnErrors" class="hidden"> |
| 315 | <div id="fnErrorsAnyError"> | 491 | <h2>Errors</h2> |
| 316 | <p><span class="tok-type">anyerror</span> means the error set is known only at runtime.</p> | 492 | <div id="fnErrorsAnyError"> |
| 317 | </div> | 493 | <p><span class="tok-type">anyerror</span> means the error set is known only at runtime.</p> |
| 318 | <div id="tableFnErrors"><dl id="listFnErrors"></dl></div> | 494 | </div> |
| 319 | </div> | 495 | <div id="tableFnErrors"><dl id="listFnErrors"></dl></div> |
| 320 | <div id="sectSearchResults" class="hidden"> | 496 | </div> |
| 321 | <h2>Search Results</h2> | 497 | <div id="sectSearchResults" class="hidden"> |
| 322 | <ul id="listSearchResults"></ul> | 498 | <h2>Search Results</h2> |
| 323 | </div> | 499 | <ul id="listSearchResults"></ul> |
| 324 | <div id="sectSearchNoResults" class="hidden"> | 500 | </div> |
| 325 | <h2>No Results Found</h2> | 501 | <div id="sectSearchNoResults" class="hidden"> |
| 326 | <p>Press escape to exit search and then '?' to see more options.</p> | 502 | <h2>No Results Found</h2> |
| 327 | </div> | 503 | <p>Press escape to exit search and then '?' to see more options.</p> |
| 328 | <div id="sectFields" class="hidden"> | 504 | </div> |
| 329 | <h2>Fields</h2> | 505 | <div id="sectFields" class="hidden"> |
| 330 | <div id="listFields"> | 506 | <h2>Fields</h2> |
| 507 | <div id="listFields"></div> | ||
| 508 | </div> | ||
| 509 | <div id="sectTypes" class="hidden"> | ||
| 510 | <h2>Types</h2> | ||
| 511 | <ul id="listTypes"></ul> | ||
| 512 | </div> | ||
| 513 | <div id="sectNamespaces" class="hidden"> | ||
| 514 | <h2>Namespaces</h2> | ||
| 515 | <ul id="listNamespaces"></ul> | ||
| 516 | </div> | ||
| 517 | <div id="sectGlobalVars" class="hidden"> | ||
| 518 | <h2>Global Variables</h2> | ||
| 519 | <table> | ||
| 520 | <tbody id="listGlobalVars"></tbody> | ||
| 521 | </table> | ||
| 522 | </div> | ||
| 523 | <div id="sectFns" class="hidden"> | ||
| 524 | <h2>Functions</h2> | ||
| 525 | <table> | ||
| 526 | <tbody id="listFns"></tbody> | ||
| 527 | </table> | ||
| 528 | </div> | ||
| 529 | <div id="sectValues" class="hidden"> | ||
| 530 | <h2>Values</h2> | ||
| 531 | <table> | ||
| 532 | <tbody id="listValues"></tbody> | ||
| 533 | </table> | ||
| 534 | </div> | ||
| 535 | <div id="sectErrSets" class="hidden"> | ||
| 536 | <h2>Error Sets</h2> | ||
| 537 | <ul id="listErrSets"></ul> | ||
| 538 | </div> | ||
| 539 | <div id="fnExamples" class="hidden"> | ||
| 540 | <h2>Examples</h2> | ||
| 541 | <ul id="listFnExamples"></ul> | ||
| 542 | </div> | ||
| 543 | </section> | ||
| 544 | </div> | ||
| 545 | <div class="flex-filler"></div> | ||
| 331 | </div> | 546 | </div> |
| 332 | </div> | 547 | </div> |
| 333 | <div id="sectTypes" class="hidden"> | ||
| 334 | <h2>Types</h2> | ||
| 335 | <ul id="listTypes"> | ||
| 336 | </ul> | ||
| 337 | </div> | ||
| 338 | <div id="sectNamespaces" class="hidden"> | ||
| 339 | <h2>Namespaces</h2> | ||
| 340 | <ul id="listNamespaces"> | ||
| 341 | </ul> | ||
| 342 | </div> | ||
| 343 | <div id="sectGlobalVars" class="hidden"> | ||
| 344 | <h2>Global Variables</h2> | ||
| 345 | <table> | ||
| 346 | <tbody id="listGlobalVars"> | ||
| 347 | </tbody> | ||
| 348 | </table> | ||
| 349 | </div> | ||
| 350 | <div id="sectFns" class="hidden"> | ||
| 351 | <h2>Functions</h2> | ||
| 352 | <table> | ||
| 353 | <tbody id="listFns"> | ||
| 354 | </tbody> | ||
| 355 | </table> | ||
| 356 | </div> | ||
| 357 | <div id="sectValues" class="hidden"> | ||
| 358 | <h2>Values</h2> | ||
| 359 | <table> | ||
| 360 | <tbody id="listValues"> | ||
| 361 | </tbody> | ||
| 362 | </table> | ||
| 363 | </div> | ||
| 364 | <div id="sectErrSets" class="hidden"> | ||
| 365 | <h2>Error Sets</h2> | ||
| 366 | <ul id="listErrSets"> | ||
| 367 | </ul> | ||
| 368 | </div> | ||
| 369 | <div id="fnExamples" class="hidden"> | ||
| 370 | <h2>Examples</h2> | ||
| 371 | <ul id="listFnExamples"></ul> | ||
| 372 | </div> | ||
| 373 | </section> | ||
| 374 | <div id="helpDialog" class="hidden"> | 548 | <div id="helpDialog" class="hidden"> |
| 375 | <h1>Keyboard Shortcuts</h1> | 549 | <div class="help-modal"> |
| 376 | <dl><dt><kbd>?</kbd></dt><dd>Show this help dialog</dd></dl> | 550 | <div class="dialog"> |
| 377 | <dl><dt><kbd>Esc</kbd></dt><dd>Clear focus; close this dialog</dd></dl> | 551 | <h1>Keyboard Shortcuts</h1> |
| 378 | <dl><dt><kbd>s</kbd></dt><dd>Focus the search field</dd></dl> | 552 | <dl><dt><kbd>?</kbd></dt><dd>Show this help dialog</dd></dl> |
| 379 | <dl><dt><kbd>↑</kbd></dt><dd>Move up in search results</dd></dl> | 553 | <dl><dt><kbd>Esc</kbd></dt><dd>Clear focus; close this dialog</dd></dl> |
| 380 | <dl><dt><kbd>↓</kbd></dt><dd>Move down in search results</dd></dl> | 554 | <dl><dt><kbd>s</kbd></dt><dd>Focus the search field</dd></dl> |
| 381 | <dl><dt><kbd>⏎</kbd></dt><dd>Go to active search result</dd></dl> | 555 | <dl><dt><kbd>↑</kbd></dt><dd>Move up in search results</dd></dl> |
| 556 | <dl><dt><kbd>↓</kbd></dt><dd>Move down in search results</dd></dl> | ||
| 557 | <dl><dt><kbd>⏎</kbd></dt><dd>Go to active search result</dd></dl> | ||
| 558 | </div> | ||
| 559 | </div> | ||
| 382 | </div> | 560 | </div> |
| 383 | <script src="data.js"></script> | 561 | <script src="data.js"></script> |
| 384 | <script src="main.js"></script> | 562 | <script src="main.js"></script> |
lib/std/special/test_runner.zig+12-3| ... | @@ -15,20 +15,29 @@ pub fn main() anyerror!void { | ... | @@ -15,20 +15,29 @@ pub fn main() anyerror!void { |
| 15 | for (test_fn_list) |test_fn, i| { | 15 | for (test_fn_list) |test_fn, i| { |
| 16 | var test_node = root_node.start(test_fn.name, null); | 16 | var test_node = root_node.start(test_fn.name, null); |
| 17 | test_node.activate(); | 17 | test_node.activate(); |
| 18 | progress.refresh(); | ||
| 19 | if (progress.terminal == null) std.debug.warn("{}/{} {}...", i + 1, test_fn_list.len, test_fn.name); | ||
| 18 | if (test_fn.func()) |_| { | 20 | if (test_fn.func()) |_| { |
| 19 | ok_count += 1; | 21 | ok_count += 1; |
| 20 | test_node.end(); | 22 | test_node.end(); |
| 23 | if (progress.terminal == null) std.debug.warn("OK\n"); | ||
| 21 | } else |err| switch (err) { | 24 | } else |err| switch (err) { |
| 22 | error.SkipZigTest => { | 25 | error.SkipZigTest => { |
| 23 | skip_count += 1; | 26 | skip_count += 1; |
| 24 | test_node.end(); | 27 | test_node.end(); |
| 25 | progress.log("{}...SKIP\n", test_fn.name); | 28 | progress.log("{}...SKIP\n", test_fn.name); |
| 29 | if (progress.terminal == null) std.debug.warn("SKIP\n"); | ||
| 30 | }, | ||
| 31 | else => { | ||
| 32 | progress.log(""); | ||
| 33 | return err; | ||
| 26 | }, | 34 | }, |
| 27 | else => return err, | ||
| 28 | } | 35 | } |
| 29 | } | 36 | } |
| 30 | root_node.end(); | 37 | root_node.end(); |
| 31 | if (ok_count != test_fn_list.len) { | 38 | if (ok_count == test_fn_list.len) { |
| 32 | progress.log("{} passed; {} skipped.\n", ok_count, skip_count); | 39 | std.debug.warn("All tests passed.\n"); |
| 40 | } else { | ||
| 41 | std.debug.warn("{} passed; {} skipped.\n", ok_count, skip_count); | ||
| 33 | } | 42 | } |
| 34 | } | 43 | } |
lib/std/zig/ast.zig+1-3| ... | @@ -290,7 +290,7 @@ pub const Error = union(enum) { | ... | @@ -290,7 +290,7 @@ pub const Error = union(enum) { |
| 290 | pub const ExpectedSuffixOp = SingleTokenError("Expected pointer dereference, optional unwrap, or field access, found '{}'"); | 290 | pub const ExpectedSuffixOp = SingleTokenError("Expected pointer dereference, optional unwrap, or field access, found '{}'"); |
| 291 | 291 | ||
| 292 | pub const ExpectedParamType = SimpleError("Expected parameter type"); | 292 | pub const ExpectedParamType = SimpleError("Expected parameter type"); |
| 293 | pub const ExpectedPubItem = SimpleError("Pub must be followed by fn decl, var decl, or container member"); | 293 | pub const ExpectedPubItem = SimpleError("Expected function or variable declaration after pub"); |
| 294 | pub const UnattachedDocComment = SimpleError("Unattached documentation comment"); | 294 | pub const UnattachedDocComment = SimpleError("Unattached documentation comment"); |
| 295 | pub const ExtraAlignQualifier = SimpleError("Extra align qualifier"); | 295 | pub const ExtraAlignQualifier = SimpleError("Extra align qualifier"); |
| 296 | pub const ExtraConstQualifier = SimpleError("Extra const qualifier"); | 296 | pub const ExtraConstQualifier = SimpleError("Extra const qualifier"); |
| ... | @@ -757,7 +757,6 @@ pub const Node = struct { | ... | @@ -757,7 +757,6 @@ pub const Node = struct { |
| 757 | pub const ContainerField = struct { | 757 | pub const ContainerField = struct { |
| 758 | base: Node, | 758 | base: Node, |
| 759 | doc_comments: ?*DocComment, | 759 | doc_comments: ?*DocComment, |
| 760 | visib_token: ?TokenIndex, | ||
| 761 | name_token: TokenIndex, | 760 | name_token: TokenIndex, |
| 762 | type_expr: ?*Node, | 761 | type_expr: ?*Node, |
| 763 | value_expr: ?*Node, | 762 | value_expr: ?*Node, |
| ... | @@ -780,7 +779,6 @@ pub const Node = struct { | ... | @@ -780,7 +779,6 @@ pub const Node = struct { |
| 780 | } | 779 | } |
| 781 | 780 | ||
| 782 | pub fn firstToken(self: *const ContainerField) TokenIndex { | 781 | pub fn firstToken(self: *const ContainerField) TokenIndex { |
| 783 | if (self.visib_token) |visib_token| return visib_token; | ||
| 784 | return self.name_token; | 782 | return self.name_token; |
| 785 | } | 783 | } |
| 786 | 784 |
lib/std/zig/parse.zig+7-9| ... | @@ -138,9 +138,15 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No | ... | @@ -138,9 +138,15 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No |
| 138 | continue; | 138 | continue; |
| 139 | } | 139 | } |
| 140 | 140 | ||
| 141 | if (visib_token != null) { | ||
| 142 | try tree.errors.push(AstError{ | ||
| 143 | .ExpectedPubItem = AstError.ExpectedPubItem{ .token = it.index }, | ||
| 144 | }); | ||
| 145 | return error.ParseError; | ||
| 146 | } | ||
| 147 | |||
| 141 | if (try parseContainerField(arena, it, tree)) |node| { | 148 | if (try parseContainerField(arena, it, tree)) |node| { |
| 142 | const field = node.cast(Node.ContainerField).?; | 149 | const field = node.cast(Node.ContainerField).?; |
| 143 | field.visib_token = visib_token; | ||
| 144 | field.doc_comments = doc_comments; | 150 | field.doc_comments = doc_comments; |
| 145 | try list.push(node); | 151 | try list.push(node); |
| 146 | const comma = eatToken(it, .Comma) orelse break; | 152 | const comma = eatToken(it, .Comma) orelse break; |
| ... | @@ -149,13 +155,6 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No | ... | @@ -149,13 +155,6 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No |
| 149 | continue; | 155 | continue; |
| 150 | } | 156 | } |
| 151 | 157 | ||
| 152 | // Dangling pub | ||
| 153 | if (visib_token != null) { | ||
| 154 | try tree.errors.push(AstError{ | ||
| 155 | .ExpectedPubItem = AstError.ExpectedPubItem{ .token = it.index }, | ||
| 156 | }); | ||
| 157 | } | ||
| 158 | |||
| 159 | break; | 158 | break; |
| 160 | } | 159 | } |
| 161 | 160 | ||
| ... | @@ -407,7 +406,6 @@ fn parseContainerField(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No | ... | @@ -407,7 +406,6 @@ fn parseContainerField(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No |
| 407 | node.* = Node.ContainerField{ | 406 | node.* = Node.ContainerField{ |
| 408 | .base = Node{ .id = .ContainerField }, | 407 | .base = Node{ .id = .ContainerField }, |
| 409 | .doc_comments = null, | 408 | .doc_comments = null, |
| 410 | .visib_token = null, | ||
| 411 | .name_token = name_token, | 409 | .name_token = name_token, |
| 412 | .type_expr = type_expr, | 410 | .type_expr = type_expr, |
| 413 | .value_expr = value_expr, | 411 | .value_expr = value_expr, |
lib/std/zig/parser_test.zig+3-3| ... | @@ -1766,7 +1766,7 @@ test "zig fmt: struct declaration" { | ... | @@ -1766,7 +1766,7 @@ test "zig fmt: struct declaration" { |
| 1766 | \\const S = struct { | 1766 | \\const S = struct { |
| 1767 | \\ const Self = @This(); | 1767 | \\ const Self = @This(); |
| 1768 | \\ f1: u8, | 1768 | \\ f1: u8, |
| 1769 | \\ pub f3: u8, | 1769 | \\ f3: u8, |
| 1770 | \\ | 1770 | \\ |
| 1771 | \\ fn method(self: *Self) Self { | 1771 | \\ fn method(self: *Self) Self { |
| 1772 | \\ return self.*; | 1772 | \\ return self.*; |
| ... | @@ -1777,14 +1777,14 @@ test "zig fmt: struct declaration" { | ... | @@ -1777,14 +1777,14 @@ test "zig fmt: struct declaration" { |
| 1777 | \\ | 1777 | \\ |
| 1778 | \\const Ps = packed struct { | 1778 | \\const Ps = packed struct { |
| 1779 | \\ a: u8, | 1779 | \\ a: u8, |
| 1780 | \\ pub b: u8, | 1780 | \\ b: u8, |
| 1781 | \\ | 1781 | \\ |
| 1782 | \\ c: u8, | 1782 | \\ c: u8, |
| 1783 | \\}; | 1783 | \\}; |
| 1784 | \\ | 1784 | \\ |
| 1785 | \\const Es = extern struct { | 1785 | \\const Es = extern struct { |
| 1786 | \\ a: u8, | 1786 | \\ a: u8, |
| 1787 | \\ pub b: u8, | 1787 | \\ b: u8, |
| 1788 | \\ | 1788 | \\ |
| 1789 | \\ c: u8, | 1789 | \\ c: u8, |
| 1790 | \\}; | 1790 | \\}; |
lib/std/zig/render.zig+2-6| ... | @@ -254,10 +254,6 @@ fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, i | ... | @@ -254,10 +254,6 @@ fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, i |
| 254 | 254 | ||
| 255 | try renderDocComments(tree, stream, field, indent, start_col); | 255 | try renderDocComments(tree, stream, field, indent, start_col); |
| 256 | 256 | ||
| 257 | if (field.visib_token) |visib_token| { | ||
| 258 | try renderToken(tree, stream, visib_token, indent, start_col, Space.Space); // pub | ||
| 259 | } | ||
| 260 | |||
| 261 | if (field.type_expr == null and field.value_expr == null) { | 257 | if (field.type_expr == null and field.value_expr == null) { |
| 262 | return renderToken(tree, stream, field.name_token, indent, start_col, Space.Comma); // name, | 258 | return renderToken(tree, stream, field.name_token, indent, start_col, Space.Comma); // name, |
| 263 | } else if (field.type_expr != null and field.value_expr == null) { | 259 | } else if (field.type_expr != null and field.value_expr == null) { |
| ... | @@ -2206,8 +2202,8 @@ const FindByteOutStream = struct { | ... | @@ -2206,8 +2202,8 @@ const FindByteOutStream = struct { |
| 2206 | pub const Error = error{}; | 2202 | pub const Error = error{}; |
| 2207 | pub const Stream = std.io.OutStream(Error); | 2203 | pub const Stream = std.io.OutStream(Error); |
| 2208 | 2204 | ||
| 2209 | pub stream: Stream, | 2205 | stream: Stream, |
| 2210 | pub byte_found: bool, | 2206 | byte_found: bool, |
| 2211 | byte: u8, | 2207 | byte: u8, |
| 2212 | 2208 | ||
| 2213 | pub fn init(byte: u8) Self { | 2209 | pub fn init(byte: u8) Self { |
src-self-hosted/main.zig+1-1| ... | @@ -747,7 +747,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro | ... | @@ -747,7 +747,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro |
| 747 | )) catch |err| switch (err) { | 747 | )) catch |err| switch (err) { |
| 748 | error.IsDir, error.AccessDenied => { | 748 | error.IsDir, error.AccessDenied => { |
| 749 | // TODO make event based (and dir.next()) | 749 | // TODO make event based (and dir.next()) |
| 750 | var dir = try fs.Dir.open(fmt.loop.allocator, file_path); | 750 | var dir = try fs.Dir.open(file_path); |
| 751 | defer dir.close(); | 751 | defer dir.close(); |
| 752 | 752 | ||
| 753 | var group = event.Group(FmtError!void).init(fmt.loop); | 753 | var group = event.Group(FmtError!void).init(fmt.loop); |
src-self-hosted/stage1.zig+5-3| ... | @@ -283,11 +283,13 @@ fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void | ... | @@ -283,11 +283,13 @@ fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void |
| 283 | const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) { | 283 | const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) { |
| 284 | error.IsDir, error.AccessDenied => { | 284 | error.IsDir, error.AccessDenied => { |
| 285 | // TODO make event based (and dir.next()) | 285 | // TODO make event based (and dir.next()) |
| 286 | var dir = try fs.Dir.open(fmt.allocator, file_path); | 286 | var dir = try fs.Dir.open(file_path); |
| 287 | defer dir.close(); | 287 | defer dir.close(); |
| 288 | 288 | ||
| 289 | while (try dir.next()) |entry| { | 289 | var dir_it = dir.iterate(); |
| 290 | if (entry.kind == fs.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) { | 290 | |
| 291 | while (try dir_it.next()) |entry| { | ||
| 292 | if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) { | ||
| 291 | const full_path = try fs.path.join(fmt.allocator, [_][]const u8{ file_path, entry.name }); | 293 | const full_path = try fs.path.join(fmt.allocator, [_][]const u8{ file_path, entry.name }); |
| 292 | try fmtPath(fmt, full_path, check_mode); | 294 | try fmtPath(fmt, full_path, check_mode); |
| 293 | } | 295 | } |
src-self-hosted/test.zig+2-2| ... | @@ -56,11 +56,11 @@ pub const TestContext = struct { | ... | @@ -56,11 +56,11 @@ pub const TestContext = struct { |
| 56 | errdefer allocator.free(self.zig_lib_dir); | 56 | errdefer allocator.free(self.zig_lib_dir); |
| 57 | 57 | ||
| 58 | try std.fs.makePath(allocator, tmp_dir_name); | 58 | try std.fs.makePath(allocator, tmp_dir_name); |
| 59 | errdefer std.fs.deleteTree(allocator, tmp_dir_name) catch {}; | 59 | errdefer std.fs.deleteTree(tmp_dir_name) catch {}; |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | fn deinit(self: *TestContext) void { | 62 | fn deinit(self: *TestContext) void { |
| 63 | std.fs.deleteTree(allocator, tmp_dir_name) catch {}; | 63 | std.fs.deleteTree(tmp_dir_name) catch {}; |
| 64 | allocator.free(self.zig_lib_dir); | 64 | allocator.free(self.zig_lib_dir); |
| 65 | self.zig_compiler.deinit(); | 65 | self.zig_compiler.deinit(); |
| 66 | self.loop.deinit(); | 66 | self.loop.deinit(); |
src/all_types.hpp+3-5| ... | @@ -990,8 +990,6 @@ struct AstNodeStructField { | ... | @@ -990,8 +990,6 @@ struct AstNodeStructField { |
| 990 | // populated if the "align(A)" is present | 990 | // populated if the "align(A)" is present |
| 991 | AstNode *align_expr; | 991 | AstNode *align_expr; |
| 992 | Buf doc_comments; | 992 | Buf doc_comments; |
| 993 | |||
| 994 | VisibMod visib_mod; | ||
| 995 | }; | 993 | }; |
| 996 | 994 | ||
| 997 | struct AstNodeStringLiteral { | 995 | struct AstNodeStringLiteral { |
| ... | @@ -2569,12 +2567,12 @@ enum IrInstructionId { | ... | @@ -2569,12 +2567,12 @@ enum IrInstructionId { |
| 2569 | struct IrInstruction { | 2567 | struct IrInstruction { |
| 2570 | Scope *scope; | 2568 | Scope *scope; |
| 2571 | AstNode *source_node; | 2569 | AstNode *source_node; |
| 2572 | ConstExprValue value; | ||
| 2573 | size_t debug_id; | ||
| 2574 | LLVMValueRef llvm_value; | 2570 | LLVMValueRef llvm_value; |
| 2571 | ConstExprValue value; | ||
| 2572 | uint32_t debug_id; | ||
| 2575 | // if ref_count is zero and the instruction has no side effects, | 2573 | // if ref_count is zero and the instruction has no side effects, |
| 2576 | // the instruction can be omitted in codegen | 2574 | // the instruction can be omitted in codegen |
| 2577 | size_t ref_count; | 2575 | uint32_t ref_count; |
| 2578 | // When analyzing IR, instructions that point to this instruction in the "old ir" | 2576 | // When analyzing IR, instructions that point to this instruction in the "old ir" |
| 2579 | // can find the instruction that corresponds to this value in the "new ir" | 2577 | // can find the instruction that corresponds to this value in the "new ir" |
| 2580 | // with this child field. | 2578 | // with this child field. |
src/analyze.cpp+2-2| ... | @@ -5783,8 +5783,8 @@ ConstExprValue *create_const_arg_tuple(CodeGen *g, size_t arg_index_start, size_ | ... | @@ -5783,8 +5783,8 @@ ConstExprValue *create_const_arg_tuple(CodeGen *g, size_t arg_index_start, size_ |
| 5783 | 5783 | ||
| 5784 | 5784 | ||
| 5785 | ConstExprValue *create_const_vals(size_t count) { | 5785 | ConstExprValue *create_const_vals(size_t count) { |
| 5786 | ConstGlobalRefs *global_refs = allocate<ConstGlobalRefs>(count); | 5786 | ConstGlobalRefs *global_refs = allocate<ConstGlobalRefs>(count, "ConstGlobalRefs"); |
| 5787 | ConstExprValue *vals = allocate<ConstExprValue>(count); | 5787 | ConstExprValue *vals = allocate<ConstExprValue>(count, "ConstExprValue"); |
| 5788 | for (size_t i = 0; i < count; i += 1) { | 5788 | for (size_t i = 0; i < count; i += 1) { |
| 5789 | vals[i].global_refs = &global_refs[i]; | 5789 | vals[i].global_refs = &global_refs[i]; |
| 5790 | } | 5790 | } |
src/codegen.cpp+7-2| ... | @@ -6355,12 +6355,17 @@ static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *un | ... | @@ -6355,12 +6355,17 @@ static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *un |
| 6355 | ConstParent *parent = &union_const_val->parent; | 6355 | ConstParent *parent = &union_const_val->parent; |
| 6356 | LLVMValueRef base_ptr = gen_parent_ptr(g, union_const_val, parent); | 6356 | LLVMValueRef base_ptr = gen_parent_ptr(g, union_const_val, parent); |
| 6357 | 6357 | ||
| 6358 | // Slot in the structure where the payload is stored, if equal to SIZE_MAX | ||
| 6359 | // the union has no tag and a single field and is collapsed into the field | ||
| 6360 | // itself | ||
| 6361 | size_t union_payload_index = union_const_val->type->data.unionation.gen_union_index; | ||
| 6362 | |||
| 6358 | ZigType *u32 = g->builtin_types.entry_u32; | 6363 | ZigType *u32 = g->builtin_types.entry_u32; |
| 6359 | LLVMValueRef indices[] = { | 6364 | LLVMValueRef indices[] = { |
| 6360 | LLVMConstNull(get_llvm_type(g, u32)), | 6365 | LLVMConstNull(get_llvm_type(g, u32)), |
| 6361 | LLVMConstInt(get_llvm_type(g, u32), 0, false), // TODO test const union with more aligned tag type than payload | 6366 | LLVMConstInt(get_llvm_type(g, u32), union_payload_index, false), |
| 6362 | }; | 6367 | }; |
| 6363 | return LLVMConstInBoundsGEP(base_ptr, indices, 2); | 6368 | return LLVMConstInBoundsGEP(base_ptr, indices, (union_payload_index != SIZE_MAX) ? 2 : 1); |
| 6364 | } | 6369 | } |
| 6365 | 6370 | ||
| 6366 | static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, ConstExprValue *const_val) { | 6371 | static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, ConstExprValue *const_val) { |
src/config.h.in+2-3| ... | @@ -13,9 +13,6 @@ | ... | @@ -13,9 +13,6 @@ |
| 13 | #define ZIG_VERSION_PATCH @ZIG_VERSION_PATCH@ | 13 | #define ZIG_VERSION_PATCH @ZIG_VERSION_PATCH@ |
| 14 | #define ZIG_VERSION_STRING "@ZIG_VERSION@" | 14 | #define ZIG_VERSION_STRING "@ZIG_VERSION@" |
| 15 | 15 | ||
| 16 | // Only used for running tests before installing. | ||
| 17 | #define ZIG_TEST_DIR "@CMAKE_SOURCE_DIR@/test" | ||
| 18 | |||
| 19 | // Used for communicating build information to self hosted build. | 16 | // Used for communicating build information to self hosted build. |
| 20 | #define ZIG_CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@" | 17 | #define ZIG_CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@" |
| 21 | #define ZIG_CXX_COMPILER "@CMAKE_CXX_COMPILER@" | 18 | #define ZIG_CXX_COMPILER "@CMAKE_CXX_COMPILER@" |
| ... | @@ -24,4 +21,6 @@ | ... | @@ -24,4 +21,6 @@ |
| 24 | #define ZIG_LLVM_CONFIG_EXE "@LLVM_CONFIG_EXE@" | 21 | #define ZIG_LLVM_CONFIG_EXE "@LLVM_CONFIG_EXE@" |
| 25 | #define ZIG_DIA_GUIDS_LIB "@ZIG_DIA_GUIDS_LIB_ESCAPED@" | 22 | #define ZIG_DIA_GUIDS_LIB "@ZIG_DIA_GUIDS_LIB_ESCAPED@" |
| 26 | 23 | ||
| 24 | #cmakedefine ZIG_ENABLE_MEM_PROFILE | ||
| 25 | |||
| 27 | #endif | 26 | #endif |
src/dump_analysis.cpp+1-18| ... | @@ -240,23 +240,6 @@ static void jw_string(JsonWriter *jw, const char *s) { | ... | @@ -240,23 +240,6 @@ static void jw_string(JsonWriter *jw, const char *s) { |
| 240 | 240 | ||
| 241 | static void tree_print(FILE *f, ZigType *ty, size_t indent); | 241 | static void tree_print(FILE *f, ZigType *ty, size_t indent); |
| 242 | 242 | ||
| 243 | static void pretty_print_bytes(FILE *f, double n) { | ||
| 244 | if (n > 1024.0 * 1024.0 * 1024.0) { | ||
| 245 | fprintf(f, "%.02f GiB", n / 1024.0 / 1024.0 / 1024.0); | ||
| 246 | return; | ||
| 247 | } | ||
| 248 | if (n > 1024.0 * 1024.0) { | ||
| 249 | fprintf(f, "%.02f MiB", n / 1024.0 / 1024.0); | ||
| 250 | return; | ||
| 251 | } | ||
| 252 | if (n > 1024.0) { | ||
| 253 | fprintf(f, "%.02f KiB", n / 1024.0); | ||
| 254 | return; | ||
| 255 | } | ||
| 256 | fprintf(f, "%.02f bytes", n ); | ||
| 257 | return; | ||
| 258 | } | ||
| 259 | |||
| 260 | static int compare_type_abi_sizes_desc(const void *a, const void *b) { | 243 | static int compare_type_abi_sizes_desc(const void *a, const void *b) { |
| 261 | uint64_t size_a = (*(ZigType * const*)(a))->abi_size; | 244 | uint64_t size_a = (*(ZigType * const*)(a))->abi_size; |
| 262 | uint64_t size_b = (*(ZigType * const*)(b))->abi_size; | 245 | uint64_t size_b = (*(ZigType * const*)(b))->abi_size; |
| ... | @@ -322,7 +305,7 @@ static void tree_print(FILE *f, ZigType *ty, size_t indent) { | ... | @@ -322,7 +305,7 @@ static void tree_print(FILE *f, ZigType *ty, size_t indent) { |
| 322 | 305 | ||
| 323 | start_peer(f, indent); | 306 | start_peer(f, indent); |
| 324 | fprintf(f, "\"sizef\": \""); | 307 | fprintf(f, "\"sizef\": \""); |
| 325 | pretty_print_bytes(f, ty->abi_size); | 308 | zig_pretty_print_bytes(f, ty->abi_size); |
| 326 | fprintf(f, "\""); | 309 | fprintf(f, "\""); |
| 327 | 310 | ||
| 328 | start_peer(f, indent); | 311 | start_peer(f, indent); |
src/ir.cpp+58-30| ... | @@ -413,7 +413,7 @@ ZigType *ir_analyze_type_expr(IrAnalyze *ira, Scope *scope, AstNode *node) { | ... | @@ -413,7 +413,7 @@ ZigType *ir_analyze_type_expr(IrAnalyze *ira, Scope *scope, AstNode *node) { |
| 413 | } | 413 | } |
| 414 | 414 | ||
| 415 | static IrBasicBlock *ir_create_basic_block(IrBuilder *irb, Scope *scope, const char *name_hint) { | 415 | static IrBasicBlock *ir_create_basic_block(IrBuilder *irb, Scope *scope, const char *name_hint) { |
| 416 | IrBasicBlock *result = allocate<IrBasicBlock>(1); | 416 | IrBasicBlock *result = allocate<IrBasicBlock>(1, "IrBasicBlock"); |
| 417 | result->scope = scope; | 417 | result->scope = scope; |
| 418 | result->name_hint = name_hint; | 418 | result->name_hint = name_hint; |
| 419 | result->debug_id = exec_next_debug_id(irb->exec); | 419 | result->debug_id = exec_next_debug_id(irb->exec); |
| ... | @@ -1085,13 +1085,18 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSpillEnd *) { | ... | @@ -1085,13 +1085,18 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSpillEnd *) { |
| 1085 | 1085 | ||
| 1086 | template<typename T> | 1086 | template<typename T> |
| 1087 | static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { | 1087 | static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { |
| 1088 | T *special_instruction = allocate<T>(1); | 1088 | const char *name = nullptr; |
| 1089 | #ifdef ZIG_ENABLE_MEM_PROFILE | ||
| 1090 | T *dummy = nullptr; | ||
| 1091 | name = ir_instruction_type_str(ir_instruction_id(dummy)); | ||
| 1092 | #endif | ||
| 1093 | T *special_instruction = allocate<T>(1, name); | ||
| 1089 | special_instruction->base.id = ir_instruction_id(special_instruction); | 1094 | special_instruction->base.id = ir_instruction_id(special_instruction); |
| 1090 | special_instruction->base.scope = scope; | 1095 | special_instruction->base.scope = scope; |
| 1091 | special_instruction->base.source_node = source_node; | 1096 | special_instruction->base.source_node = source_node; |
| 1092 | special_instruction->base.debug_id = exec_next_debug_id(irb->exec); | 1097 | special_instruction->base.debug_id = exec_next_debug_id(irb->exec); |
| 1093 | special_instruction->base.owner_bb = irb->current_basic_block; | 1098 | special_instruction->base.owner_bb = irb->current_basic_block; |
| 1094 | special_instruction->base.value.global_refs = allocate<ConstGlobalRefs>(1); | 1099 | special_instruction->base.value.global_refs = allocate<ConstGlobalRefs>(1, "ConstGlobalRefs"); |
| 1095 | return special_instruction; | 1100 | return special_instruction; |
| 1096 | } | 1101 | } |
| 1097 | 1102 | ||
| ... | @@ -3569,7 +3574,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, | ... | @@ -3569,7 +3574,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, |
| 3569 | switch (node->data.return_expr.kind) { | 3574 | switch (node->data.return_expr.kind) { |
| 3570 | case ReturnKindUnconditional: | 3575 | case ReturnKindUnconditional: |
| 3571 | { | 3576 | { |
| 3572 | ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1); | 3577 | ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1, "ResultLocReturn"); |
| 3573 | result_loc_ret->base.id = ResultLocIdReturn; | 3578 | result_loc_ret->base.id = ResultLocIdReturn; |
| 3574 | ir_build_reset_result(irb, scope, node, &result_loc_ret->base); | 3579 | ir_build_reset_result(irb, scope, node, &result_loc_ret->base); |
| 3575 | 3580 | ||
| ... | @@ -3664,7 +3669,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, | ... | @@ -3664,7 +3669,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, |
| 3664 | ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, err_val, nullptr)); | 3669 | ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, err_val, nullptr)); |
| 3665 | IrInstructionSpillBegin *spill_begin = ir_build_spill_begin(irb, scope, node, err_val, | 3670 | IrInstructionSpillBegin *spill_begin = ir_build_spill_begin(irb, scope, node, err_val, |
| 3666 | SpillIdRetErrCode); | 3671 | SpillIdRetErrCode); |
| 3667 | ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1); | 3672 | ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1, "ResultLocReturn"); |
| 3668 | result_loc_ret->base.id = ResultLocIdReturn; | 3673 | result_loc_ret->base.id = ResultLocIdReturn; |
| 3669 | ir_build_reset_result(irb, scope, node, &result_loc_ret->base); | 3674 | ir_build_reset_result(irb, scope, node, &result_loc_ret->base); |
| 3670 | ir_build_end_expr(irb, scope, node, err_val, &result_loc_ret->base); | 3675 | ir_build_end_expr(irb, scope, node, err_val, &result_loc_ret->base); |
| ... | @@ -3692,7 +3697,7 @@ static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_s | ... | @@ -3692,7 +3697,7 @@ static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_s |
| 3692 | Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime, | 3697 | Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime, |
| 3693 | bool skip_name_check) | 3698 | bool skip_name_check) |
| 3694 | { | 3699 | { |
| 3695 | ZigVar *variable_entry = allocate<ZigVar>(1); | 3700 | ZigVar *variable_entry = allocate<ZigVar>(1, "ZigVar"); |
| 3696 | variable_entry->parent_scope = parent_scope; | 3701 | variable_entry->parent_scope = parent_scope; |
| 3697 | variable_entry->shadowable = is_shadowable; | 3702 | variable_entry->shadowable = is_shadowable; |
| 3698 | variable_entry->mem_slot_index = SIZE_MAX; | 3703 | variable_entry->mem_slot_index = SIZE_MAX; |
| ... | @@ -3767,7 +3772,7 @@ static ZigVar *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *n | ... | @@ -3767,7 +3772,7 @@ static ZigVar *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *n |
| 3767 | } | 3772 | } |
| 3768 | 3773 | ||
| 3769 | static ResultLocPeer *create_peer_result(ResultLocPeerParent *peer_parent) { | 3774 | static ResultLocPeer *create_peer_result(ResultLocPeerParent *peer_parent) { |
| 3770 | ResultLocPeer *result = allocate<ResultLocPeer>(1); | 3775 | ResultLocPeer *result = allocate<ResultLocPeer>(1, "ResultLocPeer"); |
| 3771 | result->base.id = ResultLocIdPeer; | 3776 | result->base.id = ResultLocIdPeer; |
| 3772 | result->base.source_instruction = peer_parent->base.source_instruction; | 3777 | result->base.source_instruction = peer_parent->base.source_instruction; |
| 3773 | result->parent = peer_parent; | 3778 | result->parent = peer_parent; |
| ... | @@ -3806,7 +3811,7 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode | ... | @@ -3806,7 +3811,7 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode |
| 3806 | scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node, | 3811 | scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node, |
| 3807 | ir_should_inline(irb->exec, parent_scope)); | 3812 | ir_should_inline(irb->exec, parent_scope)); |
| 3808 | 3813 | ||
| 3809 | scope_block->peer_parent = allocate<ResultLocPeerParent>(1); | 3814 | scope_block->peer_parent = allocate<ResultLocPeerParent>(1, "ResultLocPeerParent"); |
| 3810 | scope_block->peer_parent->base.id = ResultLocIdPeerParent; | 3815 | scope_block->peer_parent->base.id = ResultLocIdPeerParent; |
| 3811 | scope_block->peer_parent->base.source_instruction = scope_block->is_comptime; | 3816 | scope_block->peer_parent->base.source_instruction = scope_block->is_comptime; |
| 3812 | scope_block->peer_parent->end_bb = scope_block->end_block; | 3817 | scope_block->peer_parent->end_bb = scope_block->end_block; |
| ... | @@ -3933,7 +3938,7 @@ static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node) | ... | @@ -3933,7 +3938,7 @@ static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node) |
| 3933 | if (lvalue == irb->codegen->invalid_instruction) | 3938 | if (lvalue == irb->codegen->invalid_instruction) |
| 3934 | return irb->codegen->invalid_instruction; | 3939 | return irb->codegen->invalid_instruction; |
| 3935 | 3940 | ||
| 3936 | ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1); | 3941 | ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1, "ResultLocInstruction"); |
| 3937 | result_loc_inst->base.id = ResultLocIdInstruction; | 3942 | result_loc_inst->base.id = ResultLocIdInstruction; |
| 3938 | result_loc_inst->base.source_instruction = lvalue; | 3943 | result_loc_inst->base.source_instruction = lvalue; |
| 3939 | ir_ref_instruction(lvalue, irb->current_basic_block); | 3944 | ir_ref_instruction(lvalue, irb->current_basic_block); |
| ... | @@ -4005,10 +4010,10 @@ static IrInstruction *ir_gen_bool_or(IrBuilder *irb, Scope *scope, AstNode *node | ... | @@ -4005,10 +4010,10 @@ static IrInstruction *ir_gen_bool_or(IrBuilder *irb, Scope *scope, AstNode *node |
| 4005 | 4010 | ||
| 4006 | ir_set_cursor_at_end_and_append_block(irb, true_block); | 4011 | ir_set_cursor_at_end_and_append_block(irb, true_block); |
| 4007 | 4012 | ||
| 4008 | IrInstruction **incoming_values = allocate<IrInstruction *>(2); | 4013 | IrInstruction **incoming_values = allocate<IrInstruction *>(2, "IrInstruction *"); |
| 4009 | incoming_values[0] = val1; | 4014 | incoming_values[0] = val1; |
| 4010 | incoming_values[1] = val2; | 4015 | incoming_values[1] = val2; |
| 4011 | IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2); | 4016 | IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2, "IrBasicBlock *"); |
| 4012 | incoming_blocks[0] = post_val1_block; | 4017 | incoming_blocks[0] = post_val1_block; |
| 4013 | incoming_blocks[1] = post_val2_block; | 4018 | incoming_blocks[1] = post_val2_block; |
| 4014 | 4019 | ||
| ... | @@ -8017,7 +8022,8 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A | ... | @@ -8017,7 +8022,8 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A |
| 8017 | err_set_type->abi_size = irb->codegen->builtin_types.entry_global_error_set->abi_size; | 8022 | err_set_type->abi_size = irb->codegen->builtin_types.entry_global_error_set->abi_size; |
| 8018 | err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(err_count); | 8023 | err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(err_count); |
| 8019 | 8024 | ||
| 8020 | ErrorTableEntry **errors = allocate<ErrorTableEntry *>(irb->codegen->errors_by_index.length + err_count); | 8025 | size_t errors_count = irb->codegen->errors_by_index.length + err_count; |
| 8026 | ErrorTableEntry **errors = allocate<ErrorTableEntry *>(errors_count, "ErrorTableEntry *"); | ||
| 8021 | 8027 | ||
| 8022 | for (uint32_t i = 0; i < err_count; i += 1) { | 8028 | for (uint32_t i = 0; i < err_count; i += 1) { |
| 8023 | AstNode *field_node = node->data.err_set_decl.decls.at(i); | 8029 | AstNode *field_node = node->data.err_set_decl.decls.at(i); |
| ... | @@ -8048,7 +8054,7 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A | ... | @@ -8048,7 +8054,7 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A |
| 8048 | } | 8054 | } |
| 8049 | errors[err->value] = err; | 8055 | errors[err->value] = err; |
| 8050 | } | 8056 | } |
| 8051 | free(errors); | 8057 | deallocate(errors, errors_count, "ErrorTableEntry *"); |
| 8052 | return ir_build_const_type(irb, parent_scope, node, err_set_type); | 8058 | return ir_build_const_type(irb, parent_scope, node, err_set_type); |
| 8053 | } | 8059 | } |
| 8054 | 8060 | ||
| ... | @@ -9574,7 +9580,8 @@ static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigTyp | ... | @@ -9574,7 +9580,8 @@ static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigTyp |
| 9574 | if (type_is_global_error_set(set2)) { | 9580 | if (type_is_global_error_set(set2)) { |
| 9575 | return set1; | 9581 | return set1; |
| 9576 | } | 9582 | } |
| 9577 | ErrorTableEntry **errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length); | 9583 | size_t errors_count = ira->codegen->errors_by_index.length; |
| 9584 | ErrorTableEntry **errors = allocate<ErrorTableEntry *>(errors_count, "ErrorTableEntry *"); | ||
| 9578 | populate_error_set_table(errors, set1); | 9585 | populate_error_set_table(errors, set1); |
| 9579 | ZigList<ErrorTableEntry *> intersection_list = {}; | 9586 | ZigList<ErrorTableEntry *> intersection_list = {}; |
| 9580 | 9587 | ||
| ... | @@ -9595,7 +9602,7 @@ static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigTyp | ... | @@ -9595,7 +9602,7 @@ static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigTyp |
| 9595 | buf_appendf(&err_set_type->name, "%s%s", comma, buf_ptr(&existing_entry_with_docs->name)); | 9602 | buf_appendf(&err_set_type->name, "%s%s", comma, buf_ptr(&existing_entry_with_docs->name)); |
| 9596 | } | 9603 | } |
| 9597 | } | 9604 | } |
| 9598 | free(errors); | 9605 | deallocate(errors, errors_count, "ErrorTableEntry *"); |
| 9599 | 9606 | ||
| 9600 | err_set_type->data.error_set.err_count = intersection_list.length; | 9607 | err_set_type->data.error_set.err_count = intersection_list.length; |
| 9601 | err_set_type->data.error_set.errors = intersection_list.items; | 9608 | err_set_type->data.error_set.errors = intersection_list.items; |
| ... | @@ -9792,7 +9799,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted | ... | @@ -9792,7 +9799,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted |
| 9792 | return result; | 9799 | return result; |
| 9793 | } | 9800 | } |
| 9794 | 9801 | ||
| 9795 | ErrorTableEntry **errors = allocate<ErrorTableEntry *>(g->errors_by_index.length); | 9802 | size_t errors_count = g->errors_by_index.length; |
| 9803 | ErrorTableEntry **errors = allocate<ErrorTableEntry *>(errors_count, "ErrorTableEntry *"); | ||
| 9796 | for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) { | 9804 | for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) { |
| 9797 | ErrorTableEntry *error_entry = container_set->data.error_set.errors[i]; | 9805 | ErrorTableEntry *error_entry = container_set->data.error_set.errors[i]; |
| 9798 | assert(errors[error_entry->value] == nullptr); | 9806 | assert(errors[error_entry->value] == nullptr); |
| ... | @@ -9809,7 +9817,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted | ... | @@ -9809,7 +9817,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted |
| 9809 | result.data.error_set_mismatch->missing_errors.append(contained_error_entry); | 9817 | result.data.error_set_mismatch->missing_errors.append(contained_error_entry); |
| 9810 | } | 9818 | } |
| 9811 | } | 9819 | } |
| 9812 | free(errors); | 9820 | deallocate(errors, errors_count, "ErrorTableEntry *"); |
| 9813 | return result; | 9821 | return result; |
| 9814 | } | 9822 | } |
| 9815 | 9823 | ||
| ... | @@ -10112,6 +10120,18 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT | ... | @@ -10112,6 +10120,18 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT |
| 10112 | } else { | 10120 | } else { |
| 10113 | err_set_type = cur_type; | 10121 | err_set_type = cur_type; |
| 10114 | } | 10122 | } |
| 10123 | |||
| 10124 | if (!resolve_inferred_error_set(ira->codegen, err_set_type, cur_inst->source_node)) { | ||
| 10125 | return ira->codegen->builtin_types.entry_invalid; | ||
| 10126 | } | ||
| 10127 | |||
| 10128 | if (type_is_global_error_set(err_set_type)) { | ||
| 10129 | err_set_type = ira->codegen->builtin_types.entry_global_error_set; | ||
| 10130 | continue; | ||
| 10131 | } | ||
| 10132 | |||
| 10133 | update_errors_helper(ira->codegen, &errors, &errors_count); | ||
| 10134 | |||
| 10115 | for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { | 10135 | for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { |
| 10116 | ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; | 10136 | ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; |
| 10117 | assert(errors[error_entry->value] == nullptr); | 10137 | assert(errors[error_entry->value] == nullptr); |
| ... | @@ -10814,7 +10834,8 @@ static IrInstruction *ira_suspend(IrAnalyze *ira, IrInstruction *old_instruction | ... | @@ -10814,7 +10834,8 @@ static IrInstruction *ira_suspend(IrAnalyze *ira, IrInstruction *old_instruction |
| 10814 | IrSuspendPosition *suspend_pos) | 10834 | IrSuspendPosition *suspend_pos) |
| 10815 | { | 10835 | { |
| 10816 | if (ira->codegen->verbose_ir) { | 10836 | if (ira->codegen->verbose_ir) { |
| 10817 | fprintf(stderr, "suspend %s_%zu %s_%zu #%zu (%zu,%zu)\n", ira->old_irb.current_basic_block->name_hint, | 10837 | fprintf(stderr, "suspend %s_%zu %s_%zu #%" PRIu32 " (%zu,%zu)\n", |
| 10838 | ira->old_irb.current_basic_block->name_hint, | ||
| 10818 | ira->old_irb.current_basic_block->debug_id, | 10839 | ira->old_irb.current_basic_block->debug_id, |
| 10819 | ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->name_hint, | 10840 | ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->name_hint, |
| 10820 | ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->debug_id, | 10841 | ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->debug_id, |
| ... | @@ -10852,7 +10873,7 @@ static IrInstruction *ira_resume(IrAnalyze *ira) { | ... | @@ -10852,7 +10873,7 @@ static IrInstruction *ira_resume(IrAnalyze *ira) { |
| 10852 | ira->instruction_index = pos.instruction_index; | 10873 | ira->instruction_index = pos.instruction_index; |
| 10853 | assert(pos.instruction_index < ira->old_irb.current_basic_block->instruction_list.length); | 10874 | assert(pos.instruction_index < ira->old_irb.current_basic_block->instruction_list.length); |
| 10854 | if (ira->codegen->verbose_ir) { | 10875 | if (ira->codegen->verbose_ir) { |
| 10855 | fprintf(stderr, "%s_%zu #%zu\n", ira->old_irb.current_basic_block->name_hint, | 10876 | fprintf(stderr, "%s_%zu #%" PRIu32 "\n", ira->old_irb.current_basic_block->name_hint, |
| 10856 | ira->old_irb.current_basic_block->debug_id, | 10877 | ira->old_irb.current_basic_block->debug_id, |
| 10857 | ira->old_irb.current_basic_block->instruction_list.at(pos.instruction_index)->debug_id); | 10878 | ira->old_irb.current_basic_block->instruction_list.at(pos.instruction_index)->debug_id); |
| 10858 | } | 10879 | } |
| ... | @@ -14686,14 +14707,15 @@ static IrInstruction *ir_analyze_instruction_merge_err_sets(IrAnalyze *ira, | ... | @@ -14686,14 +14707,15 @@ static IrInstruction *ir_analyze_instruction_merge_err_sets(IrAnalyze *ira, |
| 14686 | return ira->codegen->invalid_instruction; | 14707 | return ira->codegen->invalid_instruction; |
| 14687 | } | 14708 | } |
| 14688 | 14709 | ||
| 14689 | ErrorTableEntry **errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length); | 14710 | size_t errors_count = ira->codegen->errors_by_index.length; |
| 14711 | ErrorTableEntry **errors = allocate<ErrorTableEntry *>(errors_count, "ErrorTableEntry *"); | ||
| 14690 | for (uint32_t i = 0, count = op1_type->data.error_set.err_count; i < count; i += 1) { | 14712 | for (uint32_t i = 0, count = op1_type->data.error_set.err_count; i < count; i += 1) { |
| 14691 | ErrorTableEntry *error_entry = op1_type->data.error_set.errors[i]; | 14713 | ErrorTableEntry *error_entry = op1_type->data.error_set.errors[i]; |
| 14692 | assert(errors[error_entry->value] == nullptr); | 14714 | assert(errors[error_entry->value] == nullptr); |
| 14693 | errors[error_entry->value] = error_entry; | 14715 | errors[error_entry->value] = error_entry; |
| 14694 | } | 14716 | } |
| 14695 | ZigType *result_type = get_error_set_union(ira->codegen, errors, op1_type, op2_type, instruction->type_name); | 14717 | ZigType *result_type = get_error_set_union(ira->codegen, errors, op1_type, op2_type, instruction->type_name); |
| 14696 | free(errors); | 14718 | deallocate(errors, errors_count, "ErrorTableEntry *"); |
| 14697 | 14719 | ||
| 14698 | return ir_const_type(ira, &instruction->base, result_type); | 14720 | return ir_const_type(ira, &instruction->base, result_type); |
| 14699 | } | 14721 | } |
| ... | @@ -19241,7 +19263,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, | ... | @@ -19241,7 +19263,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, |
| 19241 | 19263 | ||
| 19242 | ZigType *target_type = target_value_ptr->value.type->data.pointer.child_type; | 19264 | ZigType *target_type = target_value_ptr->value.type->data.pointer.child_type; |
| 19243 | ConstExprValue *pointee_val = nullptr; | 19265 | ConstExprValue *pointee_val = nullptr; |
| 19244 | if (instr_is_comptime(target_value_ptr)) { | 19266 | if (instr_is_comptime(target_value_ptr) && target_value_ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) { |
| 19245 | pointee_val = const_ptr_pointee(ira, ira->codegen, &target_value_ptr->value, target_value_ptr->source_node); | 19267 | pointee_val = const_ptr_pointee(ira, ira->codegen, &target_value_ptr->value, target_value_ptr->source_node); |
| 19246 | if (pointee_val == nullptr) | 19268 | if (pointee_val == nullptr) |
| 19247 | return ira->codegen->invalid_instruction; | 19269 | return ira->codegen->invalid_instruction; |
| ... | @@ -23074,17 +23096,22 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction | ... | @@ -23074,17 +23096,22 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction |
| 23074 | zig_unreachable(); | 23096 | zig_unreachable(); |
| 23075 | } | 23097 | } |
| 23076 | 23098 | ||
| 23077 | uint64_t start_scalar = bigint_as_u64(&casted_start->value.data.x_bigint); | 23099 | ConstExprValue *start_val = ir_resolve_const(ira, casted_start, UndefBad); |
| 23100 | if (!start_val) | ||
| 23101 | return ira->codegen->invalid_instruction; | ||
| 23102 | |||
| 23103 | uint64_t start_scalar = bigint_as_u64(&start_val->data.x_bigint); | ||
| 23078 | if (!ptr_is_undef && start_scalar > rel_end) { | 23104 | if (!ptr_is_undef && start_scalar > rel_end) { |
| 23079 | ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice")); | 23105 | ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice")); |
| 23080 | return ira->codegen->invalid_instruction; | 23106 | return ira->codegen->invalid_instruction; |
| 23081 | } | 23107 | } |
| 23082 | 23108 | ||
| 23083 | uint64_t end_scalar; | 23109 | uint64_t end_scalar = rel_end; |
| 23084 | if (end) { | 23110 | if (end) { |
| 23085 | end_scalar = bigint_as_u64(&end->value.data.x_bigint); | 23111 | ConstExprValue *end_val = ir_resolve_const(ira, end, UndefBad); |
| 23086 | } else { | 23112 | if (!end_val) |
| 23087 | end_scalar = rel_end; | 23113 | return ira->codegen->invalid_instruction; |
| 23114 | end_scalar = bigint_as_u64(&end_val->data.x_bigint); | ||
| 23088 | } | 23115 | } |
| 23089 | if (!ptr_is_undef) { | 23116 | if (!ptr_is_undef) { |
| 23090 | if (end_scalar > rel_end) { | 23117 | if (end_scalar > rel_end) { |
| ... | @@ -24034,7 +24061,8 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, | ... | @@ -24034,7 +24061,8 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, |
| 24034 | return ira->codegen->invalid_instruction; | 24061 | return ira->codegen->invalid_instruction; |
| 24035 | } | 24062 | } |
| 24036 | 24063 | ||
| 24037 | AstNode **field_prev_uses = allocate<AstNode *>(ira->codegen->errors_by_index.length); | 24064 | size_t field_prev_uses_count = ira->codegen->errors_by_index.length; |
| 24065 | AstNode **field_prev_uses = allocate<AstNode *>(field_prev_uses_count, "AstNode *"); | ||
| 24038 | 24066 | ||
| 24039 | for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { | 24067 | for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { |
| 24040 | IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i]; | 24068 | IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i]; |
| ... | @@ -24091,7 +24119,7 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, | ... | @@ -24091,7 +24119,7 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, |
| 24091 | } | 24119 | } |
| 24092 | } | 24120 | } |
| 24093 | 24121 | ||
| 24094 | free(field_prev_uses); | 24122 | deallocate(field_prev_uses, field_prev_uses_count, "AstNode *"); |
| 24095 | } else if (switch_type->id == ZigTypeIdInt) { | 24123 | } else if (switch_type->id == ZigTypeIdInt) { |
| 24096 | RangeSet rs = {0}; | 24124 | RangeSet rs = {0}; |
| 24097 | for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { | 24125 | for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { |
| ... | @@ -26318,7 +26346,7 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_ | ... | @@ -26318,7 +26346,7 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_ |
| 26318 | } | 26346 | } |
| 26319 | 26347 | ||
| 26320 | if (ira->codegen->verbose_ir) { | 26348 | if (ira->codegen->verbose_ir) { |
| 26321 | fprintf(stderr, "analyze #%zu\n", old_instruction->debug_id); | 26349 | fprintf(stderr, "analyze #%" PRIu32 "\n", old_instruction->debug_id); |
| 26322 | } | 26350 | } |
| 26323 | IrInstruction *new_instruction = ir_analyze_instruction_base(ira, old_instruction); | 26351 | IrInstruction *new_instruction = ir_analyze_instruction_base(ira, old_instruction); |
| 26324 | if (new_instruction != nullptr) { | 26352 | if (new_instruction != nullptr) { |
src/ir_print.cpp+6-6| ... | @@ -38,8 +38,8 @@ struct IrPrint { | ... | @@ -38,8 +38,8 @@ struct IrPrint { |
| 38 | 38 | ||
| 39 | static void ir_print_other_instruction(IrPrint *irp, IrInstruction *instruction); | 39 | static void ir_print_other_instruction(IrPrint *irp, IrInstruction *instruction); |
| 40 | 40 | ||
| 41 | static const char* ir_instruction_type_str(IrInstruction* instruction) { | 41 | const char* ir_instruction_type_str(IrInstructionId id) { |
| 42 | switch (instruction->id) { | 42 | switch (id) { |
| 43 | case IrInstructionIdInvalid: | 43 | case IrInstructionIdInvalid: |
| 44 | return "Invalid"; | 44 | return "Invalid"; |
| 45 | case IrInstructionIdShuffleVector: | 45 | case IrInstructionIdShuffleVector: |
| ... | @@ -385,9 +385,9 @@ static void ir_print_prefix(IrPrint *irp, IrInstruction *instruction, bool trail | ... | @@ -385,9 +385,9 @@ static void ir_print_prefix(IrPrint *irp, IrInstruction *instruction, bool trail |
| 385 | const char mark = trailing ? ':' : '#'; | 385 | const char mark = trailing ? ':' : '#'; |
| 386 | const char *type_name = instruction->value.type ? buf_ptr(&instruction->value.type->name) : "(unknown)"; | 386 | const char *type_name = instruction->value.type ? buf_ptr(&instruction->value.type->name) : "(unknown)"; |
| 387 | const char *ref_count = ir_has_side_effects(instruction) ? | 387 | const char *ref_count = ir_has_side_effects(instruction) ? |
| 388 | "-" : buf_ptr(buf_sprintf("%" ZIG_PRI_usize "", instruction->ref_count)); | 388 | "-" : buf_ptr(buf_sprintf("%" PRIu32 "", instruction->ref_count)); |
| 389 | fprintf(irp->f, "%c%-3zu| %-22s| %-12s| %-2s| ", mark, instruction->debug_id, | 389 | fprintf(irp->f, "%c%-3" PRIu32 "| %-22s| %-12s| %-2s| ", mark, instruction->debug_id, |
| 390 | ir_instruction_type_str(instruction), type_name, ref_count); | 390 | ir_instruction_type_str(instruction->id), type_name, ref_count); |
| 391 | } | 391 | } |
| 392 | 392 | ||
| 393 | static void ir_print_const_value(IrPrint *irp, ConstExprValue *const_val) { | 393 | static void ir_print_const_value(IrPrint *irp, ConstExprValue *const_val) { |
| ... | @@ -398,7 +398,7 @@ static void ir_print_const_value(IrPrint *irp, ConstExprValue *const_val) { | ... | @@ -398,7 +398,7 @@ static void ir_print_const_value(IrPrint *irp, ConstExprValue *const_val) { |
| 398 | } | 398 | } |
| 399 | 399 | ||
| 400 | static void ir_print_var_instruction(IrPrint *irp, IrInstruction *instruction) { | 400 | static void ir_print_var_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 401 | fprintf(irp->f, "#%" ZIG_PRI_usize "", instruction->debug_id); | 401 | fprintf(irp->f, "#%" PRIu32 "", instruction->debug_id); |
| 402 | if (irp->pass != IrPassSrc && irp->printed.maybe_get(instruction) == nullptr) { | 402 | if (irp->pass != IrPassSrc && irp->printed.maybe_get(instruction) == nullptr) { |
| 403 | irp->printed.put(instruction, 0); | 403 | irp->printed.put(instruction, 0); |
| 404 | irp->pending.append(instruction); | 404 | irp->pending.append(instruction); |
src/ir_print.hpp+2| ... | @@ -15,4 +15,6 @@ | ... | @@ -15,4 +15,6 @@ |
| 15 | void ir_print(CodeGen *codegen, FILE *f, IrExecutable *executable, int indent_size, IrPass pass); | 15 | void ir_print(CodeGen *codegen, FILE *f, IrExecutable *executable, int indent_size, IrPass pass); |
| 16 | void ir_print_instruction(CodeGen *codegen, FILE *f, IrInstruction *instruction, int indent_size, IrPass pass); | 16 | void ir_print_instruction(CodeGen *codegen, FILE *f, IrInstruction *instruction, int indent_size, IrPass pass); |
| 17 | 17 | ||
| 18 | const char* ir_instruction_type_str(IrInstructionId id); | ||
| 19 | |||
| 18 | #endif | 20 | #endif |
src/main.cpp+52-23| ... | @@ -64,6 +64,9 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { | ... | @@ -64,6 +64,9 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { |
| 64 | " -fno-PIC disable Position Independent Code\n" | 64 | " -fno-PIC disable Position Independent Code\n" |
| 65 | " -ftime-report print timing diagnostics\n" | 65 | " -ftime-report print timing diagnostics\n" |
| 66 | " -fstack-report print stack size diagnostics\n" | 66 | " -fstack-report print stack size diagnostics\n" |
| 67 | #ifdef ZIG_ENABLE_MEM_PROFILE | ||
| 68 | " -fmem-report print memory usage diagnostics\n" | ||
| 69 | #endif | ||
| 67 | " -fdump-analysis write analysis.json file with type information\n" | 70 | " -fdump-analysis write analysis.json file with type information\n" |
| 68 | " -femit-docs create a docs/ dir with html documentation\n" | 71 | " -femit-docs create a docs/ dir with html documentation\n" |
| 69 | " -fno-emit-bin skip emitting machine code\n" | 72 | " -fno-emit-bin skip emitting machine code\n" |
| ... | @@ -306,9 +309,29 @@ static int zig_error_no_build_file(void) { | ... | @@ -306,9 +309,29 @@ static int zig_error_no_build_file(void) { |
| 306 | 309 | ||
| 307 | extern "C" int ZigClang_main(int argc, char **argv); | 310 | extern "C" int ZigClang_main(int argc, char **argv); |
| 308 | 311 | ||
| 312 | #ifdef ZIG_ENABLE_MEM_PROFILE | ||
| 313 | bool mem_report = false; | ||
| 314 | #endif | ||
| 315 | |||
| 316 | int main_exit(Stage2ProgressNode *root_progress_node, int exit_code) { | ||
| 317 | if (root_progress_node != nullptr) { | ||
| 318 | stage2_progress_end(root_progress_node); | ||
| 319 | } | ||
| 320 | #ifdef ZIG_ENABLE_MEM_PROFILE | ||
| 321 | if (mem_report) { | ||
| 322 | memprof_dump_stats(stderr); | ||
| 323 | } | ||
| 324 | #endif | ||
| 325 | return exit_code; | ||
| 326 | } | ||
| 327 | |||
| 309 | int main(int argc, char **argv) { | 328 | int main(int argc, char **argv) { |
| 310 | stage2_attach_segfault_handler(); | 329 | stage2_attach_segfault_handler(); |
| 311 | 330 | ||
| 331 | #ifdef ZIG_ENABLE_MEM_PROFILE | ||
| 332 | memprof_init(); | ||
| 333 | #endif | ||
| 334 | |||
| 312 | char *arg0 = argv[0]; | 335 | char *arg0 = argv[0]; |
| 313 | Error err; | 336 | Error err; |
| 314 | 337 | ||
| ... | @@ -670,6 +693,13 @@ int main(int argc, char **argv) { | ... | @@ -670,6 +693,13 @@ int main(int argc, char **argv) { |
| 670 | timing_info = true; | 693 | timing_info = true; |
| 671 | } else if (strcmp(arg, "-fstack-report") == 0) { | 694 | } else if (strcmp(arg, "-fstack-report") == 0) { |
| 672 | stack_report = true; | 695 | stack_report = true; |
| 696 | } else if (strcmp(arg, "-fmem-report") == 0) { | ||
| 697 | #ifdef ZIG_ENABLE_MEM_PROFILE | ||
| 698 | mem_report = true; | ||
| 699 | #else | ||
| 700 | fprintf(stderr, "-fmem-report requires configuring with -DZIG_ENABLE_MEM_PROFILE=ON\n"); | ||
| 701 | return print_error_usage(arg0); | ||
| 702 | #endif | ||
| 673 | } else if (strcmp(arg, "-fdump-analysis") == 0) { | 703 | } else if (strcmp(arg, "-fdump-analysis") == 0) { |
| 674 | enable_dump_analysis = true; | 704 | enable_dump_analysis = true; |
| 675 | } else if (strcmp(arg, "-femit-docs") == 0) { | 705 | } else if (strcmp(arg, "-femit-docs") == 0) { |
| ... | @@ -1038,16 +1068,14 @@ int main(int argc, char **argv) { | ... | @@ -1038,16 +1068,14 @@ int main(int argc, char **argv) { |
| 1038 | if (in_file) { | 1068 | if (in_file) { |
| 1039 | ZigLibCInstallation libc; | 1069 | ZigLibCInstallation libc; |
| 1040 | if ((err = zig_libc_parse(&libc, buf_create_from_str(in_file), &target, true))) | 1070 | if ((err = zig_libc_parse(&libc, buf_create_from_str(in_file), &target, true))) |
| 1041 | return EXIT_FAILURE; | 1071 | return main_exit(root_progress_node, EXIT_FAILURE); |
| 1042 | stage2_progress_end(root_progress_node); | 1072 | return main_exit(root_progress_node, EXIT_SUCCESS); |
| 1043 | return EXIT_SUCCESS; | ||
| 1044 | } | 1073 | } |
| 1045 | ZigLibCInstallation libc; | 1074 | ZigLibCInstallation libc; |
| 1046 | if ((err = zig_libc_find_native(&libc, true))) | 1075 | if ((err = zig_libc_find_native(&libc, true))) |
| 1047 | return EXIT_FAILURE; | 1076 | return main_exit(root_progress_node, EXIT_FAILURE); |
| 1048 | zig_libc_render(&libc, stdout); | 1077 | zig_libc_render(&libc, stdout); |
| 1049 | stage2_progress_end(root_progress_node); | 1078 | return main_exit(root_progress_node, EXIT_SUCCESS); |
| 1050 | return EXIT_SUCCESS; | ||
| 1051 | } | 1079 | } |
| 1052 | case CmdBuiltin: { | 1080 | case CmdBuiltin: { |
| 1053 | CodeGen *g = codegen_create(main_pkg_path, nullptr, &target, | 1081 | CodeGen *g = codegen_create(main_pkg_path, nullptr, &target, |
| ... | @@ -1065,10 +1093,9 @@ int main(int argc, char **argv) { | ... | @@ -1065,10 +1093,9 @@ int main(int argc, char **argv) { |
| 1065 | Buf *builtin_source = codegen_generate_builtin_source(g); | 1093 | Buf *builtin_source = codegen_generate_builtin_source(g); |
| 1066 | if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) { | 1094 | if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) { |
| 1067 | fprintf(stderr, "unable to write to stdout: %s\n", strerror(ferror(stdout))); | 1095 | fprintf(stderr, "unable to write to stdout: %s\n", strerror(ferror(stdout))); |
| 1068 | return EXIT_FAILURE; | 1096 | return main_exit(root_progress_node, EXIT_FAILURE); |
| 1069 | } | 1097 | } |
| 1070 | stage2_progress_end(root_progress_node); | 1098 | return main_exit(root_progress_node, EXIT_SUCCESS); |
| 1071 | return EXIT_SUCCESS; | ||
| 1072 | } | 1099 | } |
| 1073 | case CmdRun: | 1100 | case CmdRun: |
| 1074 | case CmdBuild: | 1101 | case CmdBuild: |
| ... | @@ -1142,7 +1169,7 @@ int main(int argc, char **argv) { | ... | @@ -1142,7 +1169,7 @@ int main(int argc, char **argv) { |
| 1142 | libc = allocate<ZigLibCInstallation>(1); | 1169 | libc = allocate<ZigLibCInstallation>(1); |
| 1143 | if ((err = zig_libc_parse(libc, buf_create_from_str(libc_txt), &target, true))) { | 1170 | if ((err = zig_libc_parse(libc, buf_create_from_str(libc_txt), &target, true))) { |
| 1144 | fprintf(stderr, "Unable to parse --libc text file: %s\n", err_str(err)); | 1171 | fprintf(stderr, "Unable to parse --libc text file: %s\n", err_str(err)); |
| 1145 | return EXIT_FAILURE; | 1172 | return main_exit(root_progress_node, EXIT_FAILURE); |
| 1146 | } | 1173 | } |
| 1147 | } | 1174 | } |
| 1148 | Buf *cache_dir_buf; | 1175 | Buf *cache_dir_buf; |
| ... | @@ -1219,7 +1246,7 @@ int main(int argc, char **argv) { | ... | @@ -1219,7 +1246,7 @@ int main(int argc, char **argv) { |
| 1219 | codegen_set_rdynamic(g, rdynamic); | 1246 | codegen_set_rdynamic(g, rdynamic); |
| 1220 | if (mmacosx_version_min && mios_version_min) { | 1247 | if (mmacosx_version_min && mios_version_min) { |
| 1221 | fprintf(stderr, "-mmacosx-version-min and -mios-version-min options not allowed together\n"); | 1248 | fprintf(stderr, "-mmacosx-version-min and -mios-version-min options not allowed together\n"); |
| 1222 | return EXIT_FAILURE; | 1249 | return main_exit(root_progress_node, EXIT_FAILURE); |
| 1223 | } | 1250 | } |
| 1224 | 1251 | ||
| 1225 | if (mmacosx_version_min) { | 1252 | if (mmacosx_version_min) { |
| ... | @@ -1259,6 +1286,11 @@ int main(int argc, char **argv) { | ... | @@ -1259,6 +1286,11 @@ int main(int argc, char **argv) { |
| 1259 | zig_print_stack_report(g, stdout); | 1286 | zig_print_stack_report(g, stdout); |
| 1260 | 1287 | ||
| 1261 | if (cmd == CmdRun) { | 1288 | if (cmd == CmdRun) { |
| 1289 | stage2_progress_end(root_progress_node); | ||
| 1290 | #ifdef ZIG_ENABLE_MEM_PROFILE | ||
| 1291 | memprof_dump_stats(stderr); | ||
| 1292 | #endif | ||
| 1293 | |||
| 1262 | const char *exec_path = buf_ptr(&g->output_file_path); | 1294 | const char *exec_path = buf_ptr(&g->output_file_path); |
| 1263 | ZigList<const char*> args = {0}; | 1295 | ZigList<const char*> args = {0}; |
| 1264 | 1296 | ||
| ... | @@ -1282,10 +1314,9 @@ int main(int argc, char **argv) { | ... | @@ -1282,10 +1314,9 @@ int main(int argc, char **argv) { |
| 1282 | buf_replace(&g->output_file_path, '/', '\\'); | 1314 | buf_replace(&g->output_file_path, '/', '\\'); |
| 1283 | #endif | 1315 | #endif |
| 1284 | if (printf("%s\n", buf_ptr(&g->output_file_path)) < 0) | 1316 | if (printf("%s\n", buf_ptr(&g->output_file_path)) < 0) |
| 1285 | return EXIT_FAILURE; | 1317 | return main_exit(root_progress_node, EXIT_FAILURE); |
| 1286 | } | 1318 | } |
| 1287 | stage2_progress_end(root_progress_node); | 1319 | return main_exit(root_progress_node, EXIT_SUCCESS); |
| 1288 | return EXIT_SUCCESS; | ||
| 1289 | } else { | 1320 | } else { |
| 1290 | zig_unreachable(); | 1321 | zig_unreachable(); |
| 1291 | } | 1322 | } |
| ... | @@ -1293,8 +1324,7 @@ int main(int argc, char **argv) { | ... | @@ -1293,8 +1324,7 @@ int main(int argc, char **argv) { |
| 1293 | codegen_translate_c(g, in_file_buf, stdout, cmd == CmdTranslateCUserland); | 1324 | codegen_translate_c(g, in_file_buf, stdout, cmd == CmdTranslateCUserland); |
| 1294 | if (timing_info) | 1325 | if (timing_info) |
| 1295 | codegen_print_timing_report(g, stderr); | 1326 | codegen_print_timing_report(g, stderr); |
| 1296 | stage2_progress_end(root_progress_node); | 1327 | return main_exit(root_progress_node, EXIT_SUCCESS); |
| 1297 | return EXIT_SUCCESS; | ||
| 1298 | } else if (cmd == CmdTest) { | 1328 | } else if (cmd == CmdTest) { |
| 1299 | codegen_set_emit_file_type(g, emit_file_type); | 1329 | codegen_set_emit_file_type(g, emit_file_type); |
| 1300 | 1330 | ||
| ... | @@ -1314,7 +1344,7 @@ int main(int argc, char **argv) { | ... | @@ -1314,7 +1344,7 @@ int main(int argc, char **argv) { |
| 1314 | 1344 | ||
| 1315 | if (g->disable_bin_generation) { | 1345 | if (g->disable_bin_generation) { |
| 1316 | fprintf(stderr, "Semantic analysis complete. No binary produced due to -fno-emit-bin.\n"); | 1346 | fprintf(stderr, "Semantic analysis complete. No binary produced due to -fno-emit-bin.\n"); |
| 1317 | return 0; | 1347 | return main_exit(root_progress_node, EXIT_SUCCESS); |
| 1318 | } | 1348 | } |
| 1319 | 1349 | ||
| 1320 | Buf *test_exe_path_unresolved = &g->output_file_path; | 1350 | Buf *test_exe_path_unresolved = &g->output_file_path; |
| ... | @@ -1324,7 +1354,7 @@ int main(int argc, char **argv) { | ... | @@ -1324,7 +1354,7 @@ int main(int argc, char **argv) { |
| 1324 | if (emit_file_type != EmitFileTypeBinary) { | 1354 | if (emit_file_type != EmitFileTypeBinary) { |
| 1325 | fprintf(stderr, "Created %s but skipping execution because it is non executable.\n", | 1355 | fprintf(stderr, "Created %s but skipping execution because it is non executable.\n", |
| 1326 | buf_ptr(test_exe_path)); | 1356 | buf_ptr(test_exe_path)); |
| 1327 | return 0; | 1357 | return main_exit(root_progress_node, EXIT_SUCCESS); |
| 1328 | } | 1358 | } |
| 1329 | 1359 | ||
| 1330 | for (size_t i = 0; i < test_exec_args.length; i += 1) { | 1360 | for (size_t i = 0; i < test_exec_args.length; i += 1) { |
| ... | @@ -1336,7 +1366,7 @@ int main(int argc, char **argv) { | ... | @@ -1336,7 +1366,7 @@ int main(int argc, char **argv) { |
| 1336 | if (!target_can_exec(&native, &target) && test_exec_args.length == 0) { | 1366 | if (!target_can_exec(&native, &target) && test_exec_args.length == 0) { |
| 1337 | fprintf(stderr, "Created %s but skipping execution because it is non-native.\n", | 1367 | fprintf(stderr, "Created %s but skipping execution because it is non-native.\n", |
| 1338 | buf_ptr(test_exe_path)); | 1368 | buf_ptr(test_exe_path)); |
| 1339 | return 0; | 1369 | return main_exit(root_progress_node, EXIT_SUCCESS); |
| 1340 | } | 1370 | } |
| 1341 | 1371 | ||
| 1342 | Termination term; | 1372 | Termination term; |
| ... | @@ -1348,21 +1378,20 @@ int main(int argc, char **argv) { | ... | @@ -1348,21 +1378,20 @@ int main(int argc, char **argv) { |
| 1348 | fprintf(stderr, "\nTests failed. Use the following command to reproduce the failure:\n"); | 1378 | fprintf(stderr, "\nTests failed. Use the following command to reproduce the failure:\n"); |
| 1349 | fprintf(stderr, "%s\n", buf_ptr(test_exe_path)); | 1379 | fprintf(stderr, "%s\n", buf_ptr(test_exe_path)); |
| 1350 | } | 1380 | } |
| 1351 | stage2_progress_end(root_progress_node); | 1381 | return main_exit(root_progress_node, (term.how == TerminationIdClean) ? term.code : -1); |
| 1352 | return (term.how == TerminationIdClean) ? term.code : -1; | ||
| 1353 | } else { | 1382 | } else { |
| 1354 | zig_unreachable(); | 1383 | zig_unreachable(); |
| 1355 | } | 1384 | } |
| 1356 | } | 1385 | } |
| 1357 | case CmdVersion: | 1386 | case CmdVersion: |
| 1358 | printf("%s\n", ZIG_VERSION_STRING); | 1387 | printf("%s\n", ZIG_VERSION_STRING); |
| 1359 | return EXIT_SUCCESS; | 1388 | return main_exit(root_progress_node, EXIT_SUCCESS); |
| 1360 | case CmdZen: { | 1389 | case CmdZen: { |
| 1361 | const char *ptr; | 1390 | const char *ptr; |
| 1362 | size_t len; | 1391 | size_t len; |
| 1363 | stage2_zen(&ptr, &len); | 1392 | stage2_zen(&ptr, &len); |
| 1364 | fwrite(ptr, len, 1, stdout); | 1393 | fwrite(ptr, len, 1, stdout); |
| 1365 | return EXIT_SUCCESS; | 1394 | return main_exit(root_progress_node, EXIT_SUCCESS); |
| 1366 | } | 1395 | } |
| 1367 | case CmdTargets: | 1396 | case CmdTargets: |
| 1368 | return print_target_list(stdout); | 1397 | return print_target_list(stdout); |
src/memory_profiling.cpp created+139| ... | @@ -0,0 +1,139 @@ | ||
| 1 | #include "memory_profiling.hpp" | ||
| 2 | #include "hash_map.hpp" | ||
| 3 | #include "list.hpp" | ||
| 4 | #include "util.hpp" | ||
| 5 | #include <string.h> | ||
| 6 | |||
| 7 | #ifdef ZIG_ENABLE_MEM_PROFILE | ||
| 8 | |||
| 9 | static bool str_eql_str(const char *a, const char *b) { | ||
| 10 | return strcmp(a, b) == 0; | ||
| 11 | } | ||
| 12 | |||
| 13 | static uint32_t str_hash(const char *s) { | ||
| 14 | // FNV 32-bit hash | ||
| 15 | uint32_t h = 2166136261; | ||
| 16 | for (; *s; s += 1) { | ||
| 17 | h = h ^ *s; | ||
| 18 | h = h * 16777619; | ||
| 19 | } | ||
| 20 | return h; | ||
| 21 | } | ||
| 22 | |||
| 23 | struct CountAndSize { | ||
| 24 | size_t item_count; | ||
| 25 | size_t type_size; | ||
| 26 | }; | ||
| 27 | |||
| 28 | ZigList<const char *> unknown_names = {}; | ||
| 29 | HashMap<const char *, CountAndSize, str_hash, str_eql_str> usage_table = {}; | ||
| 30 | bool table_active = false; | ||
| 31 | |||
| 32 | |||
| 33 | static const char *get_default_name(const char *name_or_null, size_t type_size) { | ||
| 34 | if (name_or_null != nullptr) return name_or_null; | ||
| 35 | if (type_size >= unknown_names.length) { | ||
| 36 | table_active = false; | ||
| 37 | unknown_names.resize(type_size + 1); | ||
| 38 | table_active = true; | ||
| 39 | } | ||
| 40 | if (unknown_names.at(type_size) == nullptr) { | ||
| 41 | char buf[100]; | ||
| 42 | sprintf(buf, "Unknown_%zu%c", type_size, 0); | ||
| 43 | unknown_names.at(type_size) = strdup(buf); | ||
| 44 | } | ||
| 45 | return unknown_names.at(type_size); | ||
| 46 | } | ||
| 47 | |||
| 48 | void memprof_alloc(const char *name, size_t count, size_t type_size) { | ||
| 49 | if (!table_active) return; | ||
| 50 | if (count == 0) return; | ||
| 51 | // temporarily disable during table put | ||
| 52 | table_active = false; | ||
| 53 | name = get_default_name(name, type_size); | ||
| 54 | auto existing_entry = usage_table.put_unique(name, {count, type_size}); | ||
| 55 | if (existing_entry != nullptr) { | ||
| 56 | assert(existing_entry->value.type_size == type_size); // allocated name does not match type | ||
| 57 | existing_entry->value.item_count += count; | ||
| 58 | } | ||
| 59 | table_active = true; | ||
| 60 | } | ||
| 61 | |||
| 62 | void memprof_dealloc(const char *name, size_t count, size_t type_size) { | ||
| 63 | if (!table_active) return; | ||
| 64 | if (count == 0) return; | ||
| 65 | name = get_default_name(name, type_size); | ||
| 66 | auto existing_entry = usage_table.maybe_get(name); | ||
| 67 | if (existing_entry == nullptr) { | ||
| 68 | zig_panic("deallocated more than allocated; compromised memory usage stats"); | ||
| 69 | } | ||
| 70 | if (existing_entry->value.type_size != type_size) { | ||
| 71 | zig_panic("deallocated name '%s' does not match expected type size %zu", name, type_size); | ||
| 72 | } | ||
| 73 | existing_entry->value.item_count -= count; | ||
| 74 | } | ||
| 75 | |||
| 76 | void memprof_init(void) { | ||
| 77 | usage_table.init(1024); | ||
| 78 | table_active = true; | ||
| 79 | } | ||
| 80 | |||
| 81 | struct MemItem { | ||
| 82 | const char *type_name; | ||
| 83 | CountAndSize count_and_size; | ||
| 84 | }; | ||
| 85 | |||
| 86 | static size_t get_bytes(const MemItem *item) { | ||
| 87 | return item->count_and_size.item_count * item->count_and_size.type_size; | ||
| 88 | } | ||
| 89 | |||
| 90 | static int compare_bytes_desc(const void *a, const void *b) { | ||
| 91 | size_t size_a = get_bytes((const MemItem *)(a)); | ||
| 92 | size_t size_b = get_bytes((const MemItem *)(b)); | ||
| 93 | if (size_a > size_b) | ||
| 94 | return -1; | ||
| 95 | if (size_a < size_b) | ||
| 96 | return 1; | ||
| 97 | return 0; | ||
| 98 | } | ||
| 99 | |||
| 100 | void memprof_dump_stats(FILE *file) { | ||
| 101 | assert(table_active); | ||
| 102 | // disable modifications from this function | ||
| 103 | table_active = false; | ||
| 104 | |||
| 105 | ZigList<MemItem> list = {}; | ||
| 106 | |||
| 107 | auto it = usage_table.entry_iterator(); | ||
| 108 | for (;;) { | ||
| 109 | auto *entry = it.next(); | ||
| 110 | if (!entry) | ||
| 111 | break; | ||
| 112 | |||
| 113 | list.append({entry->key, entry->value}); | ||
| 114 | } | ||
| 115 | |||
| 116 | qsort(list.items, list.length, sizeof(MemItem), compare_bytes_desc); | ||
| 117 | |||
| 118 | size_t total_bytes_used = 0; | ||
| 119 | |||
| 120 | for (size_t i = 0; i < list.length; i += 1) { | ||
| 121 | const MemItem *item = &list.at(i); | ||
| 122 | fprintf(file, "%s: %zu items, %zu bytes each, total ", item->type_name, | ||
| 123 | item->count_and_size.item_count, item->count_and_size.type_size); | ||
| 124 | size_t bytes = get_bytes(item); | ||
| 125 | zig_pretty_print_bytes(file, bytes); | ||
| 126 | fprintf(file, "\n"); | ||
| 127 | |||
| 128 | total_bytes_used += bytes; | ||
| 129 | } | ||
| 130 | |||
| 131 | fprintf(stderr, "Total bytes used: "); | ||
| 132 | zig_pretty_print_bytes(file, total_bytes_used); | ||
| 133 | fprintf(file, "\n"); | ||
| 134 | |||
| 135 | list.deinit(); | ||
| 136 | table_active = true; | ||
| 137 | } | ||
| 138 | |||
| 139 | #endif | ||
src/memory_profiling.hpp created+22| ... | @@ -0,0 +1,22 @@ | ||
| 1 | /* | ||
| 2 | * Copyright (c) 2019 Andrew Kelley | ||
| 3 | * | ||
| 4 | * This file is part of zig, which is MIT licensed. | ||
| 5 | * See http://opensource.org/licenses/MIT | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef ZIG_MEMORY_PROFILING_HPP | ||
| 9 | #define ZIG_MEMORY_PROFILING_HPP | ||
| 10 | |||
| 11 | #include "config.h" | ||
| 12 | |||
| 13 | #include <stddef.h> | ||
| 14 | #include <stdio.h> | ||
| 15 | |||
| 16 | void memprof_init(void); | ||
| 17 | |||
| 18 | void memprof_alloc(const char *name, size_t item_count, size_t type_size); | ||
| 19 | void memprof_dealloc(const char *name, size_t item_count, size_t type_size); | ||
| 20 | |||
| 21 | void memprof_dump_stats(FILE *file); | ||
| 22 | #endif | ||
src/parser.cpp+7-9| ... | @@ -518,8 +518,8 @@ static Token *ast_parse_doc_comments(ParseContext *pc, Buf *buf) { | ... | @@ -518,8 +518,8 @@ static Token *ast_parse_doc_comments(ParseContext *pc, Buf *buf) { |
| 518 | // <- TestDecl ContainerMembers | 518 | // <- TestDecl ContainerMembers |
| 519 | // / TopLevelComptime ContainerMembers | 519 | // / TopLevelComptime ContainerMembers |
| 520 | // / KEYWORD_pub? TopLevelDecl ContainerMembers | 520 | // / KEYWORD_pub? TopLevelDecl ContainerMembers |
| 521 | // / KEYWORD_pub? ContainerField COMMA ContainerMembers | 521 | // / ContainerField COMMA ContainerMembers |
| 522 | // / KEYWORD_pub? ContainerField | 522 | // / ContainerField |
| 523 | // / | 523 | // / |
| 524 | static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) { | 524 | static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) { |
| 525 | AstNodeContainerDecl res = {}; | 525 | AstNodeContainerDecl res = {}; |
| ... | @@ -548,10 +548,13 @@ static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) { | ... | @@ -548,10 +548,13 @@ static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) { |
| 548 | continue; | 548 | continue; |
| 549 | } | 549 | } |
| 550 | 550 | ||
| 551 | if (visib_token != nullptr) { | ||
| 552 | ast_error(pc, peek_token(pc), "expected function or variable declaration after pub"); | ||
| 553 | } | ||
| 554 | |||
| 551 | AstNode *container_field = ast_parse_container_field(pc); | 555 | AstNode *container_field = ast_parse_container_field(pc); |
| 552 | if (container_field != nullptr) { | 556 | if (container_field != nullptr) { |
| 553 | assert(container_field->type == NodeTypeStructField); | 557 | assert(container_field->type == NodeTypeStructField); |
| 554 | container_field->data.struct_field.visib_mod = visib_mod; | ||
| 555 | container_field->data.struct_field.doc_comments = doc_comment_buf; | 558 | container_field->data.struct_field.doc_comments = doc_comment_buf; |
| 556 | res.fields.append(container_field); | 559 | res.fields.append(container_field); |
| 557 | if (eat_token_if(pc, TokenIdComma) != nullptr) { | 560 | if (eat_token_if(pc, TokenIdComma) != nullptr) { |
| ... | @@ -561,12 +564,7 @@ static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) { | ... | @@ -561,12 +564,7 @@ static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) { |
| 561 | } | 564 | } |
| 562 | } | 565 | } |
| 563 | 566 | ||
| 564 | // We visib_token wasn't eaten, then we haven't consumed the first token in this rule yet. | 567 | break; |
| 565 | // It is therefore safe to return and let the caller continue parsing. | ||
| 566 | if (visib_token == nullptr) | ||
| 567 | break; | ||
| 568 | |||
| 569 | ast_invalid_token_error(pc, peek_token(pc)); | ||
| 570 | } | 568 | } |
| 571 | 569 | ||
| 572 | return res; | 570 | return res; |
src/util.cpp+18| ... | @@ -119,3 +119,21 @@ Slice<uint8_t> SplitIterator_rest(SplitIterator *self) { | ... | @@ -119,3 +119,21 @@ Slice<uint8_t> SplitIterator_rest(SplitIterator *self) { |
| 119 | SplitIterator memSplit(Slice<uint8_t> buffer, Slice<uint8_t> split_bytes) { | 119 | SplitIterator memSplit(Slice<uint8_t> buffer, Slice<uint8_t> split_bytes) { |
| 120 | return SplitIterator{0, buffer, split_bytes}; | 120 | return SplitIterator{0, buffer, split_bytes}; |
| 121 | } | 121 | } |
| 122 | |||
| 123 | void zig_pretty_print_bytes(FILE *f, double n) { | ||
| 124 | if (n > 1024.0 * 1024.0 * 1024.0) { | ||
| 125 | fprintf(f, "%.02f GiB", n / 1024.0 / 1024.0 / 1024.0); | ||
| 126 | return; | ||
| 127 | } | ||
| 128 | if (n > 1024.0 * 1024.0) { | ||
| 129 | fprintf(f, "%.02f MiB", n / 1024.0 / 1024.0); | ||
| 130 | return; | ||
| 131 | } | ||
| 132 | if (n > 1024.0) { | ||
| 133 | fprintf(f, "%.02f KiB", n / 1024.0); | ||
| 134 | return; | ||
| 135 | } | ||
| 136 | fprintf(f, "%.02f bytes", n ); | ||
| 137 | return; | ||
| 138 | } | ||
| 139 |
src/util.hpp+31-4| ... | @@ -8,6 +8,8 @@ | ... | @@ -8,6 +8,8 @@ |
| 8 | #ifndef ZIG_UTIL_HPP | 8 | #ifndef ZIG_UTIL_HPP |
| 9 | #define ZIG_UTIL_HPP | 9 | #define ZIG_UTIL_HPP |
| 10 | 10 | ||
| 11 | #include "memory_profiling.hpp" | ||
| 12 | |||
| 11 | #include <stdlib.h> | 13 | #include <stdlib.h> |
| 12 | #include <stdint.h> | 14 | #include <stdint.h> |
| 13 | #include <string.h> | 15 | #include <string.h> |
| ... | @@ -96,7 +98,10 @@ static inline int ctzll(unsigned long long mask) { | ... | @@ -96,7 +98,10 @@ static inline int ctzll(unsigned long long mask) { |
| 96 | 98 | ||
| 97 | 99 | ||
| 98 | template<typename T> | 100 | template<typename T> |
| 99 | ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate_nonzero(size_t count) { | 101 | ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate_nonzero(size_t count, const char *name = nullptr) { |
| 102 | #ifdef ZIG_ENABLE_MEM_PROFILE | ||
| 103 | memprof_alloc(name, count, sizeof(T)); | ||
| 104 | #endif | ||
| 100 | #ifndef NDEBUG | 105 | #ifndef NDEBUG |
| 101 | // make behavior when size == 0 portable | 106 | // make behavior when size == 0 portable |
| 102 | if (count == 0) | 107 | if (count == 0) |
| ... | @@ -109,7 +114,10 @@ ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate_nonzero(size_t count) { | ... | @@ -109,7 +114,10 @@ ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate_nonzero(size_t count) { |
| 109 | } | 114 | } |
| 110 | 115 | ||
| 111 | template<typename T> | 116 | template<typename T> |
| 112 | ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate(size_t count) { | 117 | ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate(size_t count, const char *name = nullptr) { |
| 118 | #ifdef ZIG_ENABLE_MEM_PROFILE | ||
| 119 | memprof_alloc(name, count, sizeof(T)); | ||
| 120 | #endif | ||
| 113 | #ifndef NDEBUG | 121 | #ifndef NDEBUG |
| 114 | // make behavior when size == 0 portable | 122 | // make behavior when size == 0 portable |
| 115 | if (count == 0) | 123 | if (count == 0) |
| ... | @@ -122,7 +130,7 @@ ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate(size_t count) { | ... | @@ -122,7 +130,7 @@ ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate(size_t count) { |
| 122 | } | 130 | } |
| 123 | 131 | ||
| 124 | template<typename T> | 132 | template<typename T> |
| 125 | static inline T *reallocate(T *old, size_t old_count, size_t new_count) { | 133 | static inline T *reallocate(T *old, size_t old_count, size_t new_count, const char *name = nullptr) { |
| 126 | T *ptr = reallocate_nonzero(old, old_count, new_count); | 134 | T *ptr = reallocate_nonzero(old, old_count, new_count); |
| 127 | if (new_count > old_count) { | 135 | if (new_count > old_count) { |
| 128 | memset(&ptr[old_count], 0, (new_count - old_count) * sizeof(T)); | 136 | memset(&ptr[old_count], 0, (new_count - old_count) * sizeof(T)); |
| ... | @@ -131,7 +139,11 @@ static inline T *reallocate(T *old, size_t old_count, size_t new_count) { | ... | @@ -131,7 +139,11 @@ static inline T *reallocate(T *old, size_t old_count, size_t new_count) { |
| 131 | } | 139 | } |
| 132 | 140 | ||
| 133 | template<typename T> | 141 | template<typename T> |
| 134 | static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count) { | 142 | static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count, const char *name = nullptr) { |
| 143 | #ifdef ZIG_ENABLE_MEM_PROFILE | ||
| 144 | memprof_dealloc(name, old_count, sizeof(T)); | ||
| 145 | memprof_alloc(name, new_count, sizeof(T)); | ||
| 146 | #endif | ||
| 135 | #ifndef NDEBUG | 147 | #ifndef NDEBUG |
| 136 | // make behavior when size == 0 portable | 148 | // make behavior when size == 0 portable |
| 137 | if (new_count == 0 && old == nullptr) | 149 | if (new_count == 0 && old == nullptr) |
| ... | @@ -143,6 +155,19 @@ static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count) | ... | @@ -143,6 +155,19 @@ static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count) |
| 143 | return ptr; | 155 | return ptr; |
| 144 | } | 156 | } |
| 145 | 157 | ||
| 158 | template<typename T> | ||
| 159 | static inline void deallocate(T *old, size_t count, const char *name = nullptr) { | ||
| 160 | #ifdef ZIG_ENABLE_MEM_PROFILE | ||
| 161 | memprof_dealloc(name, count, sizeof(T)); | ||
| 162 | #endif | ||
| 163 | free(old); | ||
| 164 | } | ||
| 165 | |||
| 166 | template<typename T> | ||
| 167 | static inline void destroy(T *old, const char *name = nullptr) { | ||
| 168 | return deallocate(old, 1); | ||
| 169 | } | ||
| 170 | |||
| 146 | template <typename T, size_t n> | 171 | template <typename T, size_t n> |
| 147 | constexpr size_t array_length(const T (&)[n]) { | 172 | constexpr size_t array_length(const T (&)[n]) { |
| 148 | return n; | 173 | return n; |
| ... | @@ -225,6 +250,8 @@ static inline double zig_f16_to_double(float16_t x) { | ... | @@ -225,6 +250,8 @@ static inline double zig_f16_to_double(float16_t x) { |
| 225 | return z; | 250 | return z; |
| 226 | } | 251 | } |
| 227 | 252 | ||
| 253 | void zig_pretty_print_bytes(FILE *f, double n); | ||
| 254 | |||
| 228 | template<typename T> | 255 | template<typename T> |
| 229 | struct Optional { | 256 | struct Optional { |
| 230 | T value; | 257 | T value; |
test/cli.zig+2-2| ... | @@ -37,7 +37,7 @@ pub fn main() !void { | ... | @@ -37,7 +37,7 @@ pub fn main() !void { |
| 37 | testMissingOutputPath, | 37 | testMissingOutputPath, |
| 38 | }; | 38 | }; |
| 39 | for (test_fns) |testFn| { | 39 | for (test_fns) |testFn| { |
| 40 | try fs.deleteTree(a, dir_path); | 40 | try fs.deleteTree(dir_path); |
| 41 | try fs.makeDir(dir_path); | 41 | try fs.makeDir(dir_path); |
| 42 | try testFn(zig_exe, dir_path); | 42 | try testFn(zig_exe, dir_path); |
| 43 | } | 43 | } |
| ... | @@ -87,7 +87,7 @@ fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult { | ... | @@ -87,7 +87,7 @@ fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult { |
| 87 | fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void { | 87 | fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void { |
| 88 | _ = try exec(dir_path, [_][]const u8{ zig_exe, "init-lib" }); | 88 | _ = try exec(dir_path, [_][]const u8{ zig_exe, "init-lib" }); |
| 89 | const test_result = try exec(dir_path, [_][]const u8{ zig_exe, "build", "test" }); | 89 | const test_result = try exec(dir_path, [_][]const u8{ zig_exe, "build", "test" }); |
| 90 | testing.expect(std.mem.eql(u8, test_result.stderr, "")); | 90 | testing.expect(std.mem.endsWith(u8, test_result.stderr, "All tests passed.\n")); |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void { | 93 | fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void { |
test/compile_errors.zig+10| ... | @@ -2,6 +2,16 @@ const tests = @import("tests.zig"); | ... | @@ -2,6 +2,16 @@ const tests = @import("tests.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | 3 | ||
| 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { | 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5 | cases.add( | ||
| 6 | "comparison with error union and error value", | ||
| 7 | \\export fn entry() void { | ||
| 8 | \\ var number_or_error: anyerror!i32 = error.SomethingAwful; | ||
| 9 | \\ _ = number_or_error == error.SomethingAwful; | ||
| 10 | \\} | ||
| 11 | , | ||
| 12 | "tmp.zig:3:25: error: operator not allowed for type 'anyerror!i32'", | ||
| 13 | ); | ||
| 14 | |||
| 5 | cases.add( | 15 | cases.add( |
| 6 | "switch with overlapping case ranges", | 16 | "switch with overlapping case ranges", |
| 7 | \\export fn entry() void { | 17 | \\export fn entry() void { |
test/stage1/behavior/switch.zig+18| ... | @@ -434,3 +434,21 @@ test "switch with disjoint range" { | ... | @@ -434,3 +434,21 @@ test "switch with disjoint range" { |
| 434 | 126...126 => {}, | 434 | 126...126 => {}, |
| 435 | } | 435 | } |
| 436 | } | 436 | } |
| 437 | |||
| 438 | var state: u32 = 0; | ||
| 439 | fn poll() void { | ||
| 440 | switch (state) { | ||
| 441 | 0 => { | ||
| 442 | state = 1; | ||
| 443 | }, | ||
| 444 | else => { | ||
| 445 | state += 1; | ||
| 446 | }, | ||
| 447 | } | ||
| 448 | } | ||
| 449 | |||
| 450 | test "switch on global mutable var isn't constant-folded" { | ||
| 451 | while (state < 2) { | ||
| 452 | poll(); | ||
| 453 | } | ||
| 454 | } |
test/stage1/behavior/union.zig+14| ... | @@ -521,3 +521,17 @@ test "extern union doesn't trigger field check at comptime" { | ... | @@ -521,3 +521,17 @@ test "extern union doesn't trigger field check at comptime" { |
| 521 | const x = U{ .x = 0x55AAAA55 }; | 521 | const x = U{ .x = 0x55AAAA55 }; |
| 522 | comptime expect(x.y == 0x55); | 522 | comptime expect(x.y == 0x55); |
| 523 | } | 523 | } |
| 524 | |||
| 525 | const Foo1 = union(enum) { | ||
| 526 | f: struct { | ||
| 527 | x: usize, | ||
| 528 | }, | ||
| 529 | }; | ||
| 530 | var glbl: Foo1 = undefined; | ||
| 531 | |||
| 532 | test "global union with single field is correctly initialized" { | ||
| 533 | glbl = Foo1{ | ||
| 534 | .f = @memberType(Foo1, 0){ .x = 123 }, | ||
| 535 | }; | ||
| 536 | expect(glbl.f.x == 123); | ||
| 537 | } |
tools/process_headers.zig+1-1| ... | @@ -340,7 +340,7 @@ pub fn main() !void { | ... | @@ -340,7 +340,7 @@ pub fn main() !void { |
| 340 | try dir_stack.append(target_include_dir); | 340 | try dir_stack.append(target_include_dir); |
| 341 | 341 | ||
| 342 | while (dir_stack.popOrNull()) |full_dir_name| { | 342 | while (dir_stack.popOrNull()) |full_dir_name| { |
| 343 | var dir = std.fs.Dir.open(allocator, full_dir_name) catch |err| switch (err) { | 343 | var dir = std.fs.Dir.open(full_dir_name) catch |err| switch (err) { |
| 344 | error.FileNotFound => continue :search, | 344 | error.FileNotFound => continue :search, |
| 345 | error.AccessDenied => continue :search, | 345 | error.AccessDenied => continue :search, |
| 346 | else => return err, | 346 | else => return err, |