From 2227d698c63a77b7c75aceee65d5e38bd3de712c Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Wed, 10 Jun 2026 13:27:12 +0900 Subject: [PATCH] ww: drop wwstage peekpackage 2048-byte cap, align to cstage line-scan (#16 PREP-peek) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pre-existing rule-10 asymmetry: 4 lib files (decimal/ftos/stof/memio) declare package past byte 2048, so the capped wwstage peek returned nil where cstage's uncapped line-scan found the decl — corrupting the strict-package check and any peek consumer. Mirrors cstage peek_package semantics exactly; prerequisite for the upcoming package-less boundary directive to inject identically in both twins. --- selfhost/cmd/ww/main.combined.ww | 42 ++++++++++++++++++-------------- selfhost/cmd/ww/main.ww | 42 ++++++++++++++++++-------------- 2 files changed, 48 insertions(+), 36 deletions(-) diff --git a/selfhost/cmd/ww/main.combined.ww b/selfhost/cmd/ww/main.combined.ww index 4d0b0b4d..88e043c7 100644 --- a/selfhost/cmd/ww/main.combined.ww +++ b/selfhost/cmd/ww/main.combined.ww @@ -3323,32 +3323,38 @@ fn expand(c: *expctx, pathcs: *u8) void = { // starts with `package ;` return the package name as a fresh // heap-allocated NUL-terminated *u8, else nil. Same shape as cstage // peek_package. +// +// Reads the WHOLE file (via slurp), not a fixed prefix: cstage's +// peek_package scans line-by-line with fgets and no total cap, stopping +// at the first non-comment-non-blank line. A prior 2048-byte read cap +// here diverged from cstage on files whose `package` decl sits behind a +// >2048-byte comment header (strconv decimal/ftos/stof, memio) — +// returning nil and making the #16 D-i injection asymmetric (rule-10 +// break, byte-id divergence in the regenerated combined). The corpus has +// no line >2047 chars, so a whole-file line scan matches cstage's +// per-line fgets byte-for-byte on every real input. fn peekpackage(pathcs: *u8) *u8 = { - let fd: i32 = os.open(pathstr(pathcs), os.flag.RDONLY, 0i32); - if (fd < 0) { return nil; }; - let buf: []u8 = alloc([], 2048u64)!; - buf.len = 2048; - let n: i64 = os.read(fd, buf.ptr, 2048u64); - os.close(fd); - if (n <= 0i64) { return nil; }; - let nu: u64 = n: u64; + let bufp: *u8; + let nu: u64; + bufp, nu = slurp(pathcs); + if (bufp == nil) { return nil; }; let p: u64 = 0u64; for (p < nu) { let q: u64 = p; for (q < nu) { - if (buf[q] == 10u8) { break; }; // '\n' + if (bufp[q] == 10u8) { break; }; // '\n' q += 1u64; }; let s: u64 = p; for (s < q) { - if (buf[s] != 32u8) { - if (buf[s] != 9u8) { break; }; + if (bufp[s] != 32u8) { + if (bufp[s] != 9u8) { break; }; }; s += 1u64; }; if (s < q) { let line: str; - line.ptr = buf.ptr + s; + line.ptr = bufp + s; line.len = (q - s): i32; // hasprefix("//") subsumes the old s+1= 97u8) { if (ch <= 122u8) { isalpha = true; }; }; if (ch >= 65u8) { if (ch <= 90u8) { isalpha = true; }; }; @@ -3384,7 +3390,7 @@ fn peekpackage(pathcs: *u8) *u8 = { if (plen == 0u64) { return nil; }; let r: []u8 = alloc([], plen + 1u64)!; let k: u64 = 0u64; - for (k < plen) { r[k] = buf[start + k]; k += 1u64; }; + for (k < plen) { r[k] = bufp[start + k]; k += 1u64; }; r[plen] = 0u8; return r.ptr; }; diff --git a/selfhost/cmd/ww/main.ww b/selfhost/cmd/ww/main.ww index e9136ea4..c2baefe2 100644 --- a/selfhost/cmd/ww/main.ww +++ b/selfhost/cmd/ww/main.ww @@ -537,32 +537,38 @@ fn expand(c: *expctx, pathcs: *u8) void = { // starts with `package ;` return the package name as a fresh // heap-allocated NUL-terminated *u8, else nil. Same shape as cstage // peek_package. +// +// Reads the WHOLE file (via slurp), not a fixed prefix: cstage's +// peek_package scans line-by-line with fgets and no total cap, stopping +// at the first non-comment-non-blank line. A prior 2048-byte read cap +// here diverged from cstage on files whose `package` decl sits behind a +// >2048-byte comment header (strconv decimal/ftos/stof, memio) — +// returning nil and making the #16 D-i injection asymmetric (rule-10 +// break, byte-id divergence in the regenerated combined). The corpus has +// no line >2047 chars, so a whole-file line scan matches cstage's +// per-line fgets byte-for-byte on every real input. fn peekpackage(pathcs: *u8) *u8 = { - let fd: i32 = os.open(pathstr(pathcs), os.flag.RDONLY, 0i32); - if (fd < 0) { return nil; }; - let buf: []u8 = alloc([], 2048u64)!; - buf.len = 2048; - let n: i64 = os.read(fd, buf.ptr, 2048u64); - os.close(fd); - if (n <= 0i64) { return nil; }; - let nu: u64 = n: u64; + let bufp: *u8; + let nu: u64; + bufp, nu = slurp(pathcs); + if (bufp == nil) { return nil; }; let p: u64 = 0u64; for (p < nu) { let q: u64 = p; for (q < nu) { - if (buf[q] == 10u8) { break; }; // '\n' + if (bufp[q] == 10u8) { break; }; // '\n' q += 1u64; }; let s: u64 = p; for (s < q) { - if (buf[s] != 32u8) { - if (buf[s] != 9u8) { break; }; + if (bufp[s] != 32u8) { + if (bufp[s] != 9u8) { break; }; }; s += 1u64; }; if (s < q) { let line: str; - line.ptr = buf.ptr + s; + line.ptr = bufp + s; line.len = (q - s): i32; // hasprefix("//") subsumes the old s+1= 97u8) { if (ch <= 122u8) { isalpha = true; }; }; if (ch >= 65u8) { if (ch <= 90u8) { isalpha = true; }; }; @@ -598,7 +604,7 @@ fn peekpackage(pathcs: *u8) *u8 = { if (plen == 0u64) { return nil; }; let r: []u8 = alloc([], plen + 1u64)!; let k: u64 = 0u64; - for (k < plen) { r[k] = buf[start + k]; k += 1u64; }; + for (k < plen) { r[k] = bufp[start + k]; k += 1u64; }; r[plen] = 0u8; return r.ptr; };