Upgrade to Zig 0.17

Dependencies:
- v8: zig-v8-fork 0.17 branch (lightpanda-io/zig-v8-fork#218).
- sqlite3: build the amalgamation directly; the allyourcodebase wrapper
  has no 0.17 support yet.
- pcre2: master commit with 0.17 support (no release yet).
- translate_c package (2.0.0) replaces the deprecated addTranslateC.
  default_init is set to keep 0.16's zero-initialized struct fields.

Build:
- b.pathFromRoot/build_root/args/sysroot are gone: resolve paths from
  b.root, use addPassthruArgs, declare configure-time file and directory
  dependencies, and poison the configure cache for the git version.
- Drop curl config values the template never used (now an error).
- Drop the 0.16 zip-fetch workaround from the Makefile and CI.
- -Doptimize=ReleaseFast -> -Doptimize=fast.

Language and std:
- @intFromEnum/@enumFromInt -> @backingInt/@fromBackingInt.
- Struct-of-arrays @typeInfo (field_names, field_types, decl_names,
  param_types, error_names).
- `a ** n` removed: @splat for arrays, string.repeat for strings.
- errdefer captures removed: split WebDriver.run and fulfillRedirect.
- @hasDecl only sees pub decls: make StyleManager's Spec.finish pub,
  otherwise it silently stops running.
- SafeAllocator replaces DebugAllocator; the test runner now has to
  initialize std.testing.allocator_instance itself.
- std.fmt.allocPrint/bufPrint -> Allocator.print/std.mem.print,
  dupeZ -> dupeSentinel, std.builtin -> std.lang, builtin.os ->
  builtin.target.os, zon.parse arena API, BufferFirstAllocator, and the
  remaining renames (getLastOrNull, bit_set, ascii.find*, meta.Int).
This commit is contained in:
Adrià Arrufat committed 2026-10-03 16:09:42 +02:00
1 parent e020e9d350
commit 10609b025a
192 files changed
+1124 -1141

No files matched your search

-8
View File
@@ -41,14 +41,6 @@ runs:
# Zig version used from the `minimum_zig_version` field in build.zig.zon
- uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
# Zig 0.16 writes fetched .zip archives to <global cache>/tmp but no longer
# creates that directory, so zip dependencies (sqlite amalgamation) fail
# with "failed to create temporary zip file: FileNotFound" on the fresh
# cache dir setup-zig points ZIG_GLOBAL_CACHE_DIR at.
- name: Zig 0.16 zip-fetch workaround
shell: bash
run: mkdir -p "${ZIG_GLOBAL_CACHE_DIR:-$HOME/.cache/zig}/tmp"
# Rust Toolchain for src/rust (html5ever bridge, renderer)
- uses: dtolnay/rust-toolchain@2c7215f132e9ebf062739d9130488b56d53c060c # master
with:
+1 -1
View File
@@ -44,4 +44,4 @@ runs:
- name: v8 snapshot
shell: bash
if: hashFiles('src/snapshot.bin') == ''
run: zig build -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=ReleaseFast snapshot_creator -- src/snapshot.bin
run: zig build -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=fast snapshot_creator -- src/snapshot.bin
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
- uses: ./.github/actions/v8-snapshot
- name: zig build release
run: zig build -Dsnapshot_path=../../snapshot.bin -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=ReleaseFast -Dcpu=x86_64
run: zig build -Dsnapshot_path=../../snapshot.bin -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=fast -Dcpu=x86_64
- name: upload artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+1 -1
View File
@@ -27,7 +27,7 @@ jobs:
- uses: ./.github/actions/install
- name: zig build release
run: zig build -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=ReleaseFast -Dcpu=x86_64
run: zig build -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=fast -Dcpu=x86_64
- name: upload artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+1 -1
View File
@@ -53,7 +53,7 @@ jobs:
- uses: ./.github/actions/v8-snapshot
- name: zig build release
run: zig build -Dsnapshot_path=../../snapshot.bin -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=ReleaseFast -Dorderfile=orderfile/lightpanda.ld -Dcpu=x86_64
run: zig build -Dsnapshot_path=../../snapshot.bin -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=fast -Dorderfile=orderfile/lightpanda.ld -Dcpu=x86_64
- name: upload artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+1 -1
View File
@@ -56,7 +56,7 @@ jobs:
- uses: ./.github/actions/orderfile
id: orderfile
with:
build-args: -Dsnapshot_path=../../snapshot.bin -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=ReleaseFast -Dcpu=x86_64
build-args: -Dsnapshot_path=../../snapshot.bin -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=fast -Dcpu=x86_64
- name: open a pull request
env:
+4 -4
View File
@@ -58,10 +58,10 @@ jobs:
arch: ${{env.ARCH}}
- name: v8 snapshot
run: zig build -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=ReleaseFast snapshot_creator -- src/snapshot.bin
run: zig build -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=fast snapshot_creator -- src/snapshot.bin
- name: zig build
run: zig build -Dsnapshot_path=../../snapshot.bin -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=ReleaseFast -Dorderfile=orderfile/lightpanda.ld ${{ matrix.cpu_flag }} ${{ env.VERSION_FLAG }}
run: zig build -Dsnapshot_path=../../snapshot.bin -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=fast -Dorderfile=orderfile/lightpanda.ld ${{ matrix.cpu_flag }} ${{ env.VERSION_FLAG }}
- name: Rename binary
run: mv zig-out/bin/lightpanda lightpanda-${{ env.ARCH }}-${{ env.OS }}
@@ -118,10 +118,10 @@ jobs:
arch: ${{env.ARCH}}
- name: v8 snapshot
run: zig build -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=ReleaseFast snapshot_creator -- src/snapshot.bin
run: zig build -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=fast snapshot_creator -- src/snapshot.bin
- name: zig build
run: zig build -Dsnapshot_path=../../snapshot.bin -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=ReleaseFast ${{ env.VERSION_FLAG }}
run: zig build -Dsnapshot_path=../../snapshot.bin -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=fast ${{ env.VERSION_FLAG }}
- name: Rename binary
run: mv zig-out/bin/lightpanda lightpanda-${{ env.ARCH }}-${{ env.OS }}
+1 -1
View File
@@ -37,7 +37,7 @@ jobs:
arch: ${{env.ARCH}}
- name: zig build release
run: zig build -Dwpt_extensions -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=ReleaseFast -Dcpu=generic
run: zig build -Dwpt_extensions -Dprebuilt_v8_path=v8/libc_v8.a -Doptimize=fast -Dcpu=generic
- name: upload artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+1 -1
View File
@@ -6,7 +6,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for how to open a pull request (CLA, dev
Run `make download-v8` once first: it fetches the prebuilt V8 archive into `.lp-cache/`, which `build.zig` picks up automatically. Without it every build compiles V8 from source (10+ minutes).
The C and Rust dependencies are built in ReleaseFast whatever `-Doptimize` is, so debug and release builds share them. Pass `ZIGFLAGS=-Ddebug_deps` to step into a dependency with a debugger.
The C and Rust dependencies are built with `-Doptimize=fast` whatever `-Doptimize` is, so debug and release builds share them. Pass `ZIGFLAGS=-Ddebug_deps` to step into a dependency with a debugger.
```bash
make test # Run all tests
+2 -2
View File
@@ -52,12 +52,12 @@ RUN case $TARGETPLATFORM in \
mv libc_v8.a v8/libc_v8.a
# build v8 snapshot
RUN zig build -Doptimize=ReleaseFast \
RUN zig build -Doptimize=fast \
-Dprebuilt_v8_path=v8/libc_v8.a \
snapshot_creator -- src/snapshot.bin
# build release
RUN zig build -Doptimize=ReleaseFast \
RUN zig build -Doptimize=fast \
-Dsnapshot_path=../../snapshot.bin \
-Dprebuilt_v8_path=v8/libc_v8.a \
-Dorderfile=orderfile/lightpanda.ld
+6 -13
View File
@@ -77,14 +77,7 @@ help:
# $(ZIG) commands
# ------------
.PHONY: build build-v8-snapshot build-dev download-v8 run run-release test bench data end2end clean zig-cache-tmp
# Zig 0.16.0 doesn't create <global cache>/tmp before fetching .zip deps.
# https://codeberg.org/ziglang/zig/issues/31964
zig-cache-tmp:
@dir=$$($(ZIG) env | sed -n 's/^ *\.global_cache_dir = "\(.*\)",$$/\1/p'); \
test -n "$$dir" || { printf "\033[31mCould not read global_cache_dir from '$(ZIG) env'\033[0m\n"; exit 1; }; \
mkdir -p "$$dir/tmp"
.PHONY: build build-v8-snapshot build-dev download-v8 run run-release test bench data end2end clean
## Download the prebuilt V8 libraries (skips the 10+ min source build)
download-v8:
@@ -105,19 +98,19 @@ ifeq ($(OS)_$(ARCH),linux_x86_64)
endif
## Build v8 snapshot
build-v8-snapshot: zig-cache-tmp
build-v8-snapshot:
@printf "\033[36mBuilding v8 snapshot (release safe)...\033[0m\n"
@$(ZIG) build $(ZIGFLAGS) -Doptimize=ReleaseFast snapshot_creator -- src/snapshot.bin || (printf "\033[31mBuild ERROR\033[0m\n"; exit 1;)
@$(ZIG) build $(ZIGFLAGS) -Doptimize=fast snapshot_creator -- src/snapshot.bin || (printf "\033[31mBuild ERROR\033[0m\n"; exit 1;)
@printf "\033[32mBuild OK\033[0m\n"
## Build in release-fast mode
build: build-v8-snapshot
@printf "\033[36mBuilding (release fast)...\033[0m\n"
@$(ZIG) build $(ZIGFLAGS) -Doptimize=ReleaseFast -Dsnapshot_path=../../snapshot.bin || (printf "\033[31mBuild ERROR\033[0m\n"; exit 1;)
@$(ZIG) build $(ZIGFLAGS) -Doptimize=fast -Dsnapshot_path=../../snapshot.bin || (printf "\033[31mBuild ERROR\033[0m\n"; exit 1;)
@printf "\033[32mBuild OK\033[0m\n"
## Build in debug mode
build-dev: zig-cache-tmp
build-dev:
@printf "\033[36mBuilding (debug)...\033[0m\n"
@$(ZIG) build $(ZIGFLAGS) || (printf "\033[31mBuild ERROR\033[0m\n"; exit 1;)
@printf "\033[32mBuild OK\033[0m\n"
@@ -132,7 +125,7 @@ run-debug: build-dev
@printf "\033[36mRunning...\033[0m\n"
@./zig-out/bin/lightpanda || (printf "\033[31mRun ERROR\033[0m\n"; exit 1;)
test: zig-cache-tmp
test:
TEST_FILTER="$(or $(F),$(TEST_FILTER))" $(ZIG) build $(ZIGFLAGS) test -freference-trace
## Run demo/runner end to end tests
+1 -1
View File
@@ -458,7 +458,7 @@ zig build -Dwpt_extensions run -- fetch \
it's useful to build in `releaseFast` mode to make tests faster.
```
zig build -Dwpt_extensions -Doptimize=ReleaseFast run -- serve ...
zig build -Dwpt_extensions -Doptimize=fast run -- serve ...
```
## Contributing
+73 -95
View File
@@ -18,6 +18,7 @@
const std = @import("std");
const builtin = @import("builtin");
const Translator = @import("translate_c").Translator;
const lightpanda_version = std.SemanticVersion.parse(@import("build.zig.zon").version) catch unreachable;
const min_zig_version = std.SemanticVersion.parse(@import("build.zig.zon").minimum_zig_version) catch unreachable;
@@ -44,18 +45,18 @@ pub fn build(b: *Build) !void {
const prebuilt_v8_path_option = b.option([]const u8, "prebuilt_v8_path", "Path to a prebuilt libc_v8.a or libc_v8.so");
const dev_fast = b.option(bool, "dev_fast", "Linux debug builds: shared V8 + self-hosted backend. Implies -Dshared_v8, -Duse_llvm=false and a bundled-CRT target") orelse
(builtin.os.tag == .linux and builtin.cpu.arch == .x86_64 and
optimize == .Debug and requested_target.query.isNative() and
(builtin.target.os.tag == .linux and builtin.target.cpu.arch == .x86_64 and
optimize == .debug and requested_target.query.isNative() and
!enable_tsan and !enable_asan and
(prebuilt_v8_path_option == null or std.mem.endsWith(u8, prebuilt_v8_path_option.?, ".so")));
if (dev_fast) {
if (builtin.os.tag != .linux) {
std.debug.print("-Ddev_fast is Linux-only (host is {s})\n", .{@tagName(builtin.os.tag)});
if (builtin.target.os.tag != .linux) {
std.debug.print("-Ddev_fast is Linux-only (host is {s})\n", .{@tagName(builtin.target.os.tag)});
return error.DevFastUnsupportedHost;
}
if (optimize != .Debug) {
std.debug.print("-Ddev_fast is Debug-only (optimize is {s})\n", .{@tagName(optimize)});
if (optimize != .debug) {
std.debug.print("-Ddev_fast is debug-only (optimize is {s})\n", .{@tagName(optimize)});
return error.DevFastRequiresDebug;
}
if (!requested_target.query.isNative()) {
@@ -75,10 +76,10 @@ pub fn build(b: *Build) !void {
// Dependencies never follow -Doptimize, and they build for the requested
// target rather than the dev_fast bundled-CRT query, so debug and release
// builds share one set of dependency objects in the cache.
const debug_deps = b.option(bool, "debug_deps", "Build the C and Rust dependencies in Debug instead of ReleaseFast") orelse false;
const debug_deps = b.option(bool, "debug_deps", "Build the C and Rust dependencies in debug instead of fast") orelse false;
const deps: Deps = .{
.target = requested_target,
.optimize = if (debug_deps) .Debug else .ReleaseFast,
.optimize = if (debug_deps) .debug else .fast,
};
// Without an explicit -Dprebuilt_v8_path, pick up whatever `make
@@ -118,7 +119,7 @@ pub fn build(b: *Build) !void {
const fmt_step = b.step("fmt", "Check code formatting");
const fmt = b.addFmt(.{
.paths = &.{ "src", "build.zig", "build.zig.zon" },
.paths = b.pathList(&.{ "src", "build.zig", "build.zig.zon" }),
.check = true,
});
fmt_step.dependOn(&fmt.step);
@@ -167,9 +168,7 @@ pub fn build(b: *Build) !void {
b.installArtifact(exe);
const run_cmd = b.addRunArtifact(exe);
if (b.args) |args| {
run_cmd.addArgs(args);
}
run_cmd.addPassthruArgs();
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
@@ -185,9 +184,7 @@ pub fn build(b: *Build) !void {
extras_step.dependOn(&b.addInstallArtifact(exe, .{}).step);
const run_cmd = b.addRunArtifact(exe);
if (b.args) |args| {
run_cmd.addArgs(args);
}
run_cmd.addPassthruArgs();
const run_step = b.step("snapshot_creator", "Generate a v8 snapshot");
run_step.dependOn(&run_cmd.step);
}
@@ -197,7 +194,7 @@ pub fn build(b: *Build) !void {
const exe = addExe(b, exe_config, "lightpanda-skills", "skills_check", "src/main_skills.zig");
const run_cmd = b.addRunArtifact(exe);
const out_dir = run_cmd.addOutputDirectoryArg("skills");
const out_dir = run_cmd.addOutputDirectoryArg2("skills", .{});
const install = b.addInstallDirectory(.{
.source_dir = out_dir,
.install_dir = .prefix,
@@ -222,14 +219,14 @@ pub fn build(b: *Build) !void {
const Deps = struct {
target: Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
optimize: std.lang.Optimize,
};
const ExeConfig = struct {
check: *Build.Step,
lightpanda_module: *Build.Module,
target: Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
optimize: std.lang.Optimize,
use_llvm: bool,
orderfile: ?[]const u8,
sanitize_c: ?std.zig.SanitizeC,
@@ -255,7 +252,7 @@ fn addExe(b: *Build, config: ExeConfig, name: []const u8, check_name: []const u8
if (config.orderfile) |path| {
// Per-function/per-datum sections exist only so the orderfile script
// can place individual hot functions; the self-hosted backend used by
// Debug builds does not support them on the C libraries, so they are
// debug builds does not support them on the C libraries, so they are
// gated on the orderfile being set (release/LLVM only).
exe.link_function_sections = true;
exe.link_data_sections = true;
@@ -282,9 +279,11 @@ fn devFastGlibcVersion(b: *Build) std.SemanticVersion {
/// it cannot drift from CI (the Makefile reads the same source of truth).
fn findPrebuiltV8(b: *Build, target: Build.ResolvedTarget, dev_fast: bool) ?[]const u8 {
const io = b.graph.io;
const action_path = ".github/actions/install/action.yml";
b.dependOnFileContents(b.path(action_path));
const action = std.Io.Dir.cwd().readFileAlloc(
io,
b.pathFromRoot(".github/actions/install/action.yml"),
rootPath(b, action_path),
b.allocator,
.limited(64 * 1024),
) catch return null;
@@ -294,7 +293,7 @@ fn findPrebuiltV8(b: *Build, target: Build.ResolvedTarget, dev_fast: bool) ?[]co
return null;
}
const cache_dir = b.pathFromRoot(".lp-cache");
const cache_dir = ".lp-cache";
// The .so must keep the name the exe's DT_NEEDED records; the archive
// name encodes V8 version, os and arch.
const path = if (dev_fast)
@@ -307,7 +306,8 @@ fn findPrebuiltV8(b: *Build, target: Build.ResolvedTarget, dev_fast: bool) ?[]co
@tagName(target.result.cpu.arch),
}) });
};
std.Io.Dir.cwd().access(io, path, .{}) catch {
b.dependOnFileMetadata(b.path(path));
std.Io.Dir.cwd().access(io, rootPath(b, path), .{}) catch {
std.debug.print("No prebuilt V8 at {s}; using the V8 source-build path. `make download-v8` fetches the prebuilt.\n", .{path});
return null;
};
@@ -315,6 +315,10 @@ fn findPrebuiltV8(b: *Build, target: Build.ResolvedTarget, dev_fast: bool) ?[]co
return path;
}
fn rootPath(b: *Build, sub_path: []const u8) []const u8 {
return b.root.joinString(b.allocator, sub_path) catch @panic("OOM");
}
/// Returns the quoted `default:` value of a top-level `key` in the install
/// action's yaml.
fn actionDefault(action: []const u8, key: []const u8) ?[]const u8 {
@@ -346,18 +350,18 @@ fn markHotSections(b: *Build, archive: Build.LazyPath) Build.LazyPath {
.root_module = b.createModule(.{
.root_source_file = b.path("orderfile/mark_hot_sections.zig"),
.target = b.graph.host,
.optimize = .ReleaseSafe,
.optimize = .safe,
}),
});
const run = b.addRunArtifact(tool);
run.addFileArg(archive);
run.addFileArg(b.path("orderfile/v8.txt"));
return run.addOutputFileArg("libc_v8.a");
return run.addOutputFileArg2("libc_v8.a", .{});
}
/// Per-function/per-datum sections let the -Dorderfile linker script place
/// individual hot functions. Only enabled for orderfile (release/LLVM) builds:
/// the self-hosted backend used by Debug builds fails to link the C libraries
/// the self-hosted backend used by debug builds fails to link the C libraries
/// with them.
fn sectionize(lib: *Build.Step.Compile, enabled: bool) *Build.Step.Compile {
if (enabled) {
@@ -384,7 +388,7 @@ fn linkV8(
.is_tsan = is_tsan,
.inspector_subtype = false,
.v8_enable_sandbox = is_tsan,
.cache_root = b.pathFromRoot(".lp-cache"),
.cache_root = rootPath(b, ".lp-cache"),
.prebuilt_v8_path = prebuilt_v8_path,
.shared_v8 = shared_v8,
});
@@ -393,7 +397,7 @@ fn linkV8(
fn linkRust(b: *Build, mod: *Build.Module, deps: Deps) void {
// Cargo's "dev" profile writes to target/debug.
const profile, const out_subdir = if (deps.optimize == .Debug) .{ "dev", "debug" } else .{ "release", "release" };
const profile, const out_subdir = if (deps.optimize == .debug) .{ "dev", "debug" } else .{ "release", "release" };
// One cargo workspace, one staticlib (src/rust/Cargo.toml explains why).
const exec_cargo = b.addSystemCommand(&.{
@@ -412,7 +416,7 @@ fn linkRust(b: *Build, mod: *Build.Module, deps: Deps) void {
_ = exec_cargo.captureStdErr(.{});
// TODO: We can prefer `--artifact-dir` once it become stable.
const out_dir = exec_cargo.addPrefixedOutputDirectoryArg("--target-dir=", "rust");
const out_dir = exec_cargo.addOutputDirectoryArg2("rust", .{ .prefix = "--target-dir=" });
const rust_step = b.step("rust", "Build the Rust staticlib (requires cargo)");
rust_step.dependOn(&exec_cargo.step);
@@ -425,14 +429,18 @@ fn linkRust(b: *Build, mod: *Build.Module, deps: Deps) void {
/// input of `run`, skipping the `skip_dir` subtree at any depth.
fn addDirInputs(b: *Build, run: *Build.Step.Run, root: []const u8, skip_dir: []const u8) !void {
const io = b.graph.io;
var dir = try b.build_root.handle.openDir(io, root, .{ .iterate = true });
var dir = try std.Io.Dir.cwd().openDir(io, rootPath(b, root), .{ .iterate = true });
defer dir.close(io);
b.dependOnDirectoryContents(b.path(root));
var walker = try dir.walk(b.allocator);
defer walker.deinit();
while (try walker.next(io)) |entry| {
switch (entry.kind) {
.directory => if (std.mem.eql(u8, entry.basename, skip_dir)) walker.leave(io),
.directory => if (std.mem.eql(u8, entry.basename, skip_dir))
walker.leave(io)
else
b.dependOnDirectoryContents(b.path(b.pathJoin(&.{ root, entry.path }))),
.file => run.addFileInput(b.path(b.pathJoin(&.{ root, entry.path }))),
else => {},
}
@@ -440,14 +448,12 @@ fn addDirInputs(b: *Build, run: *Build.Step.Run, root: []const u8, skip_dir: []c
}
fn linkSqlite(b: *Build, mod: *Build.Module, deps: Deps, enable_csan: ?std.zig.SanitizeC, is_tsan: bool, section: bool) void {
const dep = b.dependency("sqlite3", .{
.target = deps.target,
.optimize = deps.optimize,
});
const dep = b.dependency("sqlite3", .{});
const lib = sectionize(dep.artifact("sqlite3"), section);
lib.root_module.sanitize_c = enable_csan;
lib.root_module.sanitize_thread = is_tsan;
const lib_mod = cLibModule(b, deps.target, deps.optimize, is_tsan);
lib_mod.sanitize_c = enable_csan;
lib_mod.addCSourceFile(.{ .file = dep.path("sqlite3.c") });
const lib = sectionize(b.addLibrary(.{ .name = "sqlite3", .root_module = lib_mod }), section);
const macros = [_]struct { []const u8, []const u8 }{
.{ "SQLITE_DEFAULT_FILE_PERMISSIONS", "0600" },
@@ -489,12 +495,7 @@ fn linkSqlite(b: *Build, mod: *Build.Module, deps: Deps, enable_csan: ?std.zig.S
mod.linkLibrary(lib);
const translate_c = b.addTranslateC(.{
.root_source_file = lib.getEmittedIncludeTree().path(b, "sqlite3.h"),
.target = mod.resolved_target.?,
.optimize = mod.optimize.?,
});
mod.addImport("sqlite3", translate_c.createModule());
mod.addImport("sqlite3", translateC(b, mod, dep.path("sqlite3.h")).mod);
}
fn linkPcre2(b: *Build, mod: *Build.Module, deps: Deps, enable_csan: ?std.zig.SanitizeC, is_tsan: bool, section: bool) void {
@@ -509,13 +510,9 @@ fn linkPcre2(b: *Build, mod: *Build.Module, deps: Deps, enable_csan: ?std.zig.Sa
lib.root_module.sanitize_thread = is_tsan;
mod.linkLibrary(lib);
const translate_c = b.addTranslateC(.{
.root_source_file = lib.getEmittedIncludeTree().path(b, "pcre2.h"),
.target = mod.resolved_target.?,
.optimize = mod.optimize.?,
});
translate_c.defineCMacro("PCRE2_CODE_UNIT_WIDTH", "8");
mod.addImport("pcre2", translate_c.createModule());
const translator = translateC(b, mod, lib.getEmittedIncludeTree().path(b, "pcre2.h"));
translator.defineCMacro("PCRE2_CODE_UNIT_WIDTH", "8");
mod.addImport("pcre2", translator.mod);
}
fn linkCurl(b: *Build, mod: *Build.Module, deps: Deps, is_tsan: bool, section: bool) void {
@@ -523,13 +520,9 @@ fn linkCurl(b: *Build, mod: *Build.Module, deps: Deps, is_tsan: bool, section: b
mod.linkLibrary(curl);
const dep = b.dependency("curl", .{});
const translate_c = b.addTranslateC(.{
.root_source_file = dep.path("include/curl/curl.h"),
.target = mod.resolved_target.?,
.optimize = mod.optimize.?,
});
translate_c.addIncludePath(dep.path("include"));
mod.addImport("curl", translate_c.createModule());
const translator = translateC(b, mod, dep.path("include/curl/curl.h"));
translator.addIncludePath(dep.path("include"));
mod.addImport("curl", translator.mod);
const zlib = buildZlib(b, deps.target, deps.optimize, is_tsan, section);
curl.root_module.linkLibrary(zlib);
@@ -545,9 +538,7 @@ fn linkCurl(b: *Build, mod: *Build.Module, deps: Deps, is_tsan: bool, section: b
if (deps.target.result.os.tag == .macos) {
// needed for proxying on mac
const framework_path = if (b.sysroot) |sysroot|
b.pathJoin(&.{ sysroot, "System/Library/Frameworks" })
else if (b.graph.environ_map.get("SDKROOT")) |sdk_root|
const framework_path = if (b.graph.environ_map.get("SDKROOT")) |sdk_root|
b.pathJoin(&.{ sdk_root, "System/Library/Frameworks" })
else
"/System/Library/Frameworks";
@@ -557,7 +548,7 @@ fn linkCurl(b: *Build, mod: *Build.Module, deps: Deps, is_tsan: bool, section: b
}
}
fn cLibModule(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, is_tsan: bool) *Build.Module {
fn cLibModule(b: *Build, target: Build.ResolvedTarget, optimize: std.lang.Optimize, is_tsan: bool) *Build.Module {
return b.createModule(.{
.target = target,
.optimize = optimize,
@@ -566,7 +557,7 @@ fn cLibModule(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.Opt
});
}
fn buildZlib(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, is_tsan: bool, section: bool) *Build.Step.Compile {
fn buildZlib(b: *Build, target: Build.ResolvedTarget, optimize: std.lang.Optimize, is_tsan: bool, section: bool) *Build.Step.Compile {
const dep = b.dependency("zlib", .{});
const mod = cLibModule(b, target, optimize, is_tsan);
@@ -592,7 +583,7 @@ fn buildZlib(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.Opti
return lib;
}
fn buildBrotli(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, is_tsan: bool, section: bool) [3]*Build.Step.Compile {
fn buildBrotli(b: *Build, target: Build.ResolvedTarget, optimize: std.lang.Optimize, is_tsan: bool, section: bool) [3]*Build.Step.Compile {
const dep = b.dependency("brotli", .{});
const mod = cLibModule(b, target, optimize, is_tsan);
@@ -634,7 +625,7 @@ fn buildBrotli(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.Op
return .{ brotlicmn, brotlidec, brotlienc };
}
fn buildBoringSsl(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, section: bool) [2]*Build.Step.Compile {
fn buildBoringSsl(b: *Build, target: Build.ResolvedTarget, optimize: std.lang.Optimize, section: bool) [2]*Build.Step.Compile {
const dep = b.dependency("boringssl-zig", .{
.target = target,
.optimize = optimize,
@@ -650,7 +641,7 @@ fn buildBoringSsl(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin
return .{ ssl, crypto };
}
fn buildNghttp2(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, is_tsan: bool, section: bool) *Build.Step.Compile {
fn buildNghttp2(b: *Build, target: Build.ResolvedTarget, optimize: std.lang.Optimize, is_tsan: bool, section: bool) *Build.Step.Compile {
const dep = b.dependency("nghttp2", .{});
const mod = cLibModule(b, target, optimize, is_tsan);
@@ -696,7 +687,7 @@ fn buildNghttp2(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.O
fn buildCurl(
b: *Build,
target: Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
optimize: std.lang.Optimize,
is_tsan: bool,
section: bool,
) *Build.Step.Compile {
@@ -721,7 +712,7 @@ fn buildCurl(
const byte_size = struct {
fn it(b2: *std.Build, target2: Build.ResolvedTarget, name: []const u8, comptime ctype: std.Target.CType) []const u8 {
return b2.fmt("#define SIZEOF_{s} {d}", .{ name, target2.result.cTypeByteSize(ctype) });
return b2.fmt("#define SIZEOF_{s} {d}", .{ name, target2.result.cTypeByteSize(ctype).? });
}
}.it;
@@ -731,7 +722,6 @@ fn buildCurl(
.USE_NGHTTP2 = true,
.USE_OPENSSL = true,
.OPENSSL_IS_BORINGSSL = true,
.CURL_BORINGSSL_VERSION = null,
.CURL_CA_PATH = null,
.CURL_CA_BUNDLE = null,
@@ -751,11 +741,9 @@ fn buildCurl(
.CURL_DISABLE_LDAP = true,
.CURL_DISABLE_LDAPS = true,
.CURL_DISABLE_MQTT = true,
.CURL_DISABLE_NTLM = true,
.CURL_DISABLE_PROGRESS_METER = true,
.CURL_DISABLE_POP3 = true,
.CURL_DISABLE_RTSP = true,
.CURL_DISABLE_SMB = true,
.CURL_DISABLE_SMTP = true,
.CURL_DISABLE_TELNET = true,
.CURL_DISABLE_TFTP = true,
@@ -777,7 +765,6 @@ fn buildCurl(
.SIZEOF_INT_CODE = byte_size(b, target, "INT", .int),
.SIZEOF_LONG_CODE = byte_size(b, target, "LONG", .long),
.SIZEOF_LONG_LONG_CODE = byte_size(b, target, "LONG_LONG", .longlong),
.SIZEOF_OFF_T_CODE = byte_size(b, target, "OFF_T", .longlong),
.SIZEOF_CURL_OFF_T_CODE = byte_size(b, target, "CURL_OFF_T", .longlong),
@@ -805,8 +792,6 @@ fn buildCurl(
.HAVE_PWD_H = !is_windows,
.HAVE_STDATOMIC_H = true,
.HAVE_STDBOOL_H = true,
.HAVE_STDDEF_H = true,
.HAVE_STDINT_H = true,
.HAVE_STRINGS_H = true,
.HAVE_STROPTS_H = false,
.HAVE_SYS_EVENTFD_H = is_linux or is_freebsd or is_netbsd,
@@ -835,18 +820,15 @@ fn buildCurl(
.HAVE_BUILTIN_AVAILABLE = true,
.HAVE_CLOCK_GETTIME_MONOTONIC = !is_darwin and !is_windows,
.HAVE_CLOCK_GETTIME_MONOTONIC_RAW = is_linux,
.HAVE_FILE_OFFSET_BITS = true,
.HAVE_GETEUID = !is_windows,
.HAVE_GETPPID = !is_windows,
.HAVE_GETTIMEOFDAY = true,
.HAVE_GLIBC_STRERROR_R = is_gnu,
.HAVE_GMTIME_R = !is_windows,
.HAVE_LOCALTIME_R = !is_windows,
.HAVE_LONGLONG = !is_windows,
.HAVE_MACH_ABSOLUTE_TIME = is_darwin,
.HAVE_MEMRCHR = !is_darwin and !is_windows,
.HAVE_POSIX_STRERROR_R = !is_gnu and !is_windows,
.HAVE_PTHREAD_H = !is_windows,
.HAVE_THREADS_POSIX = !is_windows,
.HAVE_SETLOCALE = true,
.HAVE_SETRLIMIT = !is_windows,
@@ -854,12 +836,8 @@ fn buildCurl(
.HAVE_SIGINTERRUPT = !is_windows,
.HAVE_SIGNAL = true,
.HAVE_SIGSETJMP = !is_windows,
.HAVE_SIZEOF_SA_FAMILY_T = false,
.HAVE_SIZEOF_SUSECONDS_T = false,
.HAVE_SNPRINTF = true,
.HAVE_STRCASECMP = !is_windows,
.HAVE_STRCMPI = false,
.HAVE_STRDUP = true,
.HAVE_STRERROR_R = !is_windows,
.HAVE_STRICMP = false,
.HAVE_STRUCT_TIMEVAL = true,
@@ -867,8 +845,6 @@ fn buildCurl(
.HAVE_UTIME = true,
.HAVE_UTIMES = !is_windows,
.HAVE_WRITABLE_ARGV = !is_windows,
.HAVE__SETMODE = is_windows,
.USE_THREADS_POSIX = !is_windows,
.USE_RESOLV_THREADED = !is_windows,
// filesystem, network
@@ -885,16 +861,12 @@ fn buildCurl(
.HAVE_FSETXATTR = is_darwin or is_linux or is_netbsd,
.HAVE_FSETXATTR_5 = is_linux or is_netbsd,
.HAVE_FSETXATTR_6 = is_darwin,
.HAVE_FTRUNCATE = true,
.HAVE_GETADDRINFO = true,
.HAVE_GETADDRINFO_THREADSAFE = is_linux or is_freebsd or is_netbsd,
.HAVE_GETHOSTBYNAME_R = is_linux or is_freebsd,
.HAVE_GETHOSTBYNAME_R_3 = false,
.HAVE_GETHOSTBYNAME_R_3_REENTRANT = false,
.HAVE_GETHOSTBYNAME_R_5 = false,
.HAVE_GETHOSTBYNAME_R_5_REENTRANT = false,
.HAVE_GETHOSTBYNAME_R_6 = is_linux,
.HAVE_GETHOSTBYNAME_R_6_REENTRANT = is_linux,
.HAVE_GETHOSTNAME = true,
.HAVE_GETIFADDRS = if (is_windows) false else !is_android or target.result.os.versionRange().linux.android >= 24,
.HAVE_GETPASS_R = is_netbsd,
@@ -910,7 +882,6 @@ fn buildCurl(
.HAVE_IOCTLSOCKET_FIONBIO = is_windows,
.HAVE_IOCTL_FIONBIO = !is_windows,
.HAVE_IOCTL_SIOCGIFADDR = !is_windows,
.HAVE_MSG_NOSIGNAL = !is_windows,
.HAVE_OPENDIR = true,
.HAVE_PIPE = !is_windows,
.HAVE_PIPE2 = is_linux or is_freebsd or is_netbsd or is_openbsd,
@@ -922,9 +893,7 @@ fn buildCurl(
.HAVE_SEND = true,
.HAVE_SENDMMSG = !is_darwin and !is_windows,
.HAVE_SENDMSG = !is_windows,
.HAVE_SETMODE = !is_linux,
.HAVE_SETSOCKOPT_SO_NONBLOCK = false,
.HAVE_SOCKADDR_IN6_SIN6_ADDR = !is_windows,
.HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID = true,
.HAVE_SOCKET = true,
.HAVE_SOCKETPAIR = !is_windows,
@@ -1022,12 +991,17 @@ fn linkIsocline(b: *Build, mod: *Build.Module) void {
.file = dep.path("src/isocline.c"),
});
const translate_c = b.addTranslateC(.{
.root_source_file = dep.path("include/isocline.h"),
mod.addImport("isocline", translateC(b, mod, dep.path("include/isocline.h")).mod);
}
fn translateC(b: *Build, mod: *Build.Module, header: Build.LazyPath) Translator {
return .init(b.dependency("translate_c", .{}), .{
.c_source_file = header,
.target = mod.resolved_target.?,
.optimize = mod.optimize.?,
// Zig 0.16's built-in translate-c defaulted to this.
.default_init = true,
});
mod.addImport("isocline", translate_c.createModule());
}
/// Resolves the semantic version of the build.
@@ -1072,10 +1046,14 @@ fn resolveVersion(b: *std.Build) std.SemanticVersion {
}
fn runGit(b: *std.Build, args: []const []const u8) ![]const u8 {
var code: u8 = undefined;
// HEAD moves without touching any file the cache could track cheaply.
b.graph.poisonCache();
const command = try std.mem.concat(b.allocator, []const u8, &.{
&.{ "git", "-C", b.pathFromRoot(".") },
&.{ "git", "-C", rootPath(b, ".") },
args,
});
return b.runAllowFail(command, &code, .ignore);
return switch (b.runFallible(command, .{ .stderr_behavior = .ignore })) {
.success => |stdout| stdout,
else => error.GitFailed,
};
}
+13 -9
View File
@@ -2,11 +2,11 @@
.name = .browser,
.version = "1.1.0-dev",
.fingerprint = 0xda130f3af836cea0, // Changing this has security and trust implications.
.minimum_zig_version = "0.16.0",
.minimum_zig_version = "0.17.0",
.dependencies = .{
.v8 = .{
.url = "https://github.com/lightpanda-io/zig-v8-fork/archive/200123d4a5eee5136aa96a3933c651cc3d2c069f.tar.gz",
.hash = "v8-0.0.0-xddH67dJAwAdG6Puk7KzLsWsEzP8kqxLDfS-6tpkKS5B",
.url = "https://github.com/lightpanda-io/zig-v8-fork/archive/10f6a11e81fd4809988a24e3f5b3d5d4fd3a73fe.tar.gz",
.hash = "v8-0.0.0-xddH6w1QAwDcuJPtc73VXElNBsgPgrRBqQR__jWhy_C4",
},
// .v8 = .{ .path = "../zig-v8-fork" },
.brotli = .{
@@ -32,20 +32,24 @@
.hash = "N-V-__8AALJXUwHr71AwttzhEqqYTvFb_jz0EQ5Ou3OWpHBM",
},
.sqlite3 = .{
.url = "https://github.com/allyourcodebase/sqlite3/archive/7a615f5af79009cd733e3480658586d9b0d28b35.tar.gz",
.hash = "sqlite3-3.53.2-DMxLWuAOAAA_Px0arJOIOaP4AKEu5prbsQgPMA35W1zz",
.url = "https://sqlite.org/2026/sqlite-amalgamation-3530200.zip",
.hash = "N-V-__8AALc_rgC04POe18O0LKFRm4ouQpLtL3riscEmWp-Q",
},
.zenai = .{
.url = "git+https://github.com/lightpanda-io/zenai.git#b3386fde741d3d9f76674e4706ac1db27923c855",
.hash = "zenai-0.0.0-iOY_VJEhBwBj9qGuHOtw1TdQw0aiIbOZhXlybkSqhXKs",
.url = "git+https://github.com/lightpanda-io/zenai.git#63df800afc438c0e90603270a4696689eb339ccc",
.hash = "zenai-0.0.0-iOY_VEuBBwAanO_h2hbjC9Up37-a4pjYlViKeOYJLQjS",
},
.isocline = .{
.url = "git+https://github.com/arrufat/isocline#4a99434bee4a5ed04c1639514224d48cd55e1405",
.hash = "N-V-__8AAHxtEwB16xj2Xz-zx_uklGdTP5C2-GHXDoSzah-8",
},
.pcre2 = .{
.url = "https://github.com/PCRE2Project/pcre2/releases/download/pcre2-10.49/pcre2-10.49.tar.gz",
.hash = "pcre2-10.49.0-IZ6r68cregBKN199ndY6AaKNyKJpNmnQVSm8-h0gp4sj",
.url = "git+https://github.com/PCRE2Project/pcre2#9ad4a2e956876747a5022bf04614c7e39e71d4ec",
.hash = "pcre2-10.50.0-DEV-IZ6r6yDTrgCmI3jcBntPIdZLWLhNORGZZSi8hxQkc11x",
},
.translate_c = .{
.url = "git+https://codeberg.org/ziglang/translate-c?ref=2.0.0#0da7a16c3235b935b82421646076e0657cda21f6",
.hash = "translate_c-2.0.0-Q_BUWltOBwA0229IE8wwSxxGzOxyRso3hvcb1nN8oJX_",
},
},
.paths = .{""},
+1 -1
View File
@@ -59,7 +59,7 @@ below), with no change in run duration.
```bash
# root for /sys/kernel/debug/fault_around_bytes; ../demo checked out with
# `npm install` done; node, go, python3 and binutils on the PATH.
orderfile/tools/regen.sh -Doptimize=ReleaseFast -Dsnapshot_path=../../snapshot.bin -Dcpu=x86_64
orderfile/tools/regen.sh -Doptimize=fast -Dsnapshot_path=../../snapshot.bin -Dcpu=x86_64
```
The script
+1 -1
View File
@@ -123,7 +123,7 @@ fn rewriteArchive(gpa: Allocator, archive: []const u8, hot: *const HotSet, out:
out.appendSliceAssumeCapacity("!<arch>\n");
for (members.items) |m| {
var header = m.header.*;
_ = try std.fmt.bufPrint(header[48..58], "{d:<10}", .{m.body.len});
_ = try std.mem.print(header[48..58], "{d:<10}", .{m.body.len});
out.appendSliceAssumeCapacity(&header);
const start = out.items.len;
out.appendSliceAssumeCapacity(m.body);
+1 -1
View File
@@ -4,7 +4,7 @@
#
# usage: orderfile/tools/regen.sh [zig build args...]
# The build args are those of the release build minus -Dorderfile, e.g.
# -Doptimize=ReleaseFast -Dsnapshot_path=../../snapshot.bin -Dcpu=x86_64
# -Doptimize=fast -Dsnapshot_path=../../snapshot.bin -Dcpu=x86_64
#
# Needs root (sudo) for /sys/kernel/debug/fault_around_bytes, a checkout of
# lightpanda-io/demo (DEMO_DIR, npm install done), node, go, python3 and
+2 -2
View File
@@ -109,8 +109,8 @@ pub fn dupe(self: *Arena, comptime T: type, m: []const T) ![]T {
return self.allocator().dupe(T, m);
}
pub fn dupeZ(self: *Arena, comptime T: type, m: []const T) ![:0]T {
return self.allocator().dupeZ(T, m);
pub fn dupeSentinel(self: *Arena, comptime T: type, m: []const T, comptime sentinel: T) ![:sentinel]T {
return self.allocator().dupeSentinel(T, m, sentinel);
}
// Arena is being released. Account goes back to 0 (everything is being released)
+19 -19
View File
@@ -358,7 +358,7 @@ pub const AgentVerbosity = enum {
high,
pub fn atLeast(self: AgentVerbosity, min: AgentVerbosity) bool {
return @intFromEnum(self) >= @intFromEnum(min);
return @backingInt(self) >= @backingInt(min);
}
};
@@ -1038,7 +1038,7 @@ pub const HttpHeaders = struct {
languages: []const []const u8,
pub fn init(allocator: Allocator, value: []const u8) !AcceptLanguage {
const header = try allocator.dupeZ(u8, value);
const header = try allocator.dupeSentinel(u8, value, 0);
errdefer allocator.free(header);
var languages: std.ArrayList([]const u8) = .empty;
@@ -1067,15 +1067,15 @@ pub const HttpHeaders = struct {
pub fn init(allocator: Allocator, config: *const Config) !HttpHeaders {
const user_agent: [:0]const u8 = if (config.userAgent()) |ua|
try allocator.dupeZ(u8, ua)
try allocator.dupeSentinel(u8, ua, 0)
else if (config.userAgentSuffix()) |suffix|
try std.fmt.allocPrintSentinel(allocator, "{s} {s}", .{ user_agent_base, suffix }, 0)
try allocator.printSentinel("{s} {s}", .{ user_agent_base, suffix }, 0)
else
user_agent_base;
errdefer if (config.userAgent() != null or config.userAgentSuffix() != null) allocator.free(user_agent);
const proxy_bearer_header: ?[:0]const u8 = if (config.proxyBearerToken()) |token|
try std.fmt.allocPrintSentinel(allocator, "Proxy-Authorization: Bearer {s}", .{token}, 0)
try allocator.printSentinel("Proxy-Authorization: Bearer {s}", .{token}, 0)
else
null;
errdefer if (proxy_bearer_header) |hdr| allocator.free(hdr);
@@ -1133,7 +1133,7 @@ pub fn printUsageAndExit(self: *const Config, allocator: Allocator, help_for: Ru
\\{s}
\\
, .{Help.general});
break :text try std.fmt.allocPrint(allocator, template, .{exec_name});
break :text try allocator.print(template, .{exec_name});
},
inline .fetch, .serve, .mcp, .agent, .run => |tag| text: {
const template = comptimePrint(
@@ -1142,11 +1142,11 @@ pub fn printUsageAndExit(self: *const Config, allocator: Allocator, help_for: Ru
\\{s}
\\
, .{ @field(Help, @tagName(tag)), Help.common_options });
break :text try std.fmt.allocPrint(allocator, template, .{ exec_name, info_or_warn, pretty_or_logfmt });
break :text try allocator.print(template, .{ exec_name, info_or_warn, pretty_or_logfmt });
},
.version => text: {
const template = Help.version ++ "\n";
break :text try std.fmt.allocPrint(allocator, template, .{exec_name});
break :text try allocator.print(template, .{exec_name});
},
};
defer allocator.free(text);
@@ -1228,8 +1228,8 @@ pub fn parseArgs(allocator: Allocator, proc_args: std.process.Args) !Config {
}
// run's fields are a strict subset of Agent's (compile error otherwise).
var agent_opts: Agent = .{};
inline for (@typeInfo(@TypeOf(run)).@"struct".fields) |f| {
@field(agent_opts, f.name) = @field(run, f.name);
inline for (@typeInfo(@TypeOf(run)).@"struct".field_names) |field_name| {
@field(agent_opts, field_name) = @field(run, field_name);
}
command = .{ .agent = agent_opts };
}
@@ -1428,7 +1428,7 @@ test "Config: validateLocale" {
try std.testing.expectError(error.InvalidSubtag, validateLocale("en-U"));
try std.testing.expectError(error.InvalidSubtag, validateLocale("en-US-x-toolongsub"));
try std.testing.expectError(error.InvalidSubtag, validateLocale("en-U$"));
try std.testing.expectError(error.TooLong, validateLocale("en-" ++ "a" ** 40));
try std.testing.expectError(error.TooLong, validateLocale("en-" ++ string.repeat("a", 40)));
}
test "Config: validateTimezone" {
@@ -1438,7 +1438,7 @@ test "Config: validateTimezone" {
try std.testing.expectError(error.Empty, validateTimezone(""));
try std.testing.expectError(error.InvalidCharacter, validateTimezone("Europe/ Paris"));
try std.testing.expectError(error.InvalidCharacter, validateTimezone("UTC\n"));
try std.testing.expectError(error.TooLong, validateTimezone("a" ** 65));
try std.testing.expectError(error.TooLong, validateTimezone(string.repeat("a", 65)));
}
test "Config: HttpHeaders.acceptLanguageFor" {
@@ -1634,7 +1634,7 @@ pub fn validateUserAgent(ua: []const u8) !void {
}
}
if (std.ascii.indexOfIgnoreCase(ua, "mozilla") != null) {
if (std.ascii.findIgnoreCase(ua, "mozilla") != null) {
return error.Reserved;
}
}
@@ -1645,7 +1645,7 @@ fn localeValidator(allocator: Allocator, args: *std.process.Args.Iterator, field
log.fatal(.app, "invalid option value", .{ .arg = "--locale", .value = str, .err = err, .hint = "must be a BCP 47 tag such as en-US, de or zh-Hant-TW" });
return error.InvalidArgument;
};
field.* = try allocator.dupeZ(u8, str);
field.* = try allocator.dupeSentinel(u8, str, 0);
}
fn timezoneValidator(allocator: Allocator, args: *std.process.Args.Iterator, field: *?[:0]const u8) !void {
@@ -1654,7 +1654,7 @@ fn timezoneValidator(allocator: Allocator, args: *std.process.Args.Iterator, fie
log.fatal(.app, "invalid option value", .{ .arg = "--timezone", .value = str, .err = err, .hint = "must be an IANA time zone such as Europe/Paris or UTC" });
return error.InvalidArgument;
};
field.* = try allocator.dupeZ(u8, str);
field.* = try allocator.dupeSentinel(u8, str, 0);
}
/// A BCP 47 tag restricted to what ICU and the Accept-Language derivation
@@ -1708,8 +1708,8 @@ pub const tagNames = cli.tagNames;
/// `<a|b|c>` ghost-text hint built from the same enum's tag names.
pub fn tagHint(comptime E: type) []const u8 {
var s: []const u8 = "<";
for (@typeInfo(E).@"enum".fields, 0..) |f, i| {
s = s ++ (if (i == 0) f.name else "|" ++ f.name);
for (@typeInfo(E).@"enum".field_names, 0..) |field_name, i| {
s = s ++ (if (i == 0) field_name else "|" ++ field_name);
}
return s ++ ">";
}
@@ -1717,8 +1717,8 @@ pub fn tagHint(comptime E: type) []const u8 {
/// JSON array `["a","b","c"]` representation of the enum tag names.
pub fn tagJsonArray(comptime E: type) []const u8 {
var s: []const u8 = "[";
for (@typeInfo(E).@"enum".fields, 0..) |f, i| {
s = s ++ (if (i == 0) "\"" else ",\"") ++ f.name ++ "\"";
for (@typeInfo(E).@"enum".field_names, 0..) |field_name, i| {
s = s ++ (if (i == 0) "\"" else ",\"") ++ field_name ++ "\"";
}
return s ++ "]";
}
+2 -2
View File
@@ -151,8 +151,8 @@ fn _write(self: *const Metrics, writer: *std.Io.Writer) !void {
"# TYPE build_info gauge\nbuild_info{{version=\"{s}\"}} 1\n",
.{lp.build_config.version},
);
inline for (@typeInfo(Metrics).@"struct".fields) |f| {
try @field(self, f.name).write(f.name, @field(help, f.name), writer);
inline for (@typeInfo(Metrics).@"struct".field_names) |field_name| {
try @field(self, field_name).write(field_name, @field(help, field_name), writer);
}
}
+1 -6
View File
@@ -484,12 +484,7 @@ pub fn dispatch(self: *Notification, comptime event: EventType, data: ArgType(ev
// Given an event type enum, returns the type of arg the event emits
pub fn ArgType(comptime event: Notification.EventType) type {
inline for (std.meta.fields(Notification.Events)) |f| {
if (std.mem.eql(u8, f.name, @tagName(event))) {
return f.type;
}
}
unreachable;
return @FieldType(Notification.Events, @tagName(event));
}
// Given an event type enum, returns the listening function type
+1 -1
View File
@@ -43,7 +43,7 @@ free_lists: std.array_hash_map.Custom(Class, ?[*]u8, Class.Context, false) = .em
// class, e.g. a 34-byte string and a 40-byte node recycle each other.
const Class = packed struct(u64) {
alignment: Alignment,
size: std.meta.Int(.unsigned, 64 - @bitSizeOf(Alignment)),
size: @Int(.unsigned, 64 - @bitSizeOf(Alignment)),
fn of(len: usize, alignment: Alignment) Class {
const class_alignment = Alignment.max(alignment, .of(usize));
+6 -4
View File
@@ -25,6 +25,7 @@
const std = @import("std");
const pcre2 = @import("pcre2");
const repeat = @import("string.zig").repeat;
const Allocator = std.mem.Allocator;
@@ -171,8 +172,9 @@ const MATCH_SCRATCH = 24 * 1024;
/// Whether the pattern matches anywhere in `text`, as `RegExp.test` would
/// answer. A match that hits the backtracking limits counts as no match.
pub fn matches(self: Regex, text: []const u8) bool {
var scratch = std.heap.stackFallback(MATCH_SCRATCH, self.context.allocator);
var allocator = scratch.get();
var scratch_buf: [MATCH_SCRATCH]u8 = undefined;
var scratch: std.heap.BufferFirstAllocator = .init(&scratch_buf, self.context.allocator);
var allocator = scratch.allocator();
const general = pcre2.pcre2_general_context_create_8(Context.cMalloc, Context.cFree, &allocator) orelse return false;
defer pcre2.pcre2_general_context_free_8(general);
@@ -236,9 +238,9 @@ test "Regex: invalid patterns are errors, runaway ones no match" {
// the caller.
const runaway = try context.compile("^(a+)+$", .{ .case_insensitive = true }, null);
defer runaway.deinit();
const subject = "a" ** 64 ++ "b";
const subject = repeat("a", 64) ++ "b";
try testing.expect(!runaway.matches(subject));
try testing.expect(runaway.matches("a" ** 64));
try testing.expect(runaway.matches(repeat("a", 64)));
}
test "Regex: dot_all and multiline follow the JavaScript flags" {
+1 -1
View File
@@ -385,7 +385,7 @@ const JsonVisitor = struct {
try self.jw.beginObject();
try self.jw.objectField("nodeId");
try self.jw.write(try std.fmt.allocPrint(self.tree.arena, "{d}", .{data.id}));
try self.jw.write(try self.tree.arena.print("{d}", .{data.id}));
try self.jw.objectField("backendDOMNodeId");
try self.jw.write(data.id);
+1 -1
View File
@@ -129,7 +129,7 @@ fn sighandle(self: *SigHandler) noreturn {
std.process.exit(1);
}
switch (@as(std.posix.SIG, @enumFromInt(sig))) {
switch (@as(std.posix.SIG, @fromBackingInt(@intCast(sig)))) {
.INT, .TERM => {
self.mutex.lockUncancelable(lp.io);
if (self.attempt > 1 and !self.no_hard_exit) {
+1 -1
View File
@@ -145,7 +145,7 @@ fn handleClient(client: posix.socket_t) void {
// Send upgrade response
var resp_buf: [256]u8 = undefined;
const resp = std.fmt.bufPrint(&resp_buf, "HTTP/1.1 101 Switching Protocols\r\n" ++
const resp = std.mem.print(&resp_buf, "HTTP/1.1 101 Switching Protocols\r\n" ++
"Upgrade: websocket\r\n" ++
"Connection: Upgrade\r\n" ++
"Sec-WebSocket-Accept: {s}\r\n\r\n", .{accept_key}) catch return;
+16 -15
View File
@@ -55,8 +55,8 @@ const UserError = error{
};
pub fn isUserError(err: anyerror) bool {
inline for (@typeInfo(UserError).error_set.?) |e| {
if (err == @field(anyerror, e.name)) return true;
inline for (@typeInfo(UserError).error_set.error_names.?) |name| {
if (err == @field(anyerror, name)) return true;
}
return false;
}
@@ -194,7 +194,7 @@ api_error_buf: [512]u8 = undefined,
api_error_detail: ?[]const u8 = null,
pub fn init(allocator: std.mem.Allocator, app: *App, opts: Config.Agent) !*Agent {
var providers_buf: [@typeInfo(Config.AiProvider).@"enum".fields.len]Candidate = undefined;
var providers_buf: [@typeInfo(Config.AiProvider).@"enum".field_names.len]Candidate = undefined;
const found_providers = settings.availableProviders(&providers_buf);
const available_providers = try allocator.alloc([]const u8, found_providers.len);
for (found_providers, 0..) |f, i| {
@@ -233,8 +233,9 @@ pub fn init(allocator: std.mem.Allocator, app: *App, opts: Config.Agent) !*Agent
// Load remembered selection up front so a saved null provider can flip the
// REPL into basic mode before resolution. Pure script runs need nothing.
const remembered: ?settings.Remembered = if (will_repl or is_one_shot) settings.loadRemembered(allocator) else null;
defer if (remembered) |r| std.zon.parse.free(allocator, r);
var remembered_arena: std.heap.ArenaAllocator = .init(allocator);
defer remembered_arena.deinit();
const remembered: ?settings.Remembered = if (will_repl or is_one_shot) settings.loadRemembered(allocator, remembered_arena.allocator()) else null;
// A remembered null provider means the user disabled the LLM via
// `/provider null`; honor it for the REPL only (one-shot --task and script
@@ -645,7 +646,7 @@ fn runRepl(self: *Agent) void {
self.terminal.printError("{s}", .{switch (err) {
error.OutOfMemory => "out of memory",
error.FrameNotLoaded => "no page loaded — run /goto <url> first (Esc exits JS mode)",
else => std.fmt.allocPrint(aa, "evaluate failed: {s}", .{@errorName(err)}) catch "evaluate failed",
else => aa.print("evaluate failed: {s}", .{@errorName(err)}) catch "evaluate failed",
}});
continue :repl;
};
@@ -699,7 +700,7 @@ fn runRepl(self: *Agent) void {
.comment => continue :repl,
.llm => |lc| {
var label_buf: [32]u8 = undefined;
const label = std.fmt.bufPrint(&label_buf, "/{s}", .{@tagName(lc)}) catch "/?";
const label = std.mem.print(&label_buf, "/{s}", .{@tagName(lc)}) catch "/?";
if (!self.requireLlm(label)) continue :repl;
_ = self.runTurn(.{ .prompt = lc.prompt(), .record_comment = line, .capture_for_save = true, .label = label });
},
@@ -1008,7 +1009,7 @@ fn subscriptionLogin(self: *Agent, desc: *const auth.Descriptor) ?auth.Session {
fn promptStoredSubscription(self: *Agent, desc: *const auth.Descriptor, stored: auth.Session) ?auth.Session {
var session = stored;
var header_buf: [128]u8 = undefined;
const header = std.fmt.bufPrint(&header_buf, "Already logged in with your {s}. Pick:", .{desc.label}) catch
const header = std.mem.print(&header_buf, "Already logged in with your {s}. Pick:", .{desc.label}) catch
"Already logged in. Pick:";
const idx = picker.promptNumberedChoice(header, &.{
"keep — use the stored login",
@@ -1167,7 +1168,7 @@ fn handleSave(self: *Agent, arena: std.mem.Allocator, rest: []const u8) void {
fn promptSaveMode(self: *Agent, path: []const u8) ?save.Mode {
var header_buf: [256]u8 = undefined;
const header = std.fmt.bufPrint(&header_buf, "{s} already exists. Pick save mode:", .{path}) catch
const header = std.mem.print(&header_buf, "{s} already exists. Pick save mode:", .{path}) catch
"File already exists. Pick save mode:";
const with_llm = self.ai_client != null;
const modes: []const save.Mode = if (with_llm)
@@ -1507,7 +1508,7 @@ fn runCommand(self: *Agent, arena: std.mem.Allocator, tc: Command.ToolCall) brow
.text = switch (err) {
error.OutOfMemory => "out of memory",
error.FrameNotLoaded => "no page loaded — run /goto <url> first",
else => std.fmt.allocPrint(arena, "{s} failed: {s}", .{ tc.name(), browser_tools.errorMessage(err) }) catch "tool failed",
else => arena.print("{s} failed: {s}", .{ tc.name(), browser_tools.errorMessage(err) }) catch "tool failed",
},
.is_error = true,
};
@@ -1634,7 +1635,7 @@ fn recordSlashToolCall(
const tool_calls = try ma.alloc(zenai.provider.ToolCall, 1);
tool_calls[0] = .{
.id = try std.fmt.allocPrint(ma, "lp-slash-{d}", .{self.synthetic_tool_call_id}),
.id = try ma.print("lp-slash-{d}", .{self.synthetic_tool_call_id}),
.name = try ma.dupe(u8, tool_name),
.arguments = if (args) |v| try zenai.json.dupeValue(ma, v) else null,
};
@@ -1685,9 +1686,9 @@ fn formatApiError(self: *Agent, client: zenai.provider.Client, err: anyerror) []
else
"";
if (e.message) |m| {
if (std.fmt.bufPrint(&self.api_error_buf, "HTTP {d} — {s}{s}", .{ status, m, hint })) |s| return s else |_| {}
if (std.mem.print(&self.api_error_buf, "HTTP {d} — {s}{s}", .{ status, m, hint })) |s| return s else |_| {}
}
return std.fmt.bufPrint(&self.api_error_buf, "HTTP {d}{s}", .{ status, hint }) catch @errorName(err);
return std.mem.print(&self.api_error_buf, "HTTP {d}{s}", .{ status, hint }) catch @errorName(err);
}
/// Returned text lives in `conversation.arena`, valid only until the next prune.
@@ -1932,7 +1933,7 @@ fn capToolOutput(allocator: std.mem.Allocator, tool_name: []const u8, output: []
if (output.len <= cap) return output;
const prefix = string.truncateUtf8(output, cap);
var suffix_buf: [128]u8 = undefined;
const suffix = std.fmt.bufPrint(&suffix_buf, "\n...[truncated, original {d} bytes — re-read scoped (selector/backendNodeId)]", .{output.len}) catch return prefix;
const suffix = std.mem.print(&suffix_buf, "\n...[truncated, original {d} bytes — re-read scoped (selector/backendNodeId)]", .{output.len}) catch return prefix;
return std.mem.concat(allocator, u8, &.{ prefix, suffix }) catch prefix;
}
@@ -1954,7 +1955,7 @@ fn handleToolCall(ctx: *anyopaque, allocator: std.mem.Allocator, tool_name: []co
var selector: ?[]const u8 = null;
const outcome = self.toolOutcome(allocator, tool_name, arguments, &selector) catch |err| zenai.provider.Client.ToolHandler.Result{
.content = std.fmt.allocPrint(allocator, "Error: {s}", .{browser_tools.errorMessage(err)}) catch "Error: tool execution failed",
.content = allocator.print("Error: {s}", .{browser_tools.errorMessage(err)}) catch "Error: tool execution failed",
.is_error = true,
};
if (self.capturing_for_save) {
+1 -1
View File
@@ -151,7 +151,7 @@ test "expireImages keeps the newest images and annotates the rest" {
const image = [_]zenai.provider.ContentPart{.{ .image = .{ .data = "AAAA", .mime_type = "image/png" } }};
for (0..4) |n| {
const results = try a.alloc(zenai.provider.ToolResult, 1);
results[0] = .{ .id = "c", .name = "screenshot", .content = try std.fmt.allocPrint(a, "shot {d}", .{n}), .parts = &image };
results[0] = .{ .id = "c", .name = "screenshot", .content = try a.print("shot {d}", .{n}), .parts = &image };
try conv.messages.append(std.testing.allocator, .{ .role = .tool, .tool_results = results });
}
+3 -3
View File
@@ -140,7 +140,7 @@ pub fn stop(self: *Spinner) void {
const elapsed_s = @as(f64, @floatFromInt(elapsed_ms)) / @as(f64, std.time.ms_per_s);
var buf: [frame_buf_bytes]u8 = undefined;
const summary = std.fmt.bufPrint(
const summary = std.mem.print(
&buf,
"\r" ++ clear_eol ++ ansi.dim ++ "[agent: worked for {d:.1}s · {d} tool call{s}]" ++ ansi.reset ++ "\n",
.{ elapsed_s, self.tool_calls, if (self.tool_calls == 1) "" else "s" },
@@ -275,7 +275,7 @@ fn renderLocked(self: *Spinner) void {
const glyph = braille[self.frame % braille.len];
const written = switch (self.state) {
.idle => return,
.thinking => std.fmt.bufPrint(
.thinking => std.mem.print(
&buf,
"\r" ++ ansi.yellow ++ "{s}" ++ ansi.reset ++ " " ++ ansi.dim ++ "[agent: thinking]" ++ ansi.reset ++ clear_eol,
.{glyph},
@@ -297,7 +297,7 @@ fn renderLocked(self: *Spinner) void {
const cap = @min(max_args_cells, room);
const cut = truncToCells(all_args, cap);
const suffix: []const u8 = if (cut < all_args.len) ellipsis else "";
break :blk std.fmt.bufPrint(
break :blk std.mem.print(
&buf,
"\r" ++ ansi.yellow ++ "{s}" ++ ansi.reset ++ " " ++ ansi.dim ++ "[{s}{s} {s}{s}]" ++ ansi.reset ++ clear_eol,
.{ glyph, prefix, name, all_args[0..cut], suffix },
+3 -3
View File
@@ -141,7 +141,7 @@ fn emitStderr(self: *Terminal, bytes: []const u8) void {
fn formatBulletLine(arena: std.mem.Allocator, name: []const u8, args: []const u8, ok: bool) ![]const u8 {
const bullet_color = if (ok) ansi.green else ansi.red;
return std.fmt.allocPrint(arena, bullet_line_fmt, .{ bullet_color, ansi.reset, ansi.dim, name, ansi.reset, args });
return arena.print(bullet_line_fmt, .{ bullet_color, ansi.reset, ansi.dim, name, ansi.reset, args });
}
pub fn setIdleCallback(fun: ?*const c.ic_idle_fun_t, arg: ?*anyopaque) void {
@@ -307,7 +307,7 @@ pub fn printToolOutcome(self: *Terminal, name: []const u8, text: []const u8, is_
pub fn printScriptDone(self: *Terminal, name: []const u8, args: []const u8) void {
if (!self.spinner.isEnabled()) return;
var buf: [256]u8 = undefined;
const line = std.fmt.bufPrint(
const line = std.mem.print(
&buf,
ansi.green ++ "●" ++ ansi.reset ++ " " ++ ansi.dim ++ "[{s} {s}]" ++ ansi.reset ++ "\n",
.{ name, args },
@@ -354,7 +354,7 @@ pub fn printWarning(self: *Terminal, comptime fmt: []const u8, args: anytype) vo
fn printSeverity(self: *Terminal, color: []const u8, label: []const u8, comptime fmt: []const u8, args: anytype) void {
if (self.repl_arena) |*a| {
defer _ = a.reset(.retain_capacity);
const bytes = std.fmt.allocPrint(a.allocator(), "{s}●{s} " ++ fmt ++ "\n", .{ color, ansi.reset } ++ args) catch return;
const bytes = a.allocator().print("{s}●{s} " ++ fmt ++ "\n", .{ color, ansi.reset } ++ args) catch return;
return self.emitStderr(bytes);
}
std.debug.print("{s}{s}{s}: " ++ fmt ++ "{s}\n", .{ ansi.bold, color, label } ++ args ++ .{ansi.reset});
+1 -1
View File
@@ -50,7 +50,7 @@ pub const TokenSet = struct {
account_id: ?[]const u8 = null,
pub fn dup(allocator: std.mem.Allocator, access: []const u8, refresh: []const u8, expires_at_ms: i64, account_id: ?[]const u8) !TokenSet {
const a = try allocator.dupeZ(u8, access);
const a = try allocator.dupeSentinel(u8, access, 0);
errdefer allocator.free(a);
const r = try allocator.dupe(u8, refresh);
errdefer allocator.free(r);
+9 -9
View File
@@ -108,13 +108,13 @@ fn parseTokenResponse(allocator: std.mem.Allocator, body: []const u8) !auth.Toke
}
fn refreshBody(arena: std.mem.Allocator, refresh_token: []const u8) ![]u8 {
return std.fmt.allocPrint(arena, "grant_type=refresh_token&client_id=" ++ client_id ++ "&refresh_token={s}", .{
return arena.print("grant_type=refresh_token&client_id=" ++ client_id ++ "&refresh_token={s}", .{
try lp.URL.percentEncodeSegment(arena, refresh_token, .component),
});
}
fn exchangeBody(arena: std.mem.Allocator, code: []const u8, code_verifier: []const u8) ![]u8 {
return std.fmt.allocPrint(arena, "grant_type=authorization_code&client_id=" ++ client_id ++
return arena.print("grant_type=authorization_code&client_id=" ++ client_id ++
"&redirect_uri=" ++ device_redirect_uri ++ "&code={s}&code_verifier={s}", .{
try lp.URL.percentEncodeSegment(arena, code, .component),
try lp.URL.percentEncodeSegment(arena, code_verifier, .component),
@@ -155,7 +155,7 @@ fn deviceLogin(allocator: std.mem.Allocator, interrupt: ?*zenai.http.Interrupt)
const code_res = try post(a, interrupt, device_code_url, "application/json", "{\"client_id\":\"" ++ client_id ++ "\"}");
if (code_res.status != .ok) {
log.warn(.app, "codex device-code failed", .{ .status = @intFromEnum(code_res.status), .body = code_res.body });
log.warn(.app, "codex device-code failed", .{ .status = @backingInt(code_res.status), .body = code_res.body });
return error.DeviceCodeRequestFailed;
}
const dc = try std.json.parseFromSliceLeaky(DeviceCode, a, code_res.body, .{ .ignore_unknown_fields = true });
@@ -166,7 +166,7 @@ fn deviceLogin(allocator: std.mem.Allocator, interrupt: ?*zenai.http.Interrupt)
.{ verify_url, dc.user_code },
);
const poll_body = try std.fmt.allocPrint(a, "{f}", .{std.json.fmt(
const poll_body = try a.print("{f}", .{std.json.fmt(
.{ .device_auth_id = dc.device_auth_id, .user_code = dc.user_code },
.{},
)});
@@ -178,7 +178,7 @@ fn deviceLogin(allocator: std.mem.Allocator, interrupt: ?*zenai.http.Interrupt)
// Still pending — the user hasn't finished authorizing.
.forbidden, .not_found => continue,
else => {
log.warn(.app, "codex device-auth poll failed", .{ .status = @intFromEnum(res.status), .body = res.body });
log.warn(.app, "codex device-auth poll failed", .{ .status = @backingInt(res.status), .body = res.body });
return error.DeviceAuthFailed;
},
}
@@ -187,7 +187,7 @@ fn deviceLogin(allocator: std.mem.Allocator, interrupt: ?*zenai.http.Interrupt)
const exchange = try exchangeBody(a, dt.authorization_code, dt.code_verifier);
const tok_res = try post(a, interrupt, token_url, "application/x-www-form-urlencoded", exchange);
if (tok_res.status != .ok) {
log.warn(.app, "codex token exchange failed", .{ .status = @intFromEnum(tok_res.status), .body = tok_res.body });
log.warn(.app, "codex token exchange failed", .{ .status = @backingInt(tok_res.status), .body = tok_res.body });
return error.TokenExchangeFailed;
}
return parseTokenResponse(allocator, tok_res.body);
@@ -200,7 +200,7 @@ fn refreshGrant(allocator: std.mem.Allocator, refresh_token: []const u8) !auth.T
const body = try refreshBody(a, refresh_token);
const res = try post(a, null, token_url, "application/x-www-form-urlencoded", body);
if (res.status != .ok) {
log.warn(.app, "codex token refresh failed", .{ .status = @intFromEnum(res.status), .body = res.body });
log.warn(.app, "codex token refresh failed", .{ .status = @backingInt(res.status), .body = res.body });
return error.RefreshFailed;
}
return parseTokenResponse(allocator, res.body);
@@ -233,7 +233,7 @@ fn makeJwt(arena: std.mem.Allocator, payload_json: []const u8) ![]const u8 {
const enc = std.base64.url_safe_no_pad.Encoder;
const p = try arena.alloc(u8, enc.calcSize(payload_json.len));
_ = enc.encode(p, payload_json);
return std.fmt.allocPrint(arena, "aGVhZGVy.{s}.c2ln", .{p});
return arena.print("aGVhZGVy.{s}.c2ln", .{p});
}
test "accountIdFromJwt: top-level chatgpt_account_id" {
@@ -281,7 +281,7 @@ test "parseTokenResponse derives account id and absolute expiry" {
defer arena.deinit();
const a = arena.allocator();
const jwt = try makeJwt(a, "{\"chatgpt_account_id\":\"acct-x\"}");
const body = try std.fmt.allocPrint(a, "{{\"access_token\":\"acc\",\"refresh_token\":\"ref\",\"id_token\":\"{s}\",\"expires_in\":3600}}", .{jwt});
const body = try a.print("{{\"access_token\":\"acc\",\"refresh_token\":\"ref\",\"id_token\":\"{s}\",\"expires_in\":3600}}", .{jwt});
const tokens = try parseTokenResponse(std.testing.allocator, body);
defer tokens.deinit(std.testing.allocator);
try std.testing.expectEqualStrings("acc", tokens.access_token);
+1 -1
View File
@@ -51,7 +51,7 @@ const Cache = struct {
};
fn cachePath(arena: std.mem.Allocator, app_dir: []const u8, provider_id: []const u8) ![]const u8 {
const name = try std.fmt.allocPrint(arena, "models-dev-{s}.json", .{provider_id});
const name = try arena.print("models-dev-{s}.json", .{provider_id});
return std.fs.path.join(arena, &.{ app_dir, name });
}
+13 -12
View File
@@ -19,6 +19,7 @@
const std = @import("std");
const ansi = @import("ansi.zig");
const js_highlight = @import("js_highlight.zig");
const repeat = @import("../string.zig").repeat;
/// Render markdown `src` as ANSI-styled terminal output to `w`. Tables are
/// aligned straight from `src`, so unlike `Stream` there is no size limit.
@@ -470,7 +471,7 @@ fn renderLine(w: *std.Io.Writer, line: []const u8, js: ?*js_highlight.State) !vo
// Dashed, unlike the solid fence rules, so adjacent ones read differently.
if (isHorizontalRule(trimmed)) {
try styled(w, "┄" ** rule_width, ansi.dim);
try styled(w, repeat("┄", rule_width), ansi.dim);
return;
}
@@ -729,8 +730,8 @@ test "md_term: nested inline styles" {
);
}
const open_rule = "\x1b[2m╭" ++ "─" ** 23 ++ "\x1b[0m";
const close_rule = "\x1b[2m╰" ++ "─" ** 23 ++ "\x1b[0m";
const open_rule = "\x1b[2m╭" ++ repeat("─", 23) ++ "\x1b[0m";
const close_rule = "\x1b[2m╰" ++ repeat("─", 23) ++ "\x1b[0m";
test "md_term: fenced code block is highlighted as JavaScript" {
try expectRender(
@@ -743,13 +744,13 @@ test "md_term: fenced code block is highlighted as JavaScript" {
test "md_term: fence rules carry the language tag" {
try expectRender(
"\x1b[2m╭─ js " ++ "─" ** 18 ++ "\x1b[0m\n\nx\n\n" ++ close_rule,
"\x1b[2m╭─ js " ++ repeat("─", 18) ++ "\x1b[0m\n\nx\n\n" ++ close_rule,
"```js\nx\n```",
);
// An overlong info string doesn't fit the rule and is dropped.
try expectRender(
open_rule ++ "\n\nx\n\n" ++ close_rule,
"```" ++ "x" ** 20 ++ "\nx\n```",
"```" ++ repeat("x", 20) ++ "\nx\n```",
);
}
@@ -791,8 +792,8 @@ test "md_term: blockquote" {
}
test "md_term: horizontal rule" {
try expectRender("\x1b[2m" ++ "┄" ** 24 ++ "\x1b[0m", "---");
try expectRender("\x1b[2m" ++ "┄" ** 24 ++ "\x1b[0m", "***");
try expectRender("\x1b[2m" ++ repeat("┄", 24) ++ "\x1b[0m", "---");
try expectRender("\x1b[2m" ++ repeat("┄", 24) ++ "\x1b[0m", "***");
try expectRender("---x", "---x");
}
@@ -828,7 +829,7 @@ test "md_term: pipe rows without a separator pass through" {
try expectRender("| a |\n", "| a |\n");
}
const big_table = "| A | B |\n|-|-|\n" ++ ("| " ++ "a" ** 16 ++ " | " ++ "b" ** 16 ++ " |\n") ** 500;
const big_table = "| A | B |\n|-|-|\n" ++ repeat("| " ++ repeat("a", 16) ++ " | " ++ repeat("b", 16) ++ " |\n", 500);
test "md_term: batch aligns tables beyond the stream table buffer" {
try testing.expect(big_table.len > table_buf_len);
@@ -839,8 +840,8 @@ test "md_term: batch aligns tables beyond the stream table buffer" {
const pipe = "\x1b[2m│\x1b[0m";
try testing.expectEqual(0, std.mem.count(u8, out, "| a"));
try testing.expectEqual(500, std.mem.count(u8, out, pipe ++ " " ++ "a" ** 16 ++ " " ++ pipe));
try testing.expect(std.mem.indexOf(u8, out, "\x1b[2m├" ++ "─" ** 18 ++ "┼" ++ "─" ** 18 ++ "┤\x1b[0m\n") != null);
try testing.expectEqual(500, std.mem.count(u8, out, pipe ++ " " ++ repeat("a", 16) ++ " " ++ pipe));
try testing.expect(std.mem.indexOf(u8, out, "\x1b[2m├" ++ repeat("─", 18) ++ "┼" ++ repeat("─", 18) ++ "┤\x1b[0m\n") != null);
}
test "md_term: stream falls back to raw rows past its table buffer" {
@@ -853,8 +854,8 @@ test "md_term: stream falls back to raw rows past its table buffer" {
}
test "md_term: overwide table falls back to verbatim rows" {
const header = "|a" ** 17 ++ "|";
const sep = "|-" ** 17 ++ "|";
const header = repeat("|a", 17) ++ "|";
const sep = repeat("|-", 17) ++ "|";
try expectRender(
"\x1b[1m" ++ header ++ "\x1b[0m\n\x1b[2m" ++ sep ++ "\x1b[0m\n",
header ++ "\n" ++ sep,
+2 -2
View File
@@ -118,8 +118,8 @@ const RawTerminal = struct {
raw.lflag.ICANON = false;
raw.lflag.IEXTEN = false;
raw.lflag.ISIG = false;
raw.cc[@intFromEnum(std.c.V.MIN)] = 0;
raw.cc[@intFromEnum(std.c.V.TIME)] = 1;
raw.cc[@backingInt(std.c.V.MIN)] = 0;
raw.cc[@backingInt(std.c.V.TIME)] = 1;
std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, raw) catch return error.NotInteractive;
// Under `ansi.kitty_disambiguate` (pushed by `Terminal.readLine`),
// cursor keys arrive as CSI-u the byte reader can't parse; push the
+9 -9
View File
@@ -72,13 +72,13 @@ const kind_styles = blk: {
const n = std.enums.values(js_highlight.Kind).len;
var arr: [n]?[:0]const u8 = @splat(null);
for (styles) |s| for (s.kinds) |kind| {
if (arr[@intFromEnum(kind)] != null) @compileError("kind styled twice: " ++ @tagName(kind));
arr[@intFromEnum(kind)] = s.name;
if (arr[@backingInt(kind)] != null) @compileError("kind styled twice: " ++ @tagName(kind));
arr[@backingInt(kind)] = s.name;
};
var out: [n][:0]const u8 = undefined;
for (arr, 0..) |name, i| {
out[i] = name orelse @compileError("js_highlight.Kind with no ps-* style: " ++
@tagName(@as(js_highlight.Kind, @enumFromInt(i))));
@tagName(@as(js_highlight.Kind, @fromBackingInt(@intCast(i)))));
}
break :blk out;
};
@@ -169,7 +169,7 @@ fn addPrefixedCompletion(
partial: []const u8,
) void {
if (!std.ascii.startsWithIgnoreCase(name, partial)) return;
const text = std.fmt.bufPrintZ(buf, "{s}{s}{s}", .{ prefix, name, suffix }) catch return;
const text = std.mem.printSentinel(buf, "{s}{s}{s}", .{ prefix, name, suffix }, 0) catch return;
_ = c.ic_add_completion_prim(cenv, text.ptr, null, null, @intCast(input.len), 0);
}
@@ -408,7 +408,7 @@ fn addPathCompletions(
var name_buf: [completion_buf_len]u8 = undefined;
while (matches.next()) |m| {
const suffix: []const u8 = if (m.is_dir) "/" else "";
const full = std.fmt.bufPrint(&name_buf, "{s}{s}", .{ matches.dir_part, m.name }) catch continue;
const full = std.mem.print(&name_buf, "{s}{s}", .{ matches.dir_part, m.name }) catch continue;
addPrefixedCompletion(cenv, buf, input, prefix, full, suffix, body);
}
}
@@ -580,7 +580,7 @@ fn ghostPathFirstMatch(body: []const u8) [*c]const u8 {
defer matches.deinit();
const m = matches.next() orelse return null;
const suffix: []const u8 = if (m.is_dir) "/" else "";
const text = std.fmt.bufPrintZ(&hint_buf, "{s}{s}", .{ m.name[matches.base.len..], suffix }) catch return null;
const text = std.mem.printSentinel(&hint_buf, "{s}{s}", .{ m.name[matches.base.len..], suffix }, 0) catch return null;
return text.ptr;
}
@@ -589,7 +589,7 @@ fn ghostPathFirstMatch(body: []const u8) [*c]const u8 {
fn ghostFirstMatch(names: []const []const u8, body: []const u8, lead: []const u8) [*c]const u8 {
for (names) |v| {
if (!std.ascii.startsWithIgnoreCase(v, body)) continue;
const text = std.fmt.bufPrintZ(&hint_buf, "{s}{s}", .{ lead, v[body.len..] }) catch return null;
const text = std.mem.printSentinel(&hint_buf, "{s}{s}", .{ lead, v[body.len..] }, 0) catch return null;
return text.ptr;
}
return null;
@@ -621,7 +621,7 @@ fn renderSchemaHint(schema: *const Schema, body: []const u8, ends_ws: bool) [*c]
for (schema.hints) |slot| {
if (a.isUsed(slot.name)) continue;
if (!std.ascii.startsWithIgnoreCase(slot.name, pk)) continue;
const text = std.fmt.bufPrintZ(&hint_buf, "{s}=…", .{slot.name[pk.len..]}) catch return null;
const text = std.mem.printSentinel(&hint_buf, "{s}=…", .{slot.name[pk.len..]}, 0) catch return null;
return text.ptr;
}
return null;
@@ -725,7 +725,7 @@ const IcSink = struct {
henv: ?*c.ic_highlight_env_t,
pub fn emit(self: IcSink, start: usize, len: usize, kind: js_highlight.Kind) void {
c.ic_highlight(self.henv, @intCast(start), @intCast(len), kind_styles[@intFromEnum(kind)].ptr);
c.ic_highlight(self.henv, @intCast(start), @intCast(len), kind_styles[@backingInt(kind)].ptr);
}
};
+1 -1
View File
@@ -72,7 +72,7 @@ pub fn randomFilename(arena: std.mem.Allocator) ![]const u8 {
var n_bytes: [8]u8 = undefined;
lp.io.random(&n_bytes);
const n = std.mem.readInt(u64, &n_bytes, .little);
const path = try std.fmt.allocPrint(arena, "session-{x}.js", .{n});
const path = try arena.print("session-{x}.js", .{n});
if (!(try fileExists(path))) return path;
}
return error.NameCollision;
+28 -25
View File
@@ -126,7 +126,7 @@ pub fn gcloudAccessToken(allocator: std.mem.Allocator) ![:0]const u8 {
std.debug.print("`gcloud auth print-access-token` failed:\n{s}", .{result.stderr});
return error.GcloudTokenFailed;
}
return allocator.dupeZ(u8, token);
return allocator.dupeSentinel(u8, token, 0);
}
/// True when a non-Ollama provider key is available (flag, remembered, or
@@ -271,24 +271,24 @@ pub const Remembered = struct {
search_engine: ?lp.tools.SearchEngine = null,
};
pub fn loadRemembered(allocator: std.mem.Allocator) ?Remembered {
const data = std.Io.Dir.cwd().readFileAllocOptions(lp.io, remembered_path, allocator, .limited(1024), .of(u8), 0) catch return null;
defer allocator.free(data);
return parseRemembered(allocator, data);
/// The result's strings live in `arena`.
pub fn loadRemembered(gpa: std.mem.Allocator, arena: std.mem.Allocator) ?Remembered {
const data = std.Io.Dir.cwd().readFileAllocOptions(lp.io, remembered_path, gpa, .limited(1024), .of(u8), 0) catch return null;
defer gpa.free(data);
return parseRemembered(gpa, arena, data);
}
fn parseRemembered(allocator: std.mem.Allocator, data: [:0]const u8) ?Remembered {
// A real Diagnostics, not null: a type-check failure allocates an owned
// error note that leaks unless a Diagnostics owns it to free on deinit.
var diag: std.zon.parse.Diagnostics = .{};
defer diag.deinit(allocator);
const remembered = std.zon.parse.fromSliceAlloc(Remembered, allocator, data, &diag, .{}) catch return null;
fn parseRemembered(gpa: std.mem.Allocator, arena: std.mem.Allocator, data: [:0]const u8) ?Remembered {
var diag: std.zon.parse.Diagnostics = undefined;
const remembered = std.zon.parse.fromSlice(Remembered, .{
.gpa = gpa,
.arena = arena,
.source = data,
.diagnostics = &diag,
}) catch return null;
// An empty model is corrupt only when a provider is set; a null provider
// (LLM disabled) legitimately has no model to remember.
if (remembered.provider != null and remembered.model.len == 0) {
std.zon.parse.free(allocator, remembered);
return null;
}
if (remembered.provider != null and remembered.model.len == 0) return null;
return remembered;
}
@@ -423,15 +423,16 @@ pub fn reconcileModel(
const testing = @import("../testing.zig");
test "parseRemembered: invalid enum is rejected without leaking" {
// A bad enum builds an owned error note; the leak detector fails here if
// the Diagnostics doesn't free it.
try testing.expect(parseRemembered(testing.allocator, ".{ .provider = .not_a_provider, .model = \"x\" }") == null);
test "parseRemembered: invalid enum is rejected" {
var arena: std.heap.ArenaAllocator = .init(testing.allocator);
defer arena.deinit();
try testing.expect(parseRemembered(testing.allocator, arena.allocator(), ".{ .provider = .not_a_provider, .model = \"x\" }") == null);
}
test "parseRemembered: valid file round-trips" {
const remembered = parseRemembered(testing.allocator, ".{ .provider = null, .model = \"some-model\" }").?;
defer std.zon.parse.free(testing.allocator, remembered);
var arena: std.heap.ArenaAllocator = .init(testing.allocator);
defer arena.deinit();
const remembered = parseRemembered(testing.allocator, arena.allocator(), ".{ .provider = null, .model = \"some-model\" }").?;
try testing.expect(remembered.provider == null);
try testing.expectString("some-model", remembered.model);
// Absent `stream` is null so pre-streaming files still fall back to the default.
@@ -439,14 +440,16 @@ test "parseRemembered: valid file round-trips" {
}
test "parseRemembered: stream field round-trips" {
const remembered = parseRemembered(testing.allocator, ".{ .model = \"m\", .stream = false }").?;
defer std.zon.parse.free(testing.allocator, remembered);
var arena: std.heap.ArenaAllocator = .init(testing.allocator);
defer arena.deinit();
const remembered = parseRemembered(testing.allocator, arena.allocator(), ".{ .model = \"m\", .stream = false }").?;
try testing.expect(remembered.stream == false);
}
test "parseRemembered: search_engine field round-trips" {
const remembered = parseRemembered(testing.allocator, ".{ .model = \"m\", .search_engine = .brave }").?;
defer std.zon.parse.free(testing.allocator, remembered);
var arena: std.heap.ArenaAllocator = .init(testing.allocator);
defer arena.deinit();
const remembered = parseRemembered(testing.allocator, arena.allocator(), ".{ .model = \"m\", .search_engine = .brave }").?;
try testing.expect(remembered.search_engine == .brave);
}
+1 -1
View File
@@ -83,7 +83,7 @@ comptime {
/// hints beside it, vertically centered. `llm_active` picks the tagline.
pub fn print(llm_active: bool) void {
var version_buf: [192]u8 = undefined;
const version: []const u8 = std.fmt.bufPrint(&version_buf, ansi.dim ++ "{s}" ++ ansi.reset, .{lp.build_config.version}) catch "";
const version: []const u8 = std.mem.print(&version_buf, ansi.dim ++ "{s}" ++ ansi.reset, .{lp.build_config.version}) catch "";
var lines: [9][]const u8 = undefined;
var n: usize = 0;
+1 -1
View File
@@ -476,7 +476,7 @@ pub const Listener = struct {
};
},
.string => |string| {
const str = try arena.dupeZ(u8, string.str());
const str = try arena.dupeSentinel(u8, string.str(), 0);
local.eval(str, null) catch |err| {
if (err == error.ExecutionTerminated) {
return error.ExecutionTerminated;
+9 -16
View File
@@ -479,21 +479,14 @@ pub fn chained(self: *Factory, values: anytype) !*ChainedLeaf(@TypeOf(values)) {
}
pub fn chainedWithAllocator(allocator: Allocator, values: anytype) !*ChainedLeaf(@TypeOf(values)) {
const fields = @typeInfo(@TypeOf(values)).@"struct".fields;
const types = comptime blk: {
var types: [fields.len]type = undefined;
for (fields, 0..) |f, i| {
types[i] = f.type;
}
break :blk types;
};
const types = @typeInfo(@TypeOf(values)).@"struct".field_types;
comptime {
for (types[1..], 0..) |T, i| {
assert(reflect.Proto(T).? == types[i]);
}
}
const chain = try PrototypeChain(&types).allocate(allocator);
const chain = try PrototypeChain(types).allocate(allocator);
inline for (0..types.len) |i| {
const ptr = chain.get(i);
ptr.* = values[i];
@@ -505,8 +498,8 @@ pub fn chainedWithAllocator(allocator: Allocator, values: anytype) !*ChainedLeaf
}
fn ChainedLeaf(comptime Values: type) type {
const fields = @typeInfo(Values).@"struct".fields;
return fields[fields.len - 1].type;
const field_types = @typeInfo(Values).@"struct".field_types;
return field_types[field_types.len - 1];
}
pub fn document(self: *Factory, child: anytype) !*@TypeOf(child) {
@@ -684,8 +677,8 @@ fn typeInit(comptime Parent: type, value: anytype) Parent.Type {
}
fn subtypeTag(comptime Parent: type, comptime V: type) Parent.Type {
for (@typeInfo(Parent.Type).@"enum".fields) |f| {
const tag: Parent.Type = @enumFromInt(f.value);
for (@typeInfo(Parent.Type).@"enum".field_values) |field_value| {
const tag: Parent.Type = @fromBackingInt(field_value);
if (Parent.Subtype(tag) == V) return tag;
}
@compileError(@typeName(V) ++ " is not a subtype of " ++ @typeName(Parent));
@@ -699,9 +692,9 @@ fn subtypeTag(comptime Parent: type, comptime V: type) Parent.Type {
// This only works because we never have a union with a field S and another
// field *S.
fn unionFieldName(comptime T: type, comptime V: type) []const u8 {
inline for (@typeInfo(T).@"union".fields) |field| {
if (reflect.Struct(field.type) == reflect.Struct(V)) {
return field.name;
inline for (@typeInfo(T).@"union".field_names, @typeInfo(T).@"union".field_types) |field_name, field_type| {
if (reflect.Struct(field_type) == reflect.Struct(V)) {
return field_name;
}
}
@compileError(@typeName(V) ++ " is not a valid type for " ++ @typeName(T) ++ ".type");
+9 -9
View File
@@ -696,7 +696,7 @@ pub fn navigate(self: *Frame, request_url: [:0]const u8, opts: NavigateOpts) !vo
else if (is_srcdoc)
"about:srcdoc"
else
try self.arena.dupeZ(u8, request_url);
try self.arena.dupeSentinel(u8, request_url, 0);
// even though about:blank navigations may share the same _data_, we
// have to do this to make sure window.location is at a unique _address_.
@@ -825,7 +825,7 @@ pub fn navigate(self: *Frame, request_url: [:0]const u8, opts: NavigateOpts) !vo
self.url = blk: {
if (URL.isCompleteHTTPUrl(request_url)) {
break :blk try self.arena.dupeZ(u8, request_url);
break :blk try self.arena.dupeSentinel(u8, request_url, 0);
}
break :blk try std.mem.concatWithSentinel(self.arena, u8, &.{ "http://", request_url }, 0);
};
@@ -836,7 +836,7 @@ pub fn navigate(self: *Frame, request_url: [:0]const u8, opts: NavigateOpts) !vo
.reason = opts.reason,
.method = opts.method,
.body = if (opts.body) |b| try self.arena.dupe(u8, b) else null,
.header = if (opts.header) |h| try self.arena.dupeZ(u8, h) else null,
.header = if (opts.header) |h| try self.arena.dupeSentinel(u8, h, 0) else null,
};
const transfer = try http_client.newRequest(.{
@@ -947,7 +947,7 @@ pub fn scheduleNavigation(self: *Frame, request_url: []const u8, opts: NavigateO
fn scheduleNavigationWithArena(originator: *Frame, arena: *lp.Arena, request_url: []const u8, opts: NavigateOpts, nt: Navigation) !void {
const resolved_url, const is_about_something = blk: {
if (URL.isCompleteHTTPUrl(request_url)) {
break :blk .{ try arena.dupeZ(u8, request_url), false };
break :blk .{ try arena.dupeSentinel(u8, request_url, 0), false };
}
if (std.mem.eql(u8, request_url, "about:blank")) {
@@ -1012,7 +1012,7 @@ fn scheduleNavigationWithArena(originator: *Frame, arena: *lp.Arena, request_url
const is_fragment_navigation = !std.mem.eql(u8, target.url, resolved_url) and URL.eqlDocument(target.url, resolved_url);
if (!opts.force and is_fragment_navigation) {
const old_url = target.url;
target.url = try target.arena.dupeZ(u8, resolved_url);
target.url = try target.arena.dupeSentinel(u8, resolved_url, 0);
const location = try Location.init(target.url, target);
location.acquireRef();
@@ -1061,7 +1061,7 @@ fn scheduleNavigationWithArena(originator: *Frame, arena: *lp.Arena, request_url
// that from its owner. Only a top-level navigation's initiator is another
// document.
if (nav_opts.initiator_url == null and target.parent == null and std.mem.startsWith(u8, referrer_source, "http")) {
nav_opts.initiator_url = .{ .url = try arena.dupeZ(u8, referrer_source) };
nav_opts.initiator_url = .{ .url = try arena.dupeSentinel(u8, referrer_source, 0) };
}
if (nav_opts.initiator_origin == null) {
if (originator.origin) |o| {
@@ -1468,7 +1468,7 @@ fn frameHeaderDoneCallback(transfer: *HttpClient.Transfer) !HttpClient.Transfer.
const response_url = transfer.req.url;
if (std.mem.eql(u8, response_url, self.url) == false) {
// would be different than self.url in the case of a redirect
self.url = try self.arena.dupeZ(u8, response_url);
self.url = try self.arena.dupeSentinel(u8, response_url, 0);
self.origin = try URL.getOrigin(self.arena, self.url);
}
@@ -3868,10 +3868,10 @@ pub fn submitForm(self: *Frame, submitter_: ?*Element, form_: ?*Element.Html.For
opts.body = buf.written();
opts.header = switch (encoding) {
.urlencode => "Content-Type: application/x-www-form-urlencoded",
.formdata => |b| try std.fmt.allocPrintSentinel(arena.allocator(), "Content-Type: multipart/form-data; boundary={s}", .{b}, 0),
.formdata => |b| try arena.allocator().printSentinel("Content-Type: multipart/form-data; boundary={s}", .{b}, 0),
// Per WHATWG HTML §4.10.21.6, text/plain submissions include the form's
// resolved encoding (accept-charset or document charset).
.plaintext => try std.fmt.allocPrintSentinel(arena.allocator(), "Content-Type: text/plain; charset={s}", .{charset}, 0),
.plaintext => try arena.allocator().printSentinel("Content-Type: text/plain; charset={s}", .{charset}, 0),
};
} else {
action = try URL.concatQueryString(arena.allocator(), action, buf.written());
+11 -4
View File
@@ -17,6 +17,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const repeat = @import("../string.zig").repeat;
const Allocator = std.mem.Allocator;
const Mime = @This();
@@ -28,9 +29,15 @@ charset_len: usize = default_charset_len,
is_default_charset: bool = true,
/// String "UTF-8" continued by null characters.
const default_charset = .{ 'U', 'T', 'F', '-', '8' } ++ .{0} ** 36;
const default_charset = charsetBuf("UTF-8");
const default_charset_len = 5;
fn charsetBuf(comptime name: []const u8) [41]u8 {
var buf: [41]u8 = @splat(0);
@memcpy(buf[0..name.len], name);
return buf;
}
/// Mime with unknown Content-Type, empty params and empty charset.
pub const unknown = Mime{ .content_type = .{ .unknown = {} } };
@@ -376,7 +383,7 @@ pub fn sniff(body: []const u8) ?Mime {
// UTF-16 big-endian BOM
return .{
.content_type = .{ .text_plain = {} },
.charset = .{ 'U', 'T', 'F', '-', '1', '6', 'B', 'E' } ++ .{0} ** 33,
.charset = comptime charsetBuf("UTF-16BE"),
.charset_len = 8,
.is_default_charset = false,
};
@@ -385,7 +392,7 @@ pub fn sniff(body: []const u8) ?Mime {
// UTF-16 little-endian BOM
return .{
.content_type = .{ .text_plain = {} },
.charset = .{ 'U', 'T', 'F', '-', '1', '6', 'L', 'E' } ++ .{0} ** 33,
.charset = comptime charsetBuf("UTF-16LE"),
.charset_len = 8,
.is_default_charset = false,
};
@@ -1017,7 +1024,7 @@ test "Mime: parse charset (WHATWG parameter semantics)" {
try expect(.{ .content_type = .{ .text_html = {} }, .charset = "UTF-8" }, "text/html;charset =gbk");
// A long preceding parameter doesn't hide a later charset.
try expect(.{ .content_type = .{ .text_html = {} }, .charset = "gbk" }, "text/html;" ++ ("a" ** 130) ++ "=x;charset=gbk");
try expect(.{ .content_type = .{ .text_html = {} }, .charset = "gbk" }, "text/html;" ++ (repeat("a", 130)) ++ "=x;charset=gbk");
}
test "Mime: isHTML" {
+1 -1
View File
@@ -406,7 +406,7 @@ pub fn createBlobUrl(self: *Page, blob: *Blob, origin: ?[]const u8, creator_fram
var uuid: [36]u8 = undefined;
@import("../id.zig").uuidv4(&uuid);
const url = try std.fmt.allocPrint(self.frame_arena, "blob:{s}/{s}", .{ origin orelse "null", uuid });
const url = try self.frame_arena.print("blob:{s}/{s}", .{ origin orelse "null", uuid });
try self.blob_urls.put(self.frame_arena, url, .{ .blob = blob, .creator = creator_frame_id });
blob.acquireRef();
return url;
+1 -1
View File
@@ -141,7 +141,7 @@ pub fn preloadScript(self: *ScriptManager, element: ?*Element.Html, url: []const
const arena = try frame.getArena(.small, "SM.preloadScript");
errdefer arena.release();
const owned_url = try arena.dupeZ(u8, url);
const owned_url = try arena.dupeSentinel(u8, url, 0);
const script = try arena.create(Script);
script.* = .{
+5 -5
View File
@@ -63,7 +63,7 @@ fn buildGreedy(self: SelectorPath, target: *Element) !?[]const u8 {
var el = target.parentElement();
while (el) |ancestor| {
el = ancestor.parentElement();
const trial = try std.fmt.allocPrint(self.arena, "{s} {s}", .{ try self.localSegment(ancestor), candidate });
const trial = try self.arena.print("{s} {s}", .{ try self.localSegment(ancestor), candidate });
const trial_count = self.matchCount(trial);
if (trial_count != 0 and trial_count < count) {
candidate = trial;
@@ -92,7 +92,7 @@ fn buildStrictPath(self: SelectorPath, target: *Element) !?[]const u8 {
fn localSegment(self: SelectorPath, el: *Element) ![]const u8 {
if (el.getId()) |id| {
if (id.len != 0) {
const id_sel = try std.fmt.allocPrint(self.arena, "#{s}", .{try CSS.escape(id, self.frame)});
const id_sel = try self.arena.print("#{s}", .{try CSS.escape(id, self.frame)});
if (self.isFirstMatch(el, id_sel)) return id_sel;
}
}
@@ -101,7 +101,7 @@ fn localSegment(self: SelectorPath, el: *Element) ![]const u8 {
if (!self.siblingMatches(el, base)) return base;
if (try self.hasSegment(el, base)) |sel| return sel;
if (nthOfType(el)) |n| return try std.fmt.allocPrint(self.arena, "{s}:nth-of-type({d})", .{ base, n });
if (nthOfType(el)) |n| return try self.arena.print("{s}:nth-of-type({d})", .{ base, n });
return base;
}
@@ -118,7 +118,7 @@ fn hasSegment(self: SelectorPath, el: *Element, base: []const u8) !?[]const u8 {
try self.enqueueChildren(&queue, d);
const desc = (try self.descriptor(d)) orelse continue;
const candidate = try std.fmt.allocPrint(self.arena, "{s}:has({s})", .{ base, desc });
const candidate = try self.arena.print("{s}:has({s})", .{ base, desc });
if (self.isFirstMatch(el, candidate)) return candidate;
}
return null;
@@ -150,7 +150,7 @@ fn qualifyByAttrs(self: SelectorPath, base: []const u8, el: *Element, comptime a
inline for (attrs) |attr| {
if (el.getAttributeSafe(comptime .wrap(attr))) |value| {
if (value.len != 0 and isPlainAttrValue(value)) {
sel = try std.fmt.allocPrint(self.arena, "{s}[{s}=\"{s}\"]", .{ sel, attr, value });
sel = try self.arena.print("{s}[{s}=\"{s}\"]", .{ sel, attr, value });
added = true;
}
}
+7 -7
View File
@@ -312,7 +312,7 @@ fn registerLayerPath(self: *StyleManager, build_arena: Allocator, parent: u16, d
fn internAnonymousLayer(self: *StyleManager, build_arena: Allocator, parent: u16) Allocator.Error!u16 {
const id = self.next_anon_layer;
// \x00{d} isn't a valid layer name, so this can't conflict
const name = try std.fmt.allocPrint(build_arena, "\x00{d}", .{id});
const name = try build_arena.print("\x00{d}", .{id});
self.next_anon_layer = id + 1;
return self.internLayer(build_arena, parent, name);
}
@@ -321,7 +321,7 @@ fn internLayer(self: *StyleManager, build_arena: Allocator, parent: u16, name: [
const path = if (parent == NO_LAYER)
try build_arena.dupe(u8, name)
else
try std.fmt.allocPrint(build_arena, "{s}.{s}", .{ self.layers.items[parent].path, name });
try build_arena.print("{s}.{s}", .{ self.layers.items[parent].path, name });
const gop = try self.layer_ids.getOrPut(build_arena, path);
if (gop.found_existing) {
@@ -738,7 +738,7 @@ fn Group(comptime Spec: type) type {
const Declared = Spec.Declared;
const Computed = Spec.Computed;
const Field = std.meta.FieldEnum(Declared);
const fields = std.meta.fieldNames(Declared);
const fields = @typeInfo(Declared).@"struct".field_names;
comptime {
// compute copies each declared value into its Computed namesake
@@ -1026,7 +1026,7 @@ pub fn ruleInserted(self: *StyleManager, sheet: *CSSStyleSheet, rule: *CSSRule)
fn appendable(self: *const StyleManager, sheet: *CSSStyleSheet, rule: *CSSRule) bool {
const rules = sheet._css_rules orelse return false;
if (rules._rules.getLastOrNull() != rule) {
if (rules._rules.last() != rule) {
return false;
}
const sheets = self.frame.document._style_sheets orelse return false;
@@ -1282,7 +1282,7 @@ const Visibility = struct {
// element — per CSS Cascade §6.1 any normal-origin author rule beats UA
// origin regardless of specificity, so `.x { display: flex }` on a
// `<div class="x" hidden>` must report visible.
fn finish(p: *Computed, el: *Element, priorities: *const Priorities(Declared)) void {
pub fn finish(p: *Computed, el: *Element, priorities: *const Priorities(Declared)) void {
p.author_display = priorities.get(.display) != 0;
if (!p.author_display and matchesUaDisplayNoneRule(el)) {
p.display = .none;
@@ -1367,10 +1367,10 @@ const Declarations = struct {
}
};
const group_fields = std.meta.fieldNames(Declarations);
const group_fields = @typeInfo(Declarations).@"struct".field_names;
fn declaresAny(declared: anytype) bool {
inline for (comptime std.meta.fieldNames(@TypeOf(declared))) |field| {
inline for (@typeInfo(@TypeOf(declared)).@"struct".field_names) |field| {
if (@field(declared, field) != null) {
return true;
}
+19 -20
View File
@@ -47,7 +47,7 @@ pub fn resolve(
}
defer href.deinit();
return allocator.dupeZ(u8, href.slice());
return allocator.dupeSentinel(u8, href.slice(), 0);
}
/// Resolves a user-provided "address bar" URL the way curl does. Bare host like
@@ -55,7 +55,7 @@ pub fn resolve(
pub fn resolveNavigation(allocator: Allocator, url: []const u8, options: ResolveOptions) ![:0]const u8 {
return resolve(allocator, "", url, options) catch |err| switch (err) {
error.TypeError => {
const with_scheme = try std.fmt.allocPrintSentinel(allocator, "http://{s}", .{url}, 0);
const with_scheme = try allocator.printSentinel("http://{s}", .{url}, 0);
return resolve(allocator, "", with_scheme, options);
},
else => return err,
@@ -241,7 +241,7 @@ pub fn isLoopbackHost(hostname: []const u8) bool {
const address = std.Io.net.IpAddress.parseLiteral(host) catch return false;
return switch (address) {
.ip4 => |ip4| ip4.bytes[0] == 127,
.ip6 => |ip6| std.mem.eql(u8, &ip6.bytes, &([_]u8{0} ** 15 ++ [_]u8{1})),
.ip6 => |ip6| std.mem.eql(u8, &ip6.bytes, &(@as([15]u8, @splat(0)) ++ [_]u8{1})),
};
}
@@ -332,7 +332,7 @@ pub fn getOrigin(allocator: Allocator, raw: [:0]const u8) !?[]const u8 {
// Not a port (probably IPv6)
if (has_user_info) {
// Need to allocate to exclude user info
return try std.fmt.allocPrint(allocator, "{s}//{s}", .{ raw[0 .. scheme_end + 1], host_part });
return try allocator.print("{s}//{s}", .{ raw[0 .. scheme_end + 1], host_part });
}
// Can return a slice
return raw[0..authority_end];
@@ -348,14 +348,14 @@ pub fn getOrigin(allocator: Allocator, raw: [:0]const u8) !?[]const u8 {
// Need to allocate to build origin without default port and/or user info
const hostname = host_part[0..colon_pos_in_host];
if (is_default) {
return try std.fmt.allocPrint(allocator, "{s}//{s}", .{ protocol, hostname });
return try allocator.print("{s}//{s}", .{ protocol, hostname });
} else {
return try std.fmt.allocPrint(allocator, "{s}//{s}", .{ protocol, host_part });
return try allocator.print("{s}//{s}", .{ protocol, host_part });
}
}
} else if (has_user_info) {
// No port, but has user info - need to allocate
return try std.fmt.allocPrint(allocator, "{s}//{s}", .{ raw[0 .. scheme_end + 1], host_part });
return try allocator.print("{s}//{s}", .{ raw[0 .. scheme_end + 1], host_part });
}
// Common case: no user info, no default port - return slice (zero allocation!)
@@ -409,7 +409,7 @@ fn buildUrl(
search: []const u8,
hash: []const u8,
) ![:0]const u8 {
return std.fmt.allocPrintSentinel(allocator, "{s}//{s}{s}{s}{s}", .{
return allocator.printSentinel("{s}//{s}{s}{s}{s}", .{
protocol,
host,
pathname,
@@ -426,7 +426,7 @@ pub fn setProtocol(current: [:0]const u8, value: []const u8, allocator: Allocato
// Add : suffix if not present
const protocol = if (value.len > 0 and value[value.len - 1] != ':')
try std.fmt.allocPrint(allocator, "{s}:", .{value})
try allocator.print("{s}:", .{value})
else
value;
@@ -455,7 +455,7 @@ pub fn setHost(current: [:0]const u8, value: []const u8, allocator: Allocator) !
// No port in new value - preserve existing port
const current_port = getPort(current);
if (current_port.len > 0) {
break :blk try std.fmt.allocPrint(allocator, "{s}:{s}", .{ value, current_port });
break :blk try allocator.print("{s}:{s}", .{ value, current_port });
}
break :blk value;
};
@@ -466,7 +466,7 @@ pub fn setHost(current: [:0]const u8, value: []const u8, allocator: Allocator) !
pub fn setHostname(current: [:0]const u8, value: []const u8, allocator: Allocator) ![:0]const u8 {
const current_port = getPort(current);
const new_host = if (current_port.len > 0)
try std.fmt.allocPrint(allocator, "{s}:{s}", .{ value, current_port })
try allocator.print("{s}:{s}", .{ value, current_port })
else
value;
@@ -492,7 +492,7 @@ pub fn setPort(current: [:0]const u8, value: ?[]const u8, allocator: Allocator)
if (std.mem.eql(u8, protocol, "http:") and std.mem.eql(u8, port_str, "80")) {
break :blk hostname;
}
break :blk try std.fmt.allocPrint(allocator, "{s}:{s}", .{ hostname, port_str });
break :blk try allocator.print("{s}:{s}", .{ hostname, port_str });
} else hostname;
return buildUrl(allocator, protocol, new_host, pathname, search, hash);
@@ -508,7 +508,7 @@ pub fn setPathname(current: [:0]const u8, value: []const u8, allocator: Allocato
// Add / prefix if not present and value is not empty
const pathname = if (encoded.len > 0 and encoded[0] != '/')
try std.fmt.allocPrint(allocator, "/{s}", .{encoded})
try allocator.print("/{s}", .{encoded})
else
encoded;
@@ -525,7 +525,7 @@ pub fn setSearch(current: [:0]const u8, value: []const u8, allocator: Allocator)
// Add ? prefix if not present and value is not empty
const search = if (encoded.len > 0 and value[0] != '?')
try std.fmt.allocPrint(allocator, "?{s}", .{encoded})
try allocator.print("?{s}", .{encoded})
else
encoded;
@@ -542,7 +542,7 @@ pub fn setHash(current: [:0]const u8, value: []const u8, allocator: Allocator) !
// Add # prefix if not present and value is not empty
const hash = if (encoded.len > 0 and encoded[0] != '#')
try std.fmt.allocPrint(allocator, "#{s}", .{encoded})
try allocator.print("#{s}", .{encoded})
else
encoded;
@@ -586,7 +586,7 @@ fn buildUrlWithUserInfo(
if (username.len == 0 and password.len == 0) {
return buildUrl(allocator, protocol, host, pathname, search, hash);
} else if (password.len == 0) {
return std.fmt.allocPrintSentinel(allocator, "{s}//{s}@{s}{s}{s}{s}", .{
return allocator.printSentinel("{s}//{s}@{s}{s}{s}{s}", .{
protocol,
username,
host,
@@ -595,7 +595,7 @@ fn buildUrlWithUserInfo(
hash,
}, 0);
} else {
return std.fmt.allocPrintSentinel(allocator, "{s}//{s}:{s}@{s}{s}{s}{s}", .{
return allocator.printSentinel("{s}//{s}:{s}@{s}{s}{s}{s}", .{
protocol,
username,
password,
@@ -609,7 +609,7 @@ fn buildUrlWithUserInfo(
pub fn concatQueryString(arena: Allocator, url: []const u8, query_string: []const u8) ![:0]const u8 {
if (query_string.len == 0) {
return arena.dupeZ(u8, url);
return arena.dupeSentinel(u8, url, 0);
}
var buf: std.ArrayList(u8) = .empty;
@@ -633,8 +633,7 @@ pub fn concatQueryString(arena: Allocator, url: []const u8, query_string: []cons
pub fn getRobotsUrl(arena: Allocator, url: [:0]const u8) ![:0]const u8 {
const origin = try getOrigin(arena, url) orelse return error.NoOrigin;
return try std.fmt.allocPrintSentinel(
arena,
return try arena.printSentinel(
"{s}/robots.txt",
.{origin},
0,
+1 -1
View File
@@ -102,7 +102,7 @@ fn describe(node: *Node) []const u8 {
const S = struct {
threadlocal var buf: [96]u8 = undefined;
};
return std.fmt.bufPrint(&S.buf, "{s}.{s}#{s}", .{ @tagName(el.getTag()), el.getClassName() orelse "", el.getId() orelse "" }) catch S.buf[0..];
return std.mem.print(&S.buf, "{s}.{s}#{s}", .{ @tagName(el.getTag()), el.getClassName() orelse "", el.getId() orelse "" }) catch S.buf[0..];
}
const Stats = struct {
+1 -1
View File
@@ -55,7 +55,7 @@ pub fn parse(arena: Allocator, url: []const u8) !Parsed {
content_type = "text/plain;charset=US-ASCII";
} else if (content_type[0] == ';') {
// e.g. "data:;charset=utf-8,x" -> "text/plain;charset=utf-8"
content_type = try std.fmt.allocPrint(arena, "text/plain{s}", .{content_type});
content_type = try arena.print("text/plain{s}", .{content_type});
}
const body_text = try URL.unescape(arena, encoded_body);
+1 -1
View File
@@ -57,7 +57,7 @@ pub fn allowed(frame: *const Frame, transfer: *HttpClient.Transfer) bool {
fn hasFrameAncestors(csp: []const u8) bool {
const name = "frame-ancestors";
var pos: usize = 0;
while (std.ascii.indexOfIgnoreCasePos(csp, pos, name)) |start| {
while (std.ascii.findIgnoreCasePos(csp, pos, name)) |start| {
pos = start + name.len;
// A directive name starts a policy (',') or a directive (';'), so
+2 -2
View File
@@ -1156,7 +1156,7 @@ fn parserAttributeName(frame: *Frame, qname: Parser.QualName) ![]const u8 {
if (prefix.len == 0) {
return local;
}
return std.fmt.allocPrint(frame.local_arena, "{s}:{s}", .{ prefix, local });
return frame.local_arena.print("{s}:{s}", .{ prefix, local });
}
// Called when `new MyElement()` is invoked directly in JS (not via the
@@ -1300,7 +1300,7 @@ fn isXmlNameChar(c: u21) bool {
(c >= 0x203F and c <= 0x2040);
}
fn asUint(comptime string: anytype) std.meta.Int(
fn asUint(comptime string: anytype) @Int(
.unsigned,
@bitSizeOf(@TypeOf(string.*)) - 8, // (- 8) to exclude sentinel 0
) {
+1 -1
View File
@@ -113,7 +113,7 @@ fn exifOrientation(segment: []const u8) u16 {
}
const tiff = segment[6..];
const endian: std.builtin.Endian = if (std.mem.eql(u8, tiff[0..2], "II"))
const endian: std.lang.Endian = if (std.mem.eql(u8, tiff[0..2], "II"))
.little
else if (std.mem.eql(u8, tiff[0..2], "MM"))
.big
+1 -1
View File
@@ -238,7 +238,7 @@ fn walkInteractive(
if (filter.name) |nf| {
const n = name orelse continue;
const hit = switch (nf) {
.substring => |s| std.ascii.indexOfIgnoreCase(n, s) != null,
.substring => |s| std.ascii.findIgnoreCase(n, s) != null,
.regex => |re| re.matches(n),
};
if (!hit) continue;
+25 -25
View File
@@ -105,7 +105,7 @@ pub fn deinit(self: *Caller) void {
// Unlike call_arena, local_arena is reset on _every_ return, since its
// users promise not to hold data across a nested call. In debug, free
// back to the backing allocator so a stale pointer trips the
// DebugAllocator's use-after-free detection; in release, retain a buffer
// SafeAllocator's use-after-free detection; in release, retain a buffer
// to avoid realloc churn.
{
const local_arena: *ArenaAllocator = @ptrCast(@alignCast(ctx.local_arena.ptr));
@@ -202,7 +202,7 @@ fn _getIndex(comptime T: type, local: *const Local, func: anytype, idx: u32, inf
var args: ParameterTypes(F) = undefined;
@field(args, "0") = try TaggedOpaque.fromJS(*T, info.getThis());
@field(args, "1") = idx;
if (@typeInfo(F).@"fn".params.len == 3) {
if (@typeInfo(F).@"fn".param_types.len == 3) {
@field(args, "2") = getGlobalArg(@TypeOf(args.@"2"), local.ctx);
}
const ret = @call(.auto, func, args);
@@ -228,7 +228,7 @@ fn _getNamedIndex(comptime T: type, local: *const Local, func: anytype, name: *c
var args: ParameterTypes(F) = undefined;
@field(args, "0") = try TaggedOpaque.fromJS(*T, info.getThis());
@field(args, "1") = try nameToString(local, @TypeOf(args.@"1"), name);
if (@typeInfo(F).@"fn".params.len == 3) {
if (@typeInfo(F).@"fn".param_types.len == 3) {
@field(args, "2") = getGlobalArg(@TypeOf(args.@"2"), local.ctx);
}
const ret = @call(.auto, func, args);
@@ -255,7 +255,7 @@ fn _setIndex(comptime T: type, local: *const Local, func: anytype, idx: u32, js_
@field(args, "0") = try TaggedOpaque.fromJS(*T, info.getThis());
@field(args, "1") = idx;
@field(args, "2") = try local.jsValueToZig(@TypeOf(@field(args, "2")), js_value);
if (@typeInfo(F).@"fn".params.len == 4) {
if (@typeInfo(F).@"fn".param_types.len == 4) {
@field(args, "3") = getGlobalArg(@TypeOf(args.@"3"), local.ctx);
}
const ret = @call(.auto, func, args);
@@ -281,7 +281,7 @@ fn _deleteOrDefineIndex(comptime T: type, local: *const Local, func: anytype, id
var args: ParameterTypes(F) = undefined;
@field(args, "0") = try TaggedOpaque.fromJS(*T, info.getThis());
@field(args, "1") = idx;
if (@typeInfo(F).@"fn".params.len == 3) {
if (@typeInfo(F).@"fn".param_types.len == 3) {
@field(args, "2") = getGlobalArg(@TypeOf(args.@"2"), local.ctx);
}
const ret = @call(.auto, func, args);
@@ -308,7 +308,7 @@ fn _setNamedIndex(comptime T: type, local: *const Local, func: anytype, name: *c
@field(args, "0") = try TaggedOpaque.fromJS(*T, info.getThis());
@field(args, "1") = try nameToString(local, @TypeOf(args.@"1"), name);
@field(args, "2") = try local.jsValueToZig(@TypeOf(@field(args, "2")), js_value);
if (@typeInfo(F).@"fn".params.len == 4) {
if (@typeInfo(F).@"fn".param_types.len == 4) {
@field(args, "3") = getGlobalArg(@TypeOf(args.@"3"), local.ctx);
}
const ret = @call(.auto, func, args);
@@ -334,7 +334,7 @@ fn _deleteOrDefineNamedIndex(comptime T: type, local: *const Local, func: anytyp
var args: ParameterTypes(F) = undefined;
@field(args, "0") = try TaggedOpaque.fromJS(*T, info.getThis());
@field(args, "1") = try nameToString(local, @TypeOf(args.@"1"), name);
if (@typeInfo(F).@"fn".params.len == 3) {
if (@typeInfo(F).@"fn".param_types.len == 3) {
@field(args, "2") = getGlobalArg(@TypeOf(args.@"2"), local.ctx);
}
const ret = @call(.auto, func, args);
@@ -359,7 +359,7 @@ fn _getEnumerator(comptime T: type, local: *const Local, func: anytype, info: Pr
const F = @TypeOf(func);
var args: ParameterTypes(F) = undefined;
@field(args, "0") = try TaggedOpaque.fromJS(*T, info.getThis());
if (@typeInfo(F).@"fn".params.len == 2) {
if (@typeInfo(F).@"fn".param_types.len == 2) {
@field(args, "1") = getGlobalArg(@TypeOf(args.@"1"), local.ctx);
}
const ret = @call(.auto, func, args);
@@ -385,7 +385,7 @@ fn _getIndexQuery(comptime T: type, local: *const Local, func: anytype, idx: u32
var args: ParameterTypes(F) = undefined;
@field(args, "0") = try TaggedOpaque.fromJS(*T, info.getThis());
@field(args, "1") = idx;
if (@typeInfo(F).@"fn".params.len == 3) {
if (@typeInfo(F).@"fn".param_types.len == 3) {
@field(args, "2") = getGlobalArg(@TypeOf(args.@"2"), local.ctx);
}
return queryReturn(local, @call(.auto, func, args), info);
@@ -410,7 +410,7 @@ fn _getNamedQuery(comptime T: type, local: *const Local, func: anytype, name: *c
var args: ParameterTypes(F) = undefined;
@field(args, "0") = try TaggedOpaque.fromJS(*T, info.getThis());
@field(args, "1") = try nameToString(local, @TypeOf(args.@"1"), name);
if (@typeInfo(F).@"fn".params.len == 3) {
if (@typeInfo(F).@"fn".param_types.len == 3) {
@field(args, "2") = getGlobalArg(@TypeOf(args.@"2"), local.ctx);
}
return queryReturn(local, @call(.auto, func, args), info);
@@ -483,8 +483,8 @@ fn returnsBool(comptime F: type) bool {
}
fn isInErrorSet(err: anyerror, comptime T: type) bool {
inline for (@typeInfo(T).error_set.?) |e| {
if (err == @field(anyerror, e.name)) return true;
inline for (@typeInfo(T).error_set.error_names.?) |name| {
if (err == @field(anyerror, name)) return true;
}
return false;
}
@@ -666,11 +666,11 @@ fn serializeFunctionArgs(local: *const Local, info: FunctionCallbackInfo) ![]con
// Takes a function, and returns a tuple for its argument. Used when we
// @call a function
fn ParameterTypes(comptime F: type) type {
const params = @typeInfo(F).@"fn".params;
var types: [params.len]type = undefined;
const param_types = @typeInfo(F).@"fn".param_types;
var types: [param_types.len]type = undefined;
inline for (params, 0..) |param, i| {
types[i] = param.type.?;
inline for (param_types, 0..) |param_type, i| {
types[i] = param_type.?;
}
return @Tuple(&types);
@@ -1018,7 +1018,7 @@ pub const Function = struct {
fn getArgs(comptime F: type, comptime offset: usize, local: *const Local, info: FunctionCallbackInfo) !ParameterTypes(F) {
var args: ParameterTypes(F) = undefined;
const params = @typeInfo(F).@"fn".params[offset..];
const params = @typeInfo(F).@"fn".param_types[offset..];
// Except for the constructor, the first parameter is always `self`
// This isn't something we'll bind from JS, so skip it.
const params_to_map = blk: {
@@ -1029,7 +1029,7 @@ fn getArgs(comptime F: type, comptime offset: usize, local: *const Local, info:
// If the last parameter is Frame/Page/Execution, set it from
// context and exclude it from our params slice, because we don't want
// to bind it to a JS argument.
const LastParamType = params[params.len - 1].type.?;
const LastParamType = params[params.len - 1].?;
if (comptime isFrame(LastParamType) or isPage(LastParamType) or isExecution(LastParamType)) {
@field(args, tupleFieldName(params.len - 1 + offset)) = getGlobalArg(LastParamType, local.ctx);
break :blk params[0 .. params.len - 1];
@@ -1053,7 +1053,7 @@ fn getArgs(comptime F: type, comptime offset: usize, local: *const Local, info:
// is a slice AND the corresponding javascript parameter is
// NOT an an array, then we'll treat it as a variadic.
const last_parameter_type = params_to_map[params_to_map.len - 1].type.?;
const last_parameter_type = params_to_map[params_to_map.len - 1].?;
const last_parameter_type_info = @typeInfo(last_parameter_type);
if (last_parameter_type_info == .pointer and last_parameter_type_info.pointer.size == .slice) {
const slice_type = last_parameter_type_info.pointer.child;
@@ -1075,7 +1075,7 @@ fn getArgs(comptime F: type, comptime offset: usize, local: *const Local, info:
}
}
inline for (params_to_map, 0..) |param, i| {
inline for (params_to_map, 0..) |param_type, i| {
const field_index = comptime i + offset;
if (comptime i == params_to_map.len - 1) {
if (is_variadic) {
@@ -1083,14 +1083,14 @@ fn getArgs(comptime F: type, comptime offset: usize, local: *const Local, info:
}
}
if (comptime isFrame(param.type.?)) {
if (comptime isFrame(param_type.?)) {
@compileError("Frame must be the last parameter: " ++ @typeName(F));
} else if (comptime isPage(param.type.?)) {
} else if (comptime isPage(param_type.?)) {
@compileError("Page must be the last parameter: " ++ @typeName(F));
} else if (comptime isExecution(param.type.?)) {
} else if (comptime isExecution(param_type.?)) {
@compileError("Execution must be the last parameter: " ++ @typeName(F));
} else if (i >= js_parameter_count) {
if (@typeInfo(param.type.?) != .optional) {
if (@typeInfo(param_type.?) != .optional) {
return error.InvalidArgument;
}
@field(args, tupleFieldName(field_index)) = null;
@@ -1102,7 +1102,7 @@ fn getArgs(comptime F: type, comptime offset: usize, local: *const Local, info:
// to the right DOMException. Compared by name because the per-
// type instantiation of jsValueToZig may not include such errors
// in its inferred error set.
@field(args, tupleFieldName(field_index)) = local.jsValueToZig(param.type.?, js_val) catch |err| {
@field(args, tupleFieldName(field_index)) = local.jsValueToZig(param_type.?, js_val) catch |err| {
if (err == error.JsException) {
// an exception thrown by user code (e.g. a toString
// getter) is pending; propagate it untouched
+3 -3
View File
@@ -367,7 +367,7 @@ pub fn module(self: *Context, comptime want_result: bool, local: *const js.Local
}
}
const owned_url = try arena.dupeZ(u8, url);
const owned_url = try arena.dupeSentinel(u8, url, 0);
if (cacheable and !gop.found_existing) {
gop.key_ptr.* = owned_url;
}
@@ -529,7 +529,7 @@ fn postCompileModule(self: *Context, mod: js.Module, url: [:0]const u8, local: *
};
const nested_gop = try self.module_cache.getOrPut(self.arena.allocator(), normalized_specifier);
if (!nested_gop.found_existing) {
const owned_specifier = try self.arena.dupeZ(u8, normalized_specifier);
const owned_specifier = try self.arena.dupeSentinel(u8, normalized_specifier, 0);
nested_gop.key_ptr.* = owned_specifier;
nested_gop.value_ptr.* = .{};
try script_manager.preloadImport(owned_specifier, url, .{});
@@ -537,7 +537,7 @@ fn postCompileModule(self: *Context, mod: js.Module, url: [:0]const u8, local: *
// Entry exists but module failed to compile previously.
// The imported_modules entry may have been consumed, so
// re-preload to ensure waitForImport can find it.
// Key was stored via dupeZ so it has a sentinel in memory.
// Key was stored via dupeSentinel so it has a sentinel in memory.
const key = nested_gop.key_ptr.*;
const key_z: [:0]const u8 = key.ptr[0..key.len :0];
try script_manager.preloadImport(key_z, url, .{});
+5 -5
View File
@@ -196,12 +196,12 @@ fn _tryCallWithThis(self: *const Function, comptime T: type, this: anytype, args
const js_args: []const *const v8.Value = switch (@typeInfo(@TypeOf(aargs))) {
.@"struct" => |s| blk: {
const fields = s.fields;
var js_args: [fields.len]*const v8.Value = undefined;
inline for (fields, 0..) |f, i| {
js_args[i] = (try local.zigValueToJs(@field(aargs, f.name), .{})).handle;
const field_names = s.field_names;
var js_args: [field_names.len]*const v8.Value = undefined;
inline for (field_names, 0..) |field_name, i| {
js_args[i] = (try local.zigValueToJs(@field(aargs, field_name), .{})).handle;
}
const cargs: [fields.len]*const v8.Value = js_args;
const cargs: [field_names.len]*const v8.Value = js_args;
break :blk &cargs;
},
.pointer => blk: {
+1 -1
View File
@@ -48,7 +48,7 @@ pub const MemoryPressureLevel = enum(u32) {
};
pub fn memoryPressureNotification(self: Isolate, level: MemoryPressureLevel) void {
v8.v8__Isolate__MemoryPressureNotification(self.handle, @intFromEnum(level));
v8.v8__Isolate__MemoryPressureNotification(self.handle, @backingInt(level));
}
// Tells V8 how much native memory is hanging off objects in this isolate, so
+35 -35
View File
@@ -496,9 +496,9 @@ pub fn zigValueToJs(self: *const Local, value: anytype, comptime opts: CallOpts)
if (s.is_tuple) {
// return the tuple struct as an array
var js_arr = self.newArray(@intCast(s.fields.len));
inline for (s.fields, 0..) |f, i| {
if (try js_arr.set(@intCast(i), @field(value, f.name), opts) == false) {
var js_arr = self.newArray(@intCast(s.field_names.len));
inline for (s.field_names, 0..) |field_name, i| {
if (try js_arr.set(@intCast(i), @field(value, field_name), opts) == false) {
return error.FailedToCreateArray;
}
}
@@ -506,8 +506,8 @@ pub fn zigValueToJs(self: *const Local, value: anytype, comptime opts: CallOpts)
}
const js_obj = self.newObject();
inline for (s.fields) |f| {
if (try js_obj.set(f.name, @field(value, f.name), opts) == false) {
inline for (s.field_names) |field_name| {
if (try js_obj.set(field_name, @field(value, field_name), opts) == false) {
return error.CreateObjectFailure;
}
}
@@ -518,9 +518,9 @@ pub fn zigValueToJs(self: *const Local, value: anytype, comptime opts: CallOpts)
return self.zigJsonToJs(value);
}
if (un.tag_type) |UnionTagType| {
inline for (un.fields) |field| {
if (value == @field(UnionTagType, field.name)) {
return self.zigValueToJs(@field(value, field.name), opts);
inline for (un.field_names) |field_name| {
if (value == @field(UnionTagType, field_name)) {
return self.zigValueToJs(@field(value, field_name), opts);
}
}
unreachable;
@@ -695,13 +695,13 @@ pub fn jsValueToZig(self: *const Local, comptime T: type, js_val: js.Value) !T {
// compatible with. A compatible field has higher precedence
// than a coercible, but still isn't a perfect match.
var compatible_index: ?usize = null;
inline for (u.fields, 0..) |field, i| {
switch (try self.probeJsValueToZig(field.type, js_val)) {
.value => |v| return @unionInit(T, field.name, v),
inline for (u.field_names, u.field_types, 0..) |field_name, field_type, i| {
switch (try self.probeJsValueToZig(field_type, js_val)) {
.value => |v| return @unionInit(T, field_name, v),
.ok => {
// a perfect match like above case, except the probing
// didn't get the value for us.
return @unionInit(T, field.name, try self.jsValueToZig(field.type, js_val));
return @unionInit(T, field_name, try self.jsValueToZig(field_type, js_val));
},
.coerce => if (coerce_index == null) {
coerce_index = i;
@@ -715,9 +715,9 @@ pub fn jsValueToZig(self: *const Local, comptime T: type, js_val: js.Value) !T {
// We didn't find a perfect match.
const closest = compatible_index orelse coerce_index orelse return error.InvalidArgument;
inline for (u.fields, 0..) |field, i| {
inline for (u.field_names, u.field_types, 0..) |field_name, field_type, i| {
if (i == closest) {
return @unionInit(T, field.name, try self.jsValueToZig(field.type, js_val));
return @unionInit(T, field_name, try self.jsValueToZig(field_type, js_val));
}
}
unreachable;
@@ -767,7 +767,7 @@ fn jsValueToStruct(self: *const Local, comptime T: type, js_val: js.Value) !?T {
js.TypedArray(f32), js.TypedArray(f64),
// zig fmt: on
=> {
const ValueType = @typeInfo(std.meta.fieldInfo(T, .values).type).pointer.child;
const ValueType = @typeInfo(@FieldType(T, "values")).pointer.child;
const arr = (try jsValueToTypedArray(ValueType, js_val)) orelse return null;
return .{ .values = arr };
},
@@ -846,27 +846,27 @@ fn jsValueToStruct(self: *const Local, comptime T: type, js_val: js.Value) !?T {
const js_obj = js_val.toObject();
var value: T = undefined;
inline for (@typeInfo(T).@"struct".fields) |field| {
if (comptime std.mem.eql(u8, field.name, dictionary_group_marker)) {
const info = @typeInfo(T).@"struct";
inline for (info.field_names, info.field_types, info.field_attrs) |name, FieldType, attrs| {
if (comptime std.mem.eql(u8, name, dictionary_group_marker)) {
continue;
}
const name = field.name;
const key = isolate.initStringHandle(name);
if (js_obj.has(key)) {
const member = try js_obj.get(key);
const default_for_undefined = comptime field.defaultValue();
const default_for_undefined = comptime attrs.defaultValue(FieldType);
@field(value, name) = blk: {
if (comptime default_for_undefined) |dflt| {
if (member.isUndefined()) {
break :blk dflt;
}
}
break :blk try self.jsValueToZig(field.type, member);
break :blk try self.jsValueToZig(FieldType, member);
};
} else if (@typeInfo(field.type) == .optional) {
} else if (@typeInfo(FieldType) == .optional) {
@field(value, name) = null;
} else {
const dflt = field.defaultValue() orelse return null;
const dflt = attrs.defaultValue(FieldType) orelse return null;
@field(value, name) = dflt;
}
}
@@ -883,10 +883,10 @@ pub const dictionary_group_marker = "js_grouped_dictionary";
// fire in a predictable order. Could probably comptime this to work
// automatically, but it's a lot easier just to check it and ask for a manual fix.
pub fn assertDictionaryFieldOrder(comptime T: type) void {
const fields = @typeInfo(T).@"struct".fields;
const field_names = @typeInfo(T).@"struct".field_names;
var i: usize = 1;
while (i < fields.len) : (i += 1) {
if (std.mem.order(u8, fields[i - 1].name, fields[i].name) == .gt) {
while (i < field_names.len) : (i += 1) {
if (std.mem.order(u8, field_names[i - 1], field_names[i]) == .gt) {
@compileError("dictionary fields must be declared in lexicographic order: " ++ @typeName(T));
}
}
@@ -1350,17 +1350,17 @@ fn resolveValue(value: anytype) Resolved {
}
const U = @typeInfo(@TypeOf(value._type)).@"union";
inline for (U.fields) |field| {
if (value._type == @field(U.tag_type.?, field.name)) {
const child = switch (@typeInfo(field.type)) {
.pointer => @field(value._type, field.name),
.@"struct" => &@field(value._type, field.name),
inline for (U.field_names, U.field_types) |field_name, field_type| {
if (value._type == @field(U.tag_type.?, field_name)) {
const child = switch (@typeInfo(field_type)) {
.pointer => @field(value._type, field_name),
.@"struct" => &@field(value._type, field_name),
.void => {
// Unusual case, but the Event (and maybe others) can be
// returned as-is. In that case, it has a dummy void type.
return resolveT(T, value);
},
else => @compileError(@typeName(field.type) ++ " has an unsupported _type field"),
else => @compileError(@typeName(field_type) ++ " has an unsupported _type field"),
};
return resolveValue(child);
}
@@ -1573,8 +1573,8 @@ pub fn ToLocalReturnType(comptime T: type) type {
if (@typeInfo(T) == .optional) {
const GlobalType = @typeInfo(T).optional.child;
const struct_info = @typeInfo(GlobalType).@"struct";
inline for (struct_info.decls) |decl| {
if (std.mem.eql(u8, decl.name, "local")) {
inline for (struct_info.decl_names) |decl_name| {
if (std.mem.eql(u8, decl_name, "local")) {
const Fn = @TypeOf(@field(GlobalType, "local"));
const fn_info = @typeInfo(Fn).@"fn";
return ?fn_info.return_type.?;
@@ -1583,8 +1583,8 @@ pub fn ToLocalReturnType(comptime T: type) type {
@compileError("Type does not have local method");
} else {
const struct_info = @typeInfo(T).@"struct";
inline for (struct_info.decls) |decl| {
if (std.mem.eql(u8, decl.name, "local")) {
inline for (struct_info.decl_names) |decl_name| {
if (std.mem.eql(u8, decl_name, "local")) {
const Fn = @TypeOf(@field(T, "local"));
const fn_info = @typeInfo(Fn).@"fn";
return fn_info.return_type.?;
+1 -1
View File
@@ -36,7 +36,7 @@ pub const Status = enum(u32) {
};
pub fn getStatus(self: Module) Status {
return @enumFromInt(v8.v8__Module__GetStatus(self.handle));
return @fromBackingInt(@intCast(v8.v8__Module__GetStatus(self.handle)));
}
pub fn getException(self: Module) js.Value {
+1 -1
View File
@@ -55,7 +55,7 @@ pub const State = enum(u32) {
};
pub fn state(self: Promise) State {
return @enumFromInt(v8.v8__Promise__State(self.handle));
return @fromBackingInt(@intCast(v8.v8__Promise__State(self.handle)));
}
/// Settled value (fulfillment or rejection). Caller must check `state` first.
+21 -22
View File
@@ -348,10 +348,10 @@ fn createSnapshotContext(
const func_obj: *const v8.Object = @ptrCast(func);
if (v8.v8__Object__Get(func_obj, context, prototype_key)) |proto_handle| {
const proto_obj: *const v8.Object = @ptrCast(proto_handle);
inline for (@typeInfo(JsApi).@"struct".decls) |d| {
const exposed = comptime memberExposed(@field(JsApi, d.name));
inline for (@typeInfo(JsApi).@"struct".decl_names) |decl_name| {
const exposed = comptime memberExposed(@field(JsApi, decl_name));
if (comptime exposed != .both and exposed != realm.asExposed()) {
const name: [:0]const u8 = d.name;
const name: [:0]const u8 = decl_name;
const name_v8 = v8.v8__String__NewFromUtf8(isolate, name.ptr, v8.kNormal, @intCast(name.len));
var maybe_deleted: v8.MaybeBool = undefined;
v8.v8__Object__Delete(proto_obj, context, name_v8, &maybe_deleted);
@@ -399,8 +399,8 @@ fn createSnapshotContext(
fn hasGatedMember(comptime JsApi: type) bool {
comptime {
for (@typeInfo(JsApi).@"struct".decls) |d| {
if (memberExposed(@field(JsApi, d.name)) != .both) {
for (@typeInfo(JsApi).@"struct".decl_names) |decl_name| {
if (memberExposed(@field(JsApi, decl_name)) != .both) {
return true;
}
}
@@ -444,9 +444,9 @@ fn countExternalReferences() comptime_int {
count += 1;
}
const declarations = @typeInfo(JsApi).@"struct".decls;
inline for (declarations) |d| {
const value = @field(JsApi, d.name);
const decl_names = @typeInfo(JsApi).@"struct".decl_names;
inline for (decl_names) |decl_name| {
const value = @field(JsApi, decl_name);
const T = @TypeOf(value);
if (T == bridge.Accessor) {
if (value.wpt_only and wpt_extensions_enabled == false) {
@@ -524,9 +524,9 @@ fn collectExternalReferences() [countExternalReferences()]isize {
idx += 1;
}
const declarations = @typeInfo(JsApi).@"struct".decls;
inline for (declarations) |d| {
const value = @field(JsApi, d.name);
const decl_names = @typeInfo(JsApi).@"struct".decl_names;
inline for (decl_names) |decl_name| {
const value = @field(JsApi, decl_name);
const T = @TypeOf(value);
if (T == bridge.Accessor) {
if (value.wpt_only and wpt_extensions_enabled == false) {
@@ -619,8 +619,8 @@ fn countInternalFields(comptime JsApi: type) u8 {
var last_used_id = 0;
var cache_count: u8 = 0;
inline for (@typeInfo(JsApi).@"struct".decls) |d| {
const name: [:0]const u8 = d.name;
inline for (@typeInfo(JsApi).@"struct".decl_names) |decl_name| {
const name: [:0]const u8 = decl_name;
const value = @field(JsApi, name);
const definition = @TypeOf(value);
@@ -687,9 +687,9 @@ fn illegalConstructorCallback(raw_info: ?*const v8.FunctionCallbackInfo) callcon
// Helper to check if a JsApi has a NamedIndexed handler (public for reuse)
fn hasNamedIndexedGetter(comptime JsApi: type) bool {
const declarations = @typeInfo(JsApi).@"struct".decls;
inline for (declarations) |d| {
const value = @field(JsApi, d.name);
const decl_names = @typeInfo(JsApi).@"struct".decl_names;
inline for (decl_names) |decl_name| {
const value = @field(JsApi, decl_name);
const T = @TypeOf(value);
if (T == bridge.NamedIndexed) {
return true;
@@ -805,13 +805,12 @@ fn attachClass(comptime JsApi: type, comptime flatten: bool, isolate: *v8.Isolat
const own_properties = @hasDecl(JsApi.Meta, "own_properties") and JsApi.Meta.own_properties;
const member_template = if (own_properties) instance else prototype;
const declarations = @typeInfo(JsApi).@"struct".decls;
const decl_names = @typeInfo(JsApi).@"struct".decl_names;
var has_named_index_getter = false;
const wpt_extensions_enabled = lp.build_config.wpt_extensions;
inline for (declarations) |d| {
const name: [:0]const u8 = d.name;
inline for (decl_names) |name| {
const value = @field(JsApi, name);
const definition = @TypeOf(value);
@@ -992,10 +991,10 @@ const unforgeables: []const Unforgeable = blk: {
@setEvalBranchQuota(100_000);
var list: []const Unforgeable = &.{};
for (JsApis) |Api| {
for (@typeInfo(Api).@"struct".decls) |d| {
const value = @field(Api, d.name);
for (@typeInfo(Api).@"struct".decl_names) |decl_name| {
const value = @field(Api, decl_name);
if (@TypeOf(value) == bridge.Accessor and value.unforgeable and !value.static) {
list = list ++ &[_]Unforgeable{.{ .Owner = Api, .name = d.name, .accessor = value }};
list = list ++ &[_]Unforgeable{.{ .Owner = Api, .name = decl_name, .accessor = value }};
}
}
}
+1 -1
View File
@@ -26,7 +26,7 @@ const bridge = js.bridge;
//
// In a simple/perfect world, we could use this knowledge to cast the *anyopaque
// to the parameter type:
// const arg: @typeInfo(@TypeOf(function)).@"fn".params[0] = @ptrCast(v8_data);
// const arg: @typeInfo(@TypeOf(function)).@"fn".param_types[0].? = @ptrCast(v8_data);
//
// But there are 2 reasons we can't do that.
//
+4 -4
View File
@@ -136,10 +136,10 @@ pub const Caught = struct {
pub fn logFmt(self: Caught, prefix: []const u8, writer: anytype) !void {
var buf: [64]u8 = undefined;
try writer.write(try std.fmt.bufPrint(&buf, "{s}.exception", .{prefix}), self.exception orelse "???");
try writer.write(try std.fmt.bufPrint(&buf, "{s}.stack", .{prefix}), self.stack orelse "na");
try writer.write(try std.fmt.bufPrint(&buf, "{s}.line", .{prefix}), self.line);
try writer.write(try std.fmt.bufPrint(&buf, "{s}.caught", .{prefix}), self.caught);
try writer.write(try std.mem.print(&buf, "{s}.exception", .{prefix}), self.exception orelse "???");
try writer.write(try std.mem.print(&buf, "{s}.stack", .{prefix}), self.stack orelse "na");
try writer.write(try std.mem.print(&buf, "{s}.line", .{prefix}), self.line);
try writer.write(try std.mem.print(&buf, "{s}.caught", .{prefix}), self.caught);
}
pub fn jsonStringify(self: Caught, jw: anytype) !void {
+2 -2
View File
@@ -853,7 +853,7 @@ const Inert = struct {
if (i != 0) {
try writer.writeByte(',');
}
const key = std.fmt.bufPrint(&key_buf, "{d}", .{i}) catch unreachable;
const key = std.mem.print(&key_buf, "{d}", .{i}) catch unreachable;
const element = self.ownDataProperty(@ptrCast(handle), key) orelse continue;
if (v8.v8__Value__IsNullOrUndefined(element)) {
continue;
@@ -1012,7 +1012,7 @@ test "Value: inert formatting runs no page JS" {
};
for (cases) |case| {
const value = try ls.local.exec(case.expr, null);
const out = try std.fmt.allocPrint(testing.allocator, "{f}", .{value});
const out = try testing.allocator.print("{f}", .{value});
defer testing.allocator.free(out);
try testing.expectEqualSlices(u8, case.expected, out);
}
+14 -14
View File
@@ -207,9 +207,9 @@ pub const Function = struct {
const Session = @import("../Session.zig");
var count: usize = 0;
var params = @typeInfo(T).@"fn".params;
for (params[start..]) |p| { // start at 1, skip self
const PT = p.type.?;
const param_types = @typeInfo(T).@"fn".param_types;
for (param_types[start..]) |param_type| { // start at 1, skip self
const PT = param_type.?;
if (PT == *Frame or PT == *const Frame) {
break;
}
@@ -440,18 +440,18 @@ pub const Indexed = struct {
fn hasNotHandled(comptime E: type) bool {
// anyerror includes it
const errors = @typeInfo(E).error_set orelse return true;
for (errors) |e| {
if (std.mem.eql(u8, e.name, "NotHandled")) return true;
const error_names = @typeInfo(E).error_set.error_names orelse return true;
for (error_names) |name| {
if (std.mem.eql(u8, name, "NotHandled")) return true;
}
return false;
}
// Default index query if one isn't provided. Uses the getter to determine the result
fn GetterQuery(comptime getter: anytype, comptime attrs: u32) type {
const params = @typeInfo(@TypeOf(getter)).@"fn".params;
const Self = params[0].type.?;
const Index = params[1].type.?;
const param_types = @typeInfo(@TypeOf(getter)).@"fn".param_types;
const Self = param_types[0].?;
const Index = param_types[1].?;
// A getter that can return neither null nor error.NotHandled would report
// every index as present.
@@ -465,13 +465,13 @@ fn GetterQuery(comptime getter: anytype, comptime attrs: u32) type {
}
return struct {
const query = if (params.len == 3) withGlobal else plain;
const query = if (param_types.len == 3) withGlobal else plain;
fn plain(self: Self, idx: Index) !u32 {
return attributes(getter(self, idx));
}
fn withGlobal(self: Self, idx: Index, global: params[2].type.?) !u32 {
fn withGlobal(self: Self, idx: Index, global: param_types[2].?) !u32 {
return attributes(getter(self, idx, global));
}
@@ -804,7 +804,7 @@ pub fn unknownWindowPropertyCallback(c_name: ?*const v8.Name, handle: ?*const v8
// });
// if (!ignored.has(property)) {
// var buf: [2048]u8 = undefined;
// const key = std.fmt.bufPrint(&buf, "Window:{s}", .{property}) catch return js.Intercepted.no;
// const key = std.mem.print(&buf, "Window:{s}", .{property}) catch return js.Intercepted.no;
// logUnknownProperty(local, key) catch return js.Intercepted.no;
// }
// }
@@ -870,7 +870,7 @@ pub fn unknownWindowPropertyCallback(c_name: ?*const v8.Name, handle: ?*const v8
// const ignored = std.StaticStringMap(void).initComptime(.{});
// if (!ignored.has(property)) {
// var buf: [2048]u8 = undefined;
// const key = std.fmt.bufPrint(&buf, "{s}:{s}", .{ if (@hasDecl(JsApi.Meta, "name")) JsApi.Meta.name else @typeName(JsApi), property }) catch return js.Intercepted.no;
// const key = std.mem.print(&buf, "{s}:{s}", .{ if (@hasDecl(JsApi.Meta, "name")) JsApi.Meta.name else @typeName(JsApi), property }) catch return js.Intercepted.no;
// logUnknownProperty(local, key) catch return js.Intercepted.no;
// }
// return js.Intercepted.no;
@@ -1001,7 +1001,7 @@ pub const JsApiLookup = struct {
/// Returns the ID for the given type.
pub inline fn getId(t: type) BackingInt {
return @intFromEnum(getIndex(t));
return @backingInt(getIndex(t));
}
};
+2 -1
View File
@@ -29,6 +29,7 @@ const Slot = @import("webapi/element/html/Slot.zig");
const LimitedWriter = @import("../LimitedWriter.zig");
const isAllWhitespace = @import("../string.zig").isAllWhitespace;
const repeat = @import("../string.zig").repeat;
pub const Opts = struct {
max_bytes: ?u32 = null,
@@ -895,7 +896,7 @@ test "browser.markdown: max_bytes truncates with marker" {
const doc = frame.window._document;
const div = try doc.createElement("div", null, frame);
try Frame.parse.htmlAsChildren(frame, div.asNode(), "<p>" ++ ("AAAA " ** 100) ++ "</p>");
try Frame.parse.htmlAsChildren(frame, div.asNode(), "<p>" ++ (repeat("AAAA ", 100)) ++ "</p>");
var aw: std.Io.Writer.Allocating = .init(testing.allocator);
defer aw.deinit();
+2 -2
View File
@@ -542,7 +542,7 @@ fn _createElementCallback(self: *Parser, data: *anyopaque, qname: h5e.QualName,
// like createElementNS. html5ever never sets a prefix; xml5ever does.
const name = if (qname.prefix.unwrap()) |prefix| blk: {
if (prefix.len == 0) break :blk local;
break :blk try std.fmt.allocPrint(frame.local_arena, "{s}:{s}", .{ prefix.slice(), local });
break :blk try frame.local_arena.print("{s}:{s}", .{ prefix.slice(), local });
} else local;
const namespace_string = qname.ns.slice();
const namespace = if (namespace_string.len == 0) default_namespace else Element.Namespace.parse(namespace_string);
@@ -953,7 +953,7 @@ fn getNode(ref: *anyopaque) *Node {
return getParsed(ref).node;
}
fn asUint(comptime string: anytype) std.meta.Int(
fn asUint(comptime string: anytype) @Int(
.unsigned,
@bitSizeOf(@TypeOf(string.*)) - 8, // (- 8) to exclude sentinel 0
) {
+8 -8
View File
@@ -291,7 +291,7 @@ const Document = struct {
const layout = self.layout;
const lines = layout.lines();
for (layout.blocks(), 0..) |b, bi| {
if (b.kind == @intFromEnum(screenshot.LpBlock.Kind.rule)) {
if (b.kind == @backingInt(screenshot.LpBlock.Kind.rule)) {
const pg, const py = self.pageOf(b.y);
if (self.selected[pg]) {
try rect(&self.pages[pg].writer, layout.raw.rule_color, b.x, py, self.column_w - b.x, 1);
@@ -311,7 +311,7 @@ const Document = struct {
const h = boxes[j].bottom - boxes[i].top;
if (self.selected[pg]) {
const w = &self.pages[pg].writer;
if (b.kind == @intFromEnum(screenshot.LpBlock.Kind.pre) and self.opts.print_background) {
if (b.kind == @backingInt(screenshot.LpBlock.Kind.pre) and self.opts.print_background) {
const x0 = b.x - layout.raw.pre_pad;
try rect(w, layout.raw.pre_bg, x0, top, self.column_w - x0, h);
}
@@ -351,7 +351,7 @@ const Document = struct {
/// Each line's vertical extent. A <pre> grows its first and last line by
/// the padding so the background stays with the text across a break.
fn lineBoxes(self: *const Document, b: screenshot.LpLayoutBlock, block_lines: []const screenshot.LpLine) ![]const Box {
const pad: f32 = if (b.kind == @intFromEnum(screenshot.LpBlock.Kind.pre)) self.layout.raw.pre_pad else 0;
const pad: f32 = if (b.kind == @backingInt(screenshot.LpBlock.Kind.pre)) self.layout.raw.pre_pad else 0;
const boxes = try self.arena.alloc(Box, block_lines.len);
for (boxes, block_lines, 0..) |*box, line, i| {
box.* = .{
@@ -584,11 +584,11 @@ fn paginate(arena: Allocator, layout: *const screenshot.Layout, page_h: f32) ![]
try breaks.append(arena, 0);
const lines = layout.lines();
for (layout.blocks()) |b| {
if (b.kind == @intFromEnum(screenshot.LpBlock.Kind.rule)) {
if (b.kind == @backingInt(screenshot.LpBlock.Kind.rule)) {
try fit(arena, &breaks, b.y, b.y + 1, page_h);
continue;
}
const pad: f32 = if (b.kind == @intFromEnum(screenshot.LpBlock.Kind.pre)) layout.raw.pre_pad else 0;
const pad: f32 = if (b.kind == @backingInt(screenshot.LpBlock.Kind.pre)) layout.raw.pre_pad else 0;
for (lines[b.lines..][0..b.lines_len], 0..) |line, i| {
const top = line.top - if (i == 0) pad else 0;
const bottom = line.bottom + if (i + 1 == b.lines_len) pad else 0;
@@ -673,10 +673,10 @@ const FontUse = struct {
const ttf = &self.ttf;
const k = 1000 / @as(f32, @floatFromInt(ttf.upem));
const gids = try self.sortedGids(arena);
const name = try std.fmt.allocPrint(arena, "{s}+{s}", .{ SUBSET_TAG, self.info.name[0..self.info.name_len] });
const name = try arena.print("{s}+{s}", .{ SUBSET_TAG, self.info.name[0..self.info.name_len] });
const file = ttf.subset(arena, gids) catch ttf.data;
const file_id = try out.stream(try std.fmt.allocPrint(arena, "/Length1 {d}", .{file.len}), file);
const file_id = try out.stream(try arena.print("/Length1 {d}", .{file.len}), file);
const descriptor = try out.alloc();
try out.begin(descriptor);
@@ -988,7 +988,7 @@ const Out = struct {
}
fn print(self: *Out, comptime fmt: []const u8, args: anytype) !void {
try self.write(try std.fmt.allocPrint(self.arena, fmt, args));
try self.write(try self.arena.print(fmt, args));
}
fn alloc(self: *Out) !usize {
+5 -4
View File
@@ -19,6 +19,7 @@
// Referrer Policy: https://www.w3.org/TR/referrer-policy/
const std = @import("std");
const URL = @import("URL.zig");
const repeat = @import("../string.zig").repeat;
const Allocator = std.mem.Allocator;
@@ -106,7 +107,7 @@ pub fn compute(arena: Allocator, policy: Policy, referrer_url: [:0]const u8, tar
if (full) {
// Serializing through origin + path + query strips credentials and
// the fragment, and normalizes away default ports.
const value = try std.fmt.allocPrint(arena, "{s}{s}{s}", .{
const value = try arena.print("{s}{s}{s}", .{
referrer_origin,
URL.getPathname(referrer_url),
URL.getSearch(referrer_url),
@@ -117,7 +118,7 @@ pub fn compute(arena: Allocator, policy: Policy, referrer_url: [:0]const u8, tar
return value;
}
}
return try std.fmt.allocPrint(arena, "{s}/", .{referrer_origin});
return try arena.print("{s}/", .{referrer_origin});
}
fn staticStringMapEqlAsciiIgnoreCase(a: []const u8, b: []const u8) bool {
@@ -207,14 +208,14 @@ test "referrer: compute" {
}
test "referrer: compute caps at 4096 bytes" {
const path = "/" ++ ("a" ** 4096);
const path = "/" ++ (repeat("a", 4096));
const url = "http://a.com" ++ path;
// over the cap: falls back to the origin form
try testing.expectEqual("http://a.com/", (try compute(testing.arena_allocator, .unsafe_url, url, "http://b.com/x")).?);
try testing.expectEqual("http://a.com/", (try compute(testing.arena_allocator, .no_referrer_when_downgrade, url, "http://a.com/x")).?);
// exactly at the cap: sent in full
const at_cap = "http://a.com/" ++ ("a" ** (4096 - "http://a.com/".len));
const at_cap = "http://a.com/" ++ (repeat("a", 4096 - "http://a.com/".len));
try testing.expectEqual(at_cap, (try compute(testing.arena_allocator, .unsafe_url, at_cap, "http://b.com/x")).?);
// origin-only policies are unaffected by the referrer's length
+7 -7
View File
@@ -887,7 +887,7 @@ const Builder = struct {
if (stray) self.list_depth = 1;
if (!stray and self.list_stack[self.list_depth - 1].ordered) {
const state = &self.list_stack[self.list_depth - 1];
self.pending_marker = try std.fmt.allocPrint(self.arena, "{d}.", .{state.index});
self.pending_marker = try self.arena.print("{d}.", .{state.index});
state.index += 1;
} else {
self.pending_marker = "•";
@@ -1171,9 +1171,9 @@ test "browser.screenshot: rust abi matches" {
.span_strike = SPAN_STRIKE,
.span_has_color = SPAN_HAS_COLOR,
.block_heading = @intFromEnum(LpBlock.Kind.heading),
.block_pre = @intFromEnum(LpBlock.Kind.pre),
.block_rule = @intFromEnum(LpBlock.Kind.rule),
.block_heading = @backingInt(LpBlock.Kind.heading),
.block_pre = @backingInt(LpBlock.Kind.pre),
.block_rule = @backingInt(LpBlock.Kind.rule),
.block_tight = BLOCK_TIGHT,
.render_measure_only = RENDER_MEASURE_ONLY,
@@ -1189,9 +1189,9 @@ test "browser.screenshot: rust abi matches" {
// Size first: a field present on only one side of LpAbi itself shifts
// everything after it, and the per-field loop would just report noise.
try testing.expectEqual(expected.size, got.size);
inline for (@typeInfo(LpAbi).@"struct".fields) |f| {
testing.expectEqual(@field(expected, f.name), @field(got, f.name)) catch |err| {
std.debug.print("rust/zig abi mismatch on {s}\n", .{f.name});
inline for (@typeInfo(LpAbi).@"struct".field_names) |field_name| {
testing.expectEqual(@field(expected, field_name), @field(got, field_name)) catch |err| {
std.debug.print("rust/zig abi mismatch on {s}\n", .{field_name});
return err;
};
}
+45 -47
View File
@@ -783,11 +783,11 @@ const url_params_schema = minify(
\\}
);
/// Materialized form of `Tool.definition` keyed by `@intFromEnum(Tool)`.
/// Materialized form of `Tool.definition` keyed by `@backingInt(Tool)`.
/// Built at comptime by iterating every `Tool` tag — order and count
/// can't drift because both come from the enum itself.
pub const tool_defs: [@typeInfo(Tool).@"enum".fields.len]Tool.Definition = blk: {
var arr: [@typeInfo(Tool).@"enum".fields.len]Tool.Definition = undefined;
pub const tool_defs: [@typeInfo(Tool).@"enum".field_names.len]Tool.Definition = blk: {
var arr: [@typeInfo(Tool).@"enum".field_names.len]Tool.Definition = undefined;
for (std.enums.values(Tool), 0..) |t, i| arr[i] = t.definition();
break :blk arr;
};
@@ -795,10 +795,10 @@ pub const tool_defs: [@typeInfo(Tool).@"enum".fields.len]Tool.Definition = blk:
/// Comptime-built flat array of tool names, in `Tool` declaration order.
/// Use this when callers only need the names (slash-command lookup, MCP
/// `tools/list`).
pub const names: [@typeInfo(Tool).@"enum".fields.len][]const u8 = blk: {
const fields = @typeInfo(Tool).@"enum".fields;
var arr: [fields.len][]const u8 = undefined;
for (fields, 0..) |f, i| arr[i] = f.name;
pub const names: [@typeInfo(Tool).@"enum".field_names.len][]const u8 = blk: {
const field_names = @typeInfo(Tool).@"enum".field_names;
var arr: [field_names.len][]const u8 = undefined;
for (field_names, 0..) |field_name, i| arr[i] = field_name;
break :blk arr;
};
@@ -913,7 +913,7 @@ pub fn call(
// `multi_tool_use.parallel` wrapper) learns the name is wrong instead of
// retrying it with different arguments.
const tool = std.meta.stringToEnum(Tool, tool_name) orelse return .{
.text = try std.fmt.allocPrint(arena, "Unknown tool: {s}", .{tool_name}),
.text = try arena.print("Unknown tool: {s}", .{tool_name}),
.is_error = true,
};
if (diagnoseArgs(arena, arguments)) |msg|
@@ -1012,7 +1012,7 @@ fn dispatch(
fn formatNavigationError(arena: std.mem.Allocator, session: *lp.Session) ?[]const u8 {
const frame = session.currentFrame() orelse return null;
const err = frame._last_navigate_error orelse return null;
return std.fmt.allocPrint(arena, "navigation failed: {s}", .{@errorName(err)}) catch null;
return arena.print("navigation failed: {s}", .{@errorName(err)}) catch null;
}
/// Run JavaScript against the current page. The script need not be
@@ -1023,7 +1023,7 @@ pub fn evalScript(
registry: *NodeRegistry,
script: []const u8,
) ToolError!ToolResult {
const z = try arena.dupeZ(u8, script);
const z = try arena.dupeSentinel(u8, script, 0);
const page = try ensurePage(session, registry, null, null);
return runEval(arena, page, z, null);
}
@@ -1109,9 +1109,9 @@ fn navStatus(arena: std.mem.Allocator, frame: *const lp.Frame) []const u8 {
// `std.http.Status` is an enum(u10) and `_http_status` is only clamped to
// u16 (`http.getResponseCode`), so a server answering with a 4-digit code
// would make the cast illegal behaviour rather than an unknown phrase.
const phrase = if (status > 599) "" else @as(std.http.Status, @enumFromInt(status)).phrase() orelse "";
if (phrase.len == 0) return std.fmt.allocPrint(arena, "{d}", .{status}) catch "unknown";
return std.fmt.allocPrint(arena, "{d} {s}", .{ status, phrase }) catch "unknown";
const phrase = if (status > 599) "" else @as(std.http.Status, @fromBackingInt(@intCast(status))).phrase() orelse "";
if (phrase.len == 0) return arena.print("{d}", .{status}) catch "unknown";
return arena.print("{d} {s}", .{ status, phrase }) catch "unknown";
}
fn execGoto(arena: std.mem.Allocator, session: *lp.Session, registry: *NodeRegistry, arguments: ?std.json.Value) ToolError![]const u8 {
@@ -1119,8 +1119,8 @@ fn execGoto(arena: std.mem.Allocator, session: *lp.Session, registry: *NodeRegis
const result = try performGoto(session, registry, args.url, .{ .timeout = args.timeout, .wait_until = args.waitUntil });
const status = if (session.currentFrame()) |frame| navStatus(arena, frame) else "unknown";
return switch (result) {
.completed => std.fmt.allocPrint(arena, "Navigated successfully. HTTP {s}.", .{status}),
.timeout => std.fmt.allocPrint(arena, "Navigation started (HTTP {s}) but the page did not finish loading before the timeout.", .{status}),
.completed => arena.print("Navigated successfully. HTTP {s}.", .{status}),
.timeout => arena.print("Navigation started (HTTP {s}) but the page did not finish loading before the timeout.", .{status}),
} catch ToolError.InternalError;
}
@@ -1521,17 +1521,17 @@ fn execScreenshot(arena: std.mem.Allocator, session: *lp.Session, registry: *Nod
if (args.path) |path| {
const content_height = writePng(&prepared, path) catch |err| return .{
.text = std.fmt.allocPrint(arena, "could not write {s}: {s}", .{ path, @errorName(err) }) catch return ToolError.OutOfMemory,
.text = arena.print("could not write {s}: {s}", .{ path, @errorName(err) }) catch return ToolError.OutOfMemory,
.is_error = true,
};
// The renderer reports the content height; a fixed strip is its own height.
const height = if (prepared.opts.height == 0) content_height else prepared.opts.height;
return .{ .text = std.fmt.allocPrint(arena, "Saved {d}x{d} PNG to {s}", .{ prepared.opts.width, height, absolutePath(arena, path) }) catch return ToolError.OutOfMemory };
return .{ .text = arena.print("Saved {d}x{d} PNG to {s}", .{ prepared.opts.width, height, absolutePath(arena, path) }) catch return ToolError.OutOfMemory };
}
prepared.fit(inline_image_max_width, inline_image_max_height) catch return ToolError.InternalError;
return .{
.text = std.fmt.allocPrint(arena, "PNG, {d}x{d}", .{ prepared.opts.width, prepared.opts.height }) catch return ToolError.OutOfMemory,
.text = arena.print("PNG, {d}x{d}", .{ prepared.opts.width, prepared.opts.height }) catch return ToolError.OutOfMemory,
.image = prepared,
};
}
@@ -1646,14 +1646,12 @@ fn execEvaluate(arena: std.mem.Allocator, session: *lp.Session, registry: *NodeR
// Block scope preserves a trailing expression's value and keeps top-level
// `let`/`const` from leaking; top-level `await`/`return` need the async IIFE.
const block_script = std.fmt.allocPrintSentinel(
arena,
const block_script = arena.printSentinel(
"{{ {s}\n}}",
.{args.script},
0,
) catch return ToolError.OutOfMemory;
const iife_script = std.fmt.allocPrintSentinel(
arena,
const iife_script = arena.printSentinel(
"(async function(){{ \"use strict\"; {s} }})()",
.{args.script},
0,
@@ -1697,7 +1695,7 @@ fn execEvaluate(arena: std.mem.Allocator, session: *lp.Session, registry: *NodeR
if (result.text.len == 0) return result; // silenced save=; don't re-emit via nav suffix
const page_title = after.getTitle() catch null;
const text = std.fmt.allocPrint(arena, "{s}\n(Navigated to {s}, HTTP {s}, title: {s})", .{
const text = arena.print("{s}\n(Navigated to {s}, HTTP {s}, title: {s})", .{
result.text, after.url, navStatus(arena, after), page_title orelse "(none)",
}) catch return ToolError.InternalError;
return .{ .text = text };
@@ -1811,7 +1809,7 @@ fn bridgePrelude(arena: std.mem.Allocator, store: *const BridgeStore) ![:0]const
try aw.writer.writeAll(kv.value_ptr.*);
}
try aw.writer.writeAll("};");
return arena.dupeZ(u8, aw.written());
return arena.dupeSentinel(u8, aw.written(), 0);
}
const bridge_postlude: [:0]const u8 = "JSON.stringify(globalThis.lp)";
@@ -1925,7 +1923,7 @@ fn formatActionResult(
target: ActionTarget,
suffix: []const u8,
) ToolError![]const u8 {
return std.fmt.allocPrint(arena, "{s} ({f}){s}", .{ prefix, target, suffix }) catch ToolError.InternalError;
return arena.print("{s} ({f}){s}", .{ prefix, target, suffix }) catch ToolError.InternalError;
}
/// What `finalizeAction` compares against; take it before the action runs.
@@ -1972,7 +1970,7 @@ fn finalizeAction(arena: std.mem.Allocator, session: *lp.Session, registry: *Nod
}
const page_title = page.getTitle() catch null;
return std.fmt.allocPrint(arena, "{s}.{s} Page url: {s}, HTTP {s}, title: {s}", .{
return arena.print("{s}.{s} Page url: {s}, HTTP {s}, title: {s}", .{
body, note, page.url, navStatus(arena, page), page_title orelse "(none)",
}) catch ToolError.InternalError;
}
@@ -2009,7 +2007,7 @@ fn execFill(arena: std.mem.Allocator, session: *lp.Session, registry: *NodeRegis
lp.actions.fill(resolved.node, text, resolved.page) catch |err| return mapActionError(err);
// Show the original reference (e.g. $LP_PASSWORD) in the result, not the resolved value
const suffix = std.fmt.allocPrint(arena, " with \"{s}\"", .{raw_text}) catch return ToolError.InternalError;
const suffix = arena.print(" with \"{s}\"", .{raw_text}) catch return ToolError.InternalError;
const body = try formatActionResult(arena, "Filled element", resolved.target, suffix);
return finalizeAction(arena, session, registry, scope, body);
}
@@ -2032,15 +2030,15 @@ fn execScroll(arena: std.mem.Allocator, session: *lp.Session, registry: *NodeReg
const result = lp.actions.scroll(if (resolved) |r| r.node else null, args.x, args.y, page) catch |err| return mapActionError(err);
const body = (switch (result.target) {
.window => std.fmt.allocPrint(arena, "Scrolled window to x: {d}, y: {d}", .{ result.x, result.y }),
.node => std.fmt.allocPrint(arena, "Scrolled element ({f}) to x: {d}, y: {d}", .{
.window => arena.print("Scrolled window to x: {d}, y: {d}", .{ result.x, result.y }),
.node => arena.print("Scrolled element ({f}) to x: {d}, y: {d}", .{
resolved.?.target,
result.x,
result.y,
}),
.container => |container| blk: {
const registered = registry.register(container) catch return ToolError.InternalError;
break :blk std.fmt.allocPrint(arena, "Scrolled scroll container ({f}) of element ({f}) to x: {d}, y: {d}", .{
break :blk arena.print("Scrolled scroll container ({f}) of element ({f}) to x: {d}, y: {d}", .{
ActionTarget{ .backend_node_id = registered.id },
resolved.?.target,
result.x,
@@ -2087,7 +2085,7 @@ fn execWaitForSelector(arena: std.mem.Allocator, session: *lp.Session, registry:
};
const registered = registry.register(node) catch return ToolError.InternalError;
return std.fmt.allocPrint(arena, "Element found. backendNodeId: {d}", .{registered.id}) catch return ToolError.InternalError;
return arena.print("Element found. backendNodeId: {d}", .{registered.id}) catch return ToolError.InternalError;
}
fn execWaitForScript(arena: std.mem.Allocator, session: *lp.Session, arguments: ?std.json.Value) ToolError![]const u8 {
@@ -2138,7 +2136,7 @@ fn execWaitForState(arena: std.mem.Allocator, session: *lp.Session, arguments: ?
},
};
return std.fmt.allocPrint(arena, "Page reached {s}.", .{@tagName(args.state)}) catch return ToolError.InternalError;
return arena.print("Page reached {s}.", .{@tagName(args.state)}) catch return ToolError.InternalError;
}
fn execHover(arena: std.mem.Allocator, session: *lp.Session, registry: *NodeRegistry, arguments: ?std.json.Value) ToolError![]const u8 {
@@ -2182,7 +2180,7 @@ fn execPress(arena: std.mem.Allocator, session: *lp.Session, registry: *NodeRegi
// Pressing Enter on a form input triggers implicit form submission;
// `finalizeAction` drains the queued navigation before tagging the body.
const body = std.fmt.allocPrint(arena, "Pressed key '{s}'", .{args.key}) catch return ToolError.InternalError;
const body = arena.print("Pressed key '{s}'", .{args.key}) catch return ToolError.InternalError;
return finalizeAction(arena, session, registry, scope, body);
}
@@ -2199,7 +2197,7 @@ fn execSelectOption(arena: std.mem.Allocator, session: *lp.Session, registry: *N
lp.actions.selectOption(resolved.node, args.value, resolved.page) catch |err| return mapActionError(err);
const prefix = std.fmt.allocPrint(arena, "Selected option '{s}'", .{args.value}) catch return ToolError.InternalError;
const prefix = arena.print("Selected option '{s}'", .{args.value}) catch return ToolError.InternalError;
const body = try formatActionResult(arena, prefix, resolved.target, "");
return finalizeAction(arena, session, registry, scope, body);
}
@@ -2218,7 +2216,7 @@ fn execSetChecked(arena: std.mem.Allocator, session: *lp.Session, registry: *Nod
lp.actions.setChecked(resolved.node, args.checked, resolved.page) catch |err| return mapActionError(err);
const state_str: []const u8 = if (args.checked) "checked" else "unchecked";
const suffix = std.fmt.allocPrint(arena, " to {s}", .{state_str}) catch return ToolError.InternalError;
const suffix = arena.print(" to {s}", .{state_str}) catch return ToolError.InternalError;
const body = try formatActionResult(arena, "Set element", resolved.target, suffix);
return finalizeAction(arena, session, registry, scope, body);
}
@@ -2247,7 +2245,7 @@ fn execFindElement(arena: std.mem.Allocator, session: *lp.Session, registry: *No
's' => options.dot_all = true,
'm' => options.multiline = true,
else => return .{
.text = try std.fmt.allocPrint(arena, "findElement: unsupported regex flag '{c}' in '{s}'", .{ flag, name }),
.text = try arena.print("findElement: unsupported regex flag '{c}' in '{s}'", .{ flag, name }),
.is_error = true,
},
};
@@ -2255,7 +2253,7 @@ fn execFindElement(arena: std.mem.Allocator, session: *lp.Session, registry: *No
const regex = session.browser.app.regex_context.compile(lit.body, options, &diag) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.InvalidRegex => return .{
.text = try std.fmt.allocPrint(arena, "findElement: invalid name regex '{s}': {s} at offset {d}", .{ lit.body, diag.message(), diag.offset }),
.text = try arena.print("findElement: invalid name regex '{s}': {s} at offset {d}", .{ lit.body, diag.message(), diag.offset }),
.is_error = true,
},
};
@@ -2315,7 +2313,7 @@ fn execGetEnv(arena: std.mem.Allocator, arguments: ?std.json.Value) ToolError![]
if (args.name) |name| {
if (lookupLpEnv(name)) |value| return value;
return std.fmt.allocPrint(arena, "Environment variable '{s}' is not set", .{name}) catch ToolError.InternalError;
return arena.print("Environment variable '{s}' is not set", .{name}) catch ToolError.InternalError;
}
const env_names = lpEnvNames(arena) catch return ToolError.InternalError;
@@ -2390,7 +2388,7 @@ fn execGetCookies(arena: std.mem.Allocator, session: *lp.Session, arguments: ?st
const filter_url: ?[:0]const u8 = blk: {
if (args.all) break :blk null;
if (args.url) |u| break :blk arena.dupeZ(u8, u) catch return ToolError.InternalError;
if (args.url) |u| break :blk arena.dupeSentinel(u8, u, 0) catch return ToolError.InternalError;
if (session.currentFrame()) |f| break :blk f.url;
return "No current page. Pass `url` to filter by host or `all=true` to list every cookie.";
};
@@ -2410,7 +2408,7 @@ fn execGetCookies(arena: std.mem.Allocator, session: *lp.Session, arguments: ?st
}
if (count == 0) {
const label = filter_url orelse "(unfiltered)";
return std.fmt.allocPrint(arena, "No cookies for {s}.", .{label}) catch ToolError.InternalError;
return arena.print("No cookies for {s}.", .{label}) catch ToolError.InternalError;
}
return aw.written();
}
@@ -2544,7 +2542,7 @@ fn diagnoseArgs(arena: std.mem.Allocator, arguments: ?std.json.Value) ?[]const u
if (args.object.get("state")) |v| switch (v) {
.string => |s| if (std.meta.stringToEnum(lp.Config.WaitUntil, s) == null)
return formatEnumError(arena, "state", s, lp.Config.WaitUntil),
else => return std.fmt.allocPrint(arena, "state must be a string", .{}) catch null,
else => return arena.print("state must be a string", .{}) catch null,
};
return null;
@@ -2553,9 +2551,9 @@ fn diagnoseArgs(arena: std.mem.Allocator, arguments: ?std.json.Value) ?[]const u
fn formatEnumError(arena: std.mem.Allocator, field: []const u8, got: []const u8, comptime E: type) ?[]const u8 {
var aw: std.Io.Writer.Allocating = .init(arena);
aw.writer.print("invalid {s} '{s}'. Expected one of: ", .{ field, got }) catch return null;
inline for (std.meta.fields(E), 0..) |f, i| {
inline for (@typeInfo(E).@"enum".field_names, 0..) |field_name, i| {
if (i > 0) aw.writer.writeAll(", ") catch return null;
aw.writer.writeAll(f.name) catch return null;
aw.writer.writeAll(field_name) catch return null;
}
return aw.written();
}
@@ -2601,7 +2599,7 @@ pub fn normalizeArgKeys(arena: std.mem.Allocator, tool: Tool, args: ?std.json.Va
if (v != .object) return v;
const schemas = lp.Schema.all();
const tool_idx = @intFromEnum(tool);
const tool_idx = @backingInt(tool);
if (tool_idx >= schemas.len) return v;
const schema = schemas[tool_idx];
@@ -2719,7 +2717,7 @@ pub fn reverseSubstituteEnvVars(arena: std.mem.Allocator, input: []const u8) err
var changed = false;
for (pairs.items) |p| {
if (std.mem.indexOf(u8, current, p.value) == null) continue;
const placeholder = try std.fmt.allocPrint(arena, "${s}", .{p.name});
const placeholder = try arena.print("${s}", .{p.name});
current = try std.mem.replaceOwned(u8, arena, current, p.value, placeholder);
changed = true;
}
@@ -2751,12 +2749,12 @@ test "tree and nodeDetails read the node's own frame" {
const input_id = (try registry.register(input)).id;
const aa = testing.arena_allocator;
const tree_args = try std.json.parseFromSliceLeaky(std.json.Value, aa, try std.fmt.allocPrint(aa, "{{\"backendNodeId\":{d}}}", .{html_id}), .{});
const tree_args = try std.json.parseFromSliceLeaky(std.json.Value, aa, try aa.print("{{\"backendNodeId\":{d}}}", .{html_id}), .{});
const tree = try call(aa, page.session, &registry, "tree", tree_args, .{});
try std.testing.expect(std.mem.indexOf(u8, tree.text, "child-label") != null);
try std.testing.expect(std.mem.indexOf(u8, tree.text, "parent-") == null);
const details_args = try std.json.parseFromSliceLeaky(std.json.Value, aa, try std.fmt.allocPrint(aa, "{{\"backendNodeId\":{d}}}", .{input_id}), .{});
const details_args = try std.json.parseFromSliceLeaky(std.json.Value, aa, try aa.print("{{\"backendNodeId\":{d}}}", .{input_id}), .{});
const details = try call(aa, page.session, &registry, "nodeDetails", details_args, .{});
try std.testing.expect(std.mem.indexOf(u8, details.text, "child-label") != null);
}
+2 -2
View File
@@ -277,7 +277,7 @@ fn writePartWithEndings(part: []const u8, use_native_endings: bool, writer: *Wri
const match = chunk == cr;
// Create a bitset out of match vector.
const bitset = std.bit_set.IntegerBitSet(vector_len){
const bitset = std.bit_set.Integer(vector_len){
.mask = @bitCast(@intFromBool(match)),
};
@@ -453,7 +453,7 @@ test "Blob: a pinned arena reaches the browser's account and is given back" {
browser.flushArenaMemory();
try testing.expectEqual(0, browser.arena_account.pending);
const data = [_]u8{'x'} ** (64 * 1024);
const data: [64 * 1024]u8 = @splat('x');
const blob = try Blob.initFromBytes(&data, "text/plain", &frame.js.execution);
try testing.expect(browser.arena_account.pending >= data.len);
+2 -2
View File
@@ -107,7 +107,7 @@ pub fn escape(value: []const u8, frame: *Frame) ![]const u8 {
// Second char (i==0) is a digit and first is '-', needs hex escape
if (i == 0 and first == '-' and c >= '0' and c <= '9') {
result[pos] = '\\';
const hex_str = std.fmt.bufPrint(result[pos + 1 ..], "{x} ", .{c}) catch unreachable;
const hex_str = std.mem.print(result[pos + 1 ..], "{x} ", .{c}) catch unreachable;
pos += 1 + hex_str.len;
} else if (!needsEscape(false, c)) {
result[pos] = c;
@@ -180,7 +180,7 @@ fn writeEscape(comptime is_first: bool, buf: []u8, c: u8) usize {
var data = buf[1..];
if (isHexEscape(c) or ((comptime is_first) and c >= '0' and c <= '9')) {
const hex_str = std.fmt.bufPrint(data, "{x} ", .{c}) catch unreachable;
const hex_str = std.mem.print(data, "{x} ", .{c}) catch unreachable;
return 1 + hex_str.len;
}
+1 -1
View File
@@ -203,7 +203,7 @@ const ValueWriter = struct {
pub fn logFmt(self: ValueWriter, _: []const u8, writer: anytype) !void {
var buf: [32]u8 = undefined;
for (self.valuesToLog(), 0..) |value, i| {
const name = try std.fmt.bufPrint(&buf, "param.{d}", .{i});
const name = try std.mem.print(&buf, "param.{d}", .{i});
try writer.write(name, value);
}
}
+2 -2
View File
@@ -73,7 +73,7 @@ fn getCode(self: *const DOMException) u8 {
return switch (self._code) {
// no legacy numeric code
.operation_error, .data_error, .constraint_error, .version_error, .transaction_inactive_error, .read_only_error => 0,
else => @intFromEnum(self._code),
else => @backingInt(self._code),
};
}
@@ -162,7 +162,7 @@ pub fn toString(self: *const DOMException, exec: *js.Execution) ![]const u8 {
else => break :blk self.getMessage(),
}
};
return std.fmt.bufPrint(exec.buf, "{s}: {s}", .{ self.getName(), msg }) catch return msg;
return std.mem.print(exec.buf, "{s}: {s}", .{ self.getName(), msg }) catch return msg;
}
const Code = enum(u8) {
+1 -1
View File
@@ -206,7 +206,7 @@ fn invertSelf(self: *DOMMatrix) !*DOMMatrix {
if (RO.invertMatrix(state.matrix)) |v| {
state.matrix = v;
} else {
state.matrix = .{std.math.nan(f64)} ** 16;
state.matrix = @splat(std.math.nan(f64));
state.is_2d = false;
}
return self.applyState(state);
+3 -3
View File
@@ -758,7 +758,7 @@ fn inverse(self: *const DOMMatrixReadOnly, page: *Page) !*DOMMatrix {
return DOMMatrix.create(v, self._is_2d, page);
}
// Non-invertible matrices become all-NaN with is2D = false.
return DOMMatrix.create(.{std.math.nan(f64)} ** 16, false, page);
return DOMMatrix.create(@splat(std.math.nan(f64)), false, page);
}
fn toFloat32Array(self: *const DOMMatrixReadOnly, exec: *const js.Execution) !js.TypedArray(f32) {
@@ -783,7 +783,7 @@ pub fn toString(self: *const DOMMatrixReadOnly, exec: *const js.Execution) ![]co
return error.InvalidStateError;
}
}
return std.fmt.allocPrint(exec.local_arena, "matrix({d}, {d}, {d}, {d}, {d}, {d})", .{
return exec.local_arena.print("matrix({d}, {d}, {d}, {d}, {d}, {d})", .{
m[0], m[1], m[4], m[5], m[12], m[13],
});
}
@@ -792,7 +792,7 @@ pub fn toString(self: *const DOMMatrixReadOnly, exec: *const js.Execution) ![]co
return error.InvalidStateError;
}
}
return std.fmt.allocPrint(exec.local_arena, "matrix3d({d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d})", .{
return exec.local_arena.print("matrix3d({d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d}, {d})", .{
m[0], m[1], m[2], m[3],
m[4], m[5], m[6], m[7],
m[8], m[9], m[10], m[11],
+2 -2
View File
@@ -218,7 +218,7 @@ fn getLastModified(self: *const Document, frame: *Frame) ![]const u8 {
};
const tm = try dt.localTime(timestamp);
return std.fmt.allocPrint(frame.local_arena, "{d:0>2}/{d:0>2}/{d} {d:0>2}:{d:0>2}:{d:0>2}", .{
return frame.local_arena.print("{d:0>2}/{d:0>2}/{d} {d:0>2}:{d:0>2}:{d:0>2}", .{
@as(u32, @intCast(tm.tm_mon + 1)),
@as(u32, @intCast(tm.tm_mday)),
tm.tm_year + 1900,
@@ -1223,7 +1223,7 @@ fn writeInternal(self: *Document, text: []const []const u8, append_newline: bool
}
frame.domChanged();
self._write_insertion_point = children_to_insert.getLast();
self._write_insertion_point = children_to_insert.last().?;
}
pub fn open(self: *Document, call_frame: *Frame) !*Document {
+1 -1
View File
@@ -733,7 +733,7 @@ fn prefixedAttributeName(namespace: []const u8, local_name: []const u8, frame: *
}
return null;
};
return try std.fmt.allocPrint(frame.local_arena, "{s}:{s}", .{ prefix, local_name });
return try frame.local_arena.print("{s}:{s}", .{ prefix, local_name });
}
pub fn getAttributeSafe(self: *const Element, name: String) ?[]const u8 {
+21 -33
View File
@@ -295,7 +295,7 @@ fn setCancelBubble(self: *Event) void {
}
fn getEventPhase(self: *const Event) u8 {
return @intFromEnum(self._event_phase);
return @backingInt(self._event_phase);
}
fn getTimeStamp(self: *const Event, exec: *js.Execution) f64 {
@@ -399,42 +399,30 @@ pub fn inheritOptions(comptime T: type, comptime additions: anytype) type {
// this per-level check produce.
js.Local.assertDictionaryFieldOrder(additions);
var all_fields: []const std.builtin.Type.StructField = &.{};
var names: []const [:0]const u8 = &.{};
var types: []const type = &.{};
var attrs: []const std.lang.Type.Struct.FieldAttributes = &.{};
if (@hasField(T, "_proto")) {
const t_fields = @typeInfo(T).@"struct".fields;
inline for (t_fields) |field| {
if (std.mem.eql(u8, field.name, "_proto")) {
const ProtoType = @typeInfo(field.type).pointer.child;
if (@hasDecl(ProtoType, "Options")) {
const parent_options = @typeInfo(ProtoType.Options);
for (parent_options.@"struct".fields) |f| {
if (!std.mem.eql(u8, f.name, js.Local.dictionary_group_marker)) {
all_fields = all_fields ++ &[_]std.builtin.Type.StructField{f};
}
}
const ProtoType = @typeInfo(@FieldType(T, "_proto")).pointer.child;
if (@hasDecl(ProtoType, "Options")) {
const parent_options = @typeInfo(ProtoType.Options).@"struct";
for (parent_options.field_names, parent_options.field_types, parent_options.field_attrs) |field_name, field_type, field_attrs| {
if (!std.mem.eql(u8, field_name, js.Local.dictionary_group_marker)) {
names = names ++ .{field_name};
types = types ++ .{field_type};
attrs = attrs ++ .{field_attrs};
}
}
}
}
const additions_info = @typeInfo(additions);
all_fields = all_fields ++ additions_info.@"struct".fields;
const additions_info = @typeInfo(additions).@"struct";
const marker_default: void = {};
var names: [all_fields.len + 1][:0]const u8 = undefined;
var types: [all_fields.len + 1]type = undefined;
var attrs: [all_fields.len + 1]std.builtin.Type.StructField.Attributes = undefined;
for (all_fields, 0..) |f, i| {
names[i] = f.name;
types[i] = f.type;
attrs[i] = .{ .@"comptime" = f.is_comptime, .@"align" = f.alignment, .default_value_ptr = f.default_value_ptr };
}
names[all_fields.len] = js.Local.dictionary_group_marker;
types[all_fields.len] = void;
attrs[all_fields.len] = .{ .default_value_ptr = @ptrCast(&marker_default) };
return @Struct(.auto, null, &names, &types, &attrs);
names = names ++ additions_info.field_names ++ .{js.Local.dictionary_group_marker};
types = types ++ additions_info.field_types ++ .{void};
attrs = attrs ++ additions_info.field_attrs ++ .{std.lang.Type.Struct.FieldAttributes{ .default_value_ptr = @ptrCast(&marker_default) }};
return @Struct(.auto, null, names, types, attrs);
}
pub fn populatePrototypes(self: anytype, opts: anytype, trusted: bool) void {
@@ -494,10 +482,10 @@ pub const JsApi = struct {
pub const cancelBubble = bridge.accessor(Event.getCancelBubble, Event.setCancelBubble, .{});
// Event phase constants
pub const NONE = bridge.property(@intFromEnum(EventPhase.none), .{ .template = true });
pub const CAPTURING_PHASE = bridge.property(@intFromEnum(EventPhase.capturing_phase), .{ .template = true });
pub const AT_TARGET = bridge.property(@intFromEnum(EventPhase.at_target), .{ .template = true });
pub const BUBBLING_PHASE = bridge.property(@intFromEnum(EventPhase.bubbling_phase), .{ .template = true });
pub const NONE = bridge.property(@backingInt(EventPhase.none), .{ .template = true });
pub const CAPTURING_PHASE = bridge.property(@backingInt(EventPhase.capturing_phase), .{ .template = true });
pub const AT_TARGET = bridge.property(@backingInt(EventPhase.at_target), .{ .template = true });
pub const BUBBLING_PHASE = bridge.property(@backingInt(EventPhase.bubbling_phase), .{ .template = true });
};
// tested in event_target
+1 -1
View File
@@ -77,7 +77,7 @@ const tracked_event_types = [_][]const u8{
};
// Counts stored in a fixed array
_counts: [tracked_event_types.len]u32 = [_]u32{0} ** tracked_event_types.len,
_counts: [tracked_event_types.len]u32 = @splat(0),
pub fn increment(self: *EventCounts, event_type: []const u8) void {
if (getIndex(event_type)) |idx| {
+4 -4
View File
@@ -150,7 +150,7 @@ fn setOnProgress(self: *FileReader, cb: ?js.Function.Global) !void {
}
fn getReadyState(self: *const FileReader) u8 {
return @intFromEnum(self._ready_state);
return @backingInt(self._ready_state);
}
fn getResult(self: *const FileReader) ?Result {
@@ -466,9 +466,9 @@ pub const JsApi = struct {
pub const constructor = bridge.constructor(FileReader.init, .{});
// State constants
pub const EMPTY = bridge.property(@intFromEnum(FileReader.ReadyState.empty), .{ .template = true });
pub const LOADING = bridge.property(@intFromEnum(FileReader.ReadyState.loading), .{ .template = true });
pub const DONE = bridge.property(@intFromEnum(FileReader.ReadyState.done), .{ .template = true });
pub const EMPTY = bridge.property(@backingInt(FileReader.ReadyState.empty), .{ .template = true });
pub const LOADING = bridge.property(@backingInt(FileReader.ReadyState.loading), .{ .template = true });
pub const DONE = bridge.property(@backingInt(FileReader.ReadyState.done), .{ .template = true });
// Properties
pub const readyState = bridge.accessor(FileReader.getReadyState, null, .{});
+2 -2
View File
@@ -56,7 +56,7 @@ pub fn pushState(_: *History, state: js.Value, _: ?[]const u8, _url: ?[]const u8
const url = if (_url) |u|
try @import("../URL.zig").resolve(arena.allocator(), frame.url, u, .{})
else
try arena.dupeZ(u8, frame.url);
try arena.dupeSentinel(u8, frame.url, 0);
const json = state.toJson(arena.allocator()) catch return error.DataClone;
_ = try session.navigation.pushEntry(url, .{ .source = .history, .value = json }, frame, true);
@@ -80,7 +80,7 @@ fn replaceState(_: *History, state: js.Value, _: ?[]const u8, _url: ?[]const u8,
const url = if (_url) |u|
try @import("../URL.zig").resolve(arena.allocator(), frame.url, u, .{})
else
try arena.dupeZ(u8, frame.url);
try arena.dupeSentinel(u8, frame.url, 0);
const json = state.toJson(arena.allocator()) catch return error.DataClone;
_ = try session.navigation.replaceEntry(url, .{ .source = .history, .value = json }, frame, true);
+1 -1
View File
@@ -114,7 +114,7 @@ fn setHash(_: *const Location, hash: []const u8, frame: *Frame) !void {
}
// Scratch only: scheduleNavigation dupes the URL into its own arena
// synchronously, so the local arena suffices.
break :blk try std.fmt.allocPrint(frame.local_arena, "#{s}", .{hash});
break :blk try frame.local_arena.print("#{s}", .{hash});
};
// Per the Location hash setter, when the fragment doesn't change no
+1 -1
View File
@@ -122,7 +122,7 @@ pub fn getGlobalPrivacyControl(_: *const Navigator) bool {
}
pub fn getPlatform(_: *const Navigator) []const u8 {
return switch (builtin.os.tag) {
return switch (builtin.target.os.tag) {
.macos => "MacIntel",
.windows => "Win32",
.linux => "Linux x86_64",
+3 -3
View File
@@ -93,7 +93,7 @@ fn brandList() []const Brand {
}
fn uaPlatform() []const u8 {
return switch (builtin.os.tag) {
return switch (builtin.target.os.tag) {
.macos => "macOS",
.windows => "Windows",
.linux => "Linux",
@@ -103,7 +103,7 @@ fn uaPlatform() []const u8 {
}
fn uaArchitecture() []const u8 {
return switch (builtin.cpu.arch) {
return switch (builtin.target.cpu.arch) {
.x86, .x86_64 => "x86",
.aarch64, .aarch64_be, .arm, .armeb => "arm",
else => "",
@@ -111,7 +111,7 @@ fn uaArchitecture() []const u8 {
}
fn uaBitness() []const u8 {
return switch (builtin.cpu.arch) {
return switch (builtin.target.cpu.arch) {
.x86_64, .aarch64, .aarch64_be, .powerpc64, .powerpc64le, .riscv64 => "64",
else => "32",
};
+5 -5
View File
@@ -112,10 +112,10 @@ pub fn observe(self: *PerformanceObserver, maybe_options: ?ObserveOptions) !void
// Update entries.
var interests: u16 = 0;
for (entry_types) |entry_type| {
const fields = @typeInfo(Performance.Entry.Type.Enum).@"enum".fields;
inline for (fields) |field| {
if (std.mem.eql(u8, field.name, entry_type)) {
const flag = @as(u16, 1) << @as(u16, field.value);
const info = @typeInfo(Performance.Entry.Type.Enum).@"enum";
inline for (info.field_names, info.field_values) |field_name, field_value| {
if (std.mem.eql(u8, field_name, entry_type)) {
const flag = @as(u16, 1) << @as(u16, field_value);
interests |= flag;
}
}
@@ -182,7 +182,7 @@ pub fn interested(
}
pub fn interestedIn(self: *const PerformanceObserver, kind: Performance.Entry.Type.Enum) bool {
const flag = @as(u16, 1) << @intCast(@intFromEnum(kind));
const flag = @as(u16, 1) << @intCast(@backingInt(kind));
return self._interests & flag != 0;
}
+2 -2
View File
@@ -127,7 +127,7 @@ const NameContext = struct {
pub fn hash(_: NameContext, key: Name) u32 {
var hasher = std.hash.Wyhash.init(0);
hasher.update(key.name.str());
hasher.update(&.{@intFromEnum(std.meta.activeTag(key.namespace))});
hasher.update(&.{@backingInt(std.meta.activeTag(key.namespace))});
if (key.namespace == .other) {
hasher.update(key.namespace.other);
}
@@ -1181,7 +1181,7 @@ fn sanitize(self: *const Sanitizer, root: *Node, safe: bool, frame: *Frame) !voi
fn touch(touched: *std.ArrayList(*Node), parent: *Node, arena: Allocator) !void {
// Siblings are removed one after another, so this catches most repeats.
// Those it misses just get a second, no-op, merge.
if (touched.getLastOrNull() != parent) {
if (touched.last() != parent) {
try touched.append(arena, parent);
}
}
@@ -93,7 +93,7 @@ fn init(
const arena = try session.getArena(.small, "ServiceWorker");
errdefer arena.release();
const owned_url = try arena.dupeZ(u8, script_url);
const owned_url = try arena.dupeSentinel(u8, script_url, 0);
const frame_id = session.nextFrameId();
const loader_id = session.nextLoaderId();
+1 -1
View File
@@ -68,7 +68,7 @@ pub fn init(url: []const u8, name_or_options: ?NameOrOpts, frame: *Frame) !*Shar
const scope = blk: {
const session = frame._session;
// \x00 can appear in neither a URL nor a name
const lookup_key = try std.fmt.allocPrint(frame.call_arena, "{s}\x00{s}", .{ resolved_url, options.name });
const lookup_key = try frame.call_arena.print("{s}\x00{s}", .{ resolved_url, options.name });
if (session.shared_workers.get(lookup_key)) |existing| {
break :blk existing;
}
@@ -63,7 +63,7 @@ pub fn init(frame: *Frame, url: [:0]const u8, name: []const u8, worker_type: Wor
const arena = try session.getArena(.small, "SharedWorker");
errdefer arena.release();
const owned_url = try arena.dupeZ(u8, url);
const owned_url = try arena.dupeSentinel(u8, url, 0);
const frame_id = session.nextFrameId();
const loader_id = session.nextLoaderId();
const self = try WorkerGlobalScope.init(
+2 -2
View File
@@ -286,13 +286,13 @@ fn exportJwk(key: *CryptoKey, exec: *const Execution) !js.Promise {
// The `alg` registry value depends on the algorithm and key length.
const alg: []const u8 = switch (key._type) {
.aes => try std.fmt.allocPrint(exec.local_arena, "A{d}{s}", .{
.aes => try exec.local_arena.print("A{d}{s}", .{
key._key.len * 8,
key._algorithm.name[4..], // strip "AES-"
}),
.hmac => blk: {
const hash: []const u8 = key._algorithm.hash orelse "SHA-";
break :blk try std.fmt.allocPrint(exec.local_arena, "HS{s}", .{hash[4..]}); // strip "SHA-"
break :blk try exec.local_arena.print("HS{s}", .{hash[4..]}); // strip "SHA-"
},
else => {
log.debug(.not_implemented, "SubtleCrypto.exportKey", .{ .format = "jwk", .type = key._type });
+1 -1
View File
@@ -154,7 +154,7 @@ fn setHost(self: *URL, value: []const u8) void {
pub fn getPort(self: *URL) []const u8 {
const port = U.urlGetPort(self._url) orelse return "";
return std.fmt.bufPrint(&self._port, "{d}", .{port}) catch unreachable;
return std.mem.print(&self._port, "{d}", .{port}) catch unreachable;
}
/// Spec requires us to silently ignore errors of this setter.
+7 -7
View File
@@ -312,9 +312,9 @@ fn match(self: *const URLPattern, input: ?Input, base_url: ?[]const u8, exec: *c
// The IDL conversion yields a fresh dictionary; that, not the
// caller's object, is what ends up in result.inputs.
const dict = local.newObject();
inline for (@typeInfo(Init).@"struct".fields) |f| {
if (@field(dict_init, f.name)) |v| {
_ = try dict.set(f.name, v, .{});
inline for (@typeInfo(Init).@"struct".field_names) |field_name| {
if (@field(dict_init, field_name)) |v| {
_ = try dict.set(field_name, v, .{});
}
}
inputs = local.newArray(1);
@@ -357,7 +357,7 @@ fn match(self: *const URLPattern, input: ?Input, base_url: ?[]const u8, exec: *c
const groups = local.newObject();
var key_buf: [16]u8 = undefined;
for (component.group_names, 1..) |group_name, i| {
const key = std.fmt.bufPrint(&key_buf, "{d}", .{i}) catch unreachable;
const key = std.mem.print(&key_buf, "{d}", .{i}) catch unreachable;
_ = try groups.set(group_name, try exec_result.get(key), .{});
}
@field(result, name) = .{ .input = value, .groups = groups };
@@ -874,7 +874,7 @@ fn urlHost(url: *const U.Url) []const u8 {
fn urlPort(arena: Allocator, url: *const U.Url) Error![]const u8 {
const port = U.urlGetPort(url) orelse return "";
return std.fmt.allocPrint(arena, "{d}", .{port});
return arena.print("{d}", .{port});
}
fn urlPath(url: *const U.Url) []const u8 {
@@ -1005,7 +1005,7 @@ fn canonicalizePort(arena: Allocator, value: []const u8, protocol: ?[]const u8)
if (digits == 0) {
return error.TypeError;
}
const serialized = try std.fmt.allocPrint(arena, "{d}", .{port});
const serialized = try arena.print("{d}", .{port});
if (protocol) |p| {
if (defaultPort(p)) |default| {
if (std.mem.eql(u8, default, serialized)) {
@@ -1546,7 +1546,7 @@ const Parser = struct {
if (name_token) |t| {
name = t.value;
} else if (regexp_or_wildcard != null) {
name = try std.fmt.allocPrint(self.env.arena, "{d}", .{self.next_numeric_name});
name = try self.env.arena.print("{d}", .{self.next_numeric_name});
self.next_numeric_name += 1;
}
for (self.parts.items) |part| {
+9 -5
View File
@@ -173,10 +173,17 @@ const ActionSequence = struct {
frame.js.localScope(&ls);
defer ls.deinit();
errdefer |err| {
performSources(self, &ls) catch |err| {
ls.toLocal(self.resolver).reject("WebDriver.actionSequence", ls.local.newString(@errorName(err)));
}
return err;
};
ls.toLocal(self.resolver).resolve("WebDriver.actionSequence", {});
return null;
}
fn performSources(self: *ActionSequence, ls: *js.Local.Scope) !void {
const frame = self.frame;
const sources = self.sources.local(&ls.local).toArray();
for (0..sources.len()) |i| {
const source_val = try sources.get(@intCast(i));
@@ -194,9 +201,6 @@ const ActionSequence = struct {
}
// "none" sources only carry pauses, which have no observable effect here.
}
ls.toLocal(self.resolver).resolve("WebDriver.actionSequence", {});
return null;
}
fn finalize(ptr: *anyopaque) void {
+3 -3
View File
@@ -113,7 +113,7 @@ fn keys(self: *Cache, exec: *const Execution) !js.Promise {
};
for (entries) |entry| {
const url = try exec.call_arena.dupeZ(u8, entry.url);
const url = try exec.call_arena.dupeSentinel(u8, entry.url, 0);
const request = try Request.init(.{ .url = url }, null, exec);
request.acquireRef();
requests.appendAssumeCapacity(request);
@@ -324,7 +324,7 @@ fn toEntry(allocator: Allocator, url: []const u8, response: *const Response, bod
kv.* = .{ try arena.dupe(u8, header.name.str()), try arena.dupe(u8, header.value.str()) };
}
entry.response_url = try arena.dupeZ(u8, response._url);
entry.response_url = try arena.dupeSentinel(u8, response._url, 0);
entry.status = response._status;
entry.status_text = try arena.dupe(u8, response._status_text);
entry.response_type = response._type;
@@ -343,7 +343,7 @@ fn toResponse(entry: *const Store.Entry, exec: *const Execution) !*Response {
._arena = arena,
._status = entry.status,
._status_text = try arena.dupe(u8, entry.status_text),
._url = try arena.dupeZ(u8, entry.response_url),
._url = try arena.dupeSentinel(u8, entry.response_url, 0),
._body = .{ .bytes = try arena.dupe(u8, entry.body) },
._type = entry.response_type,
._is_redirected = entry.is_redirected,
@@ -79,15 +79,9 @@ const Extension = union(enum) {
/// Reified enum type from the fields of this union.
const Kind = blk: {
const info = @typeInfo(Extension).@"union";
const fields = info.fields;
const Tag = std.math.IntFittingRange(0, if (fields.len == 0) 0 else fields.len - 1);
var names: [fields.len][:0]const u8 = undefined;
for (fields, 0..) |field, i| {
names[i] = field.name;
}
break :blk @Enum(Tag, .exhaustive, &names, &std.simd.iota(Tag, fields.len));
const names = @typeInfo(Extension).@"union".field_names;
const Tag = std.math.IntFittingRange(0, if (names.len == 0) 0 else names.len - 1);
break :blk @Enum(Tag, .exhaustive, names, &std.simd.iota(Tag, names.len));
};
/// Returns the `Extension.Kind` by its name.
@@ -98,9 +92,9 @@ const Extension = union(enum) {
const kvs = comptime build_kvs: {
const T = Extension.Kind;
const EnumKV = struct { []const u8, T };
var kvs_array: [@typeInfo(T).@"enum".fields.len]EnumKV = undefined;
for (@typeInfo(T).@"enum".fields, 0..) |enumField, i| {
kvs_array[i] = .{ enumField.name, @field(T, enumField.name) };
var kvs_array: [@typeInfo(T).@"enum".field_names.len]EnumKV = undefined;
for (@typeInfo(T).@"enum".field_names, 0..) |field_name, i| {
kvs_array[i] = .{ field_name, @field(T, field_name) };
}
break :build_kvs kvs_array[0..];
};
@@ -180,7 +174,7 @@ fn getExtension(_: *const WebGLRenderingContext, name: []const u8, frame: *Frame
/// Returns a list of all the supported WebGL extensions.
fn getSupportedExtensions(_: *const WebGLRenderingContext) []const []const u8 {
return std.meta.fieldNames(Extension.Kind);
return @typeInfo(Extension.Kind).@"enum".field_names;
}
pub const JsApi = struct {
+1 -1
View File
@@ -66,7 +66,7 @@ pub const State = struct {
stroke_style: Style = .{ .color = color.RGBA.Named.black },
font_size: f64 = 10,
font_buf: [font_capacity]u8 = default_font.* ++ [_]u8{0} ** (font_capacity - default_font.len),
font_buf: [font_capacity]u8 = default_font.* ++ @as([font_capacity - default_font.len]u8, @splat(0)),
font_len: u16 = default_font.len,
font_overflow: ?[]const u8 = null,
+4 -4
View File
@@ -94,7 +94,7 @@ fn reflect(comptime Inner: type, comptime field: ?[]const u8) Reflect {
const R = fn_info.return_type.?;
const has_error_return = @typeInfo(R) == .error_union;
// The executor type is the last parameter of inner.next (after self)
const Executor = fn_info.params[1].type.?;
const Executor = fn_info.param_types[1].?;
return .{
.has_error_return = has_error_return,
.ValueType = ValueType(unwrapOptional(unwrapError(R)), field),
@@ -121,9 +121,9 @@ fn unwrapOptional(comptime T: type) type {
fn ValueType(comptime R: type, comptime field_: ?[]const u8) type {
const field = field_ orelse return R;
inline for (@typeInfo(R).@"struct".fields) |f| {
if (comptime std.mem.eql(u8, f.name, field)) {
return f.type;
inline for (@typeInfo(R).@"struct".field_names, @typeInfo(R).@"struct".field_types) |field_name, field_type| {
if (comptime std.mem.eql(u8, field_name, field)) {
return field_type;
}
}
@compileError("Unknown EntryIterator field " ++ @typeName(R) ++ "." ++ field);
Loaded 100 of 192 files, more files were not shown because too many files have changed in this diff. Show more