ww driver: enumeratedir grows past 256 entries
The wwstage driver silently dropped directory entries past a 256 cap; the cstage twin already grows by realloc-doubling. Grow the same way (seed 8, double) so both stages agree on any directory size.
This commit is contained in:
12
Makefile
12
Makefile
@@ -262,6 +262,7 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
|
||||
$(BIN)/test_slttypepref_run \
|
||||
$(BIN)/test_defercap_run \
|
||||
$(BIN)/test_loopcap_run \
|
||||
$(BIN)/test_enumcap_run \
|
||||
$(BIN)/test_ampfncollide_run \
|
||||
$(BIN)/test_trycallcollide_run \
|
||||
$(BIN)/test_gunsigned_run \
|
||||
@@ -817,6 +818,17 @@ $(BIN)/test_loopcap_run: test/wcc/989_loopcap_run.c \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
# 989_enumcap_run (#65, F13 c3): the driver's directory enumerator grows
|
||||
# dynamically (mirror cstage realloc-doubling) instead of silently capping at
|
||||
# 256 files. Builds on both driver twins and asserts a byte-identical
|
||||
# combined.ww on a >256-file module (rule-10). See the test header.
|
||||
$(BIN)/test_enumcap_run: test/wcc/989_enumcap_run.c \
|
||||
$(BIN)/ww $(BIN)/ww_ww \
|
||||
$(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
# 989_ampfncollide_run (#4, c2): `&fn` synthesis (unoptype TK_AMP +
|
||||
# assignableaddrfn) prefers the current module's fn when a same-leaf fn is
|
||||
# declared in a later module. Builds/rejects on BOTH driver twins (rule-10).
|
||||
|
||||
@@ -3199,9 +3199,13 @@ fn bytecmp(a: *u8, alen: u64, b: *u8, blen: u64) i32 = {
|
||||
fn enumeratedir(dirpath: *u8) (**u8, i32) = {
|
||||
let fd: i32 = os.open(pathstr(dirpath), os.flag.RDONLY, 0i32);
|
||||
if (fd < 0) { return nil: **u8, 0; };
|
||||
let maxnames: i32 = 256;
|
||||
let names: []*u8 = alloc([], maxnames: u64)!;
|
||||
let nlens: []u64 = alloc([], maxnames: u64)!;
|
||||
// #65: grow-dynamic (mirror cstage enumerate_dir_ww realloc-doubling,
|
||||
// cmd/ww/main.c:209). The old fixed 256-name cap silently dropped every
|
||||
// eligible file past it, diverging the combined.ww from cstage on a
|
||||
// module dir with >256 sources.
|
||||
let cap: i32 = 8;
|
||||
let names: []*u8 = alloc([], cap: u64)!;
|
||||
let nlens: []u64 = alloc([], cap: u64)!;
|
||||
let n: i32 = 0;
|
||||
let buf: []u8 = alloc([], 8192u64)!;
|
||||
buf.len = 8192;
|
||||
@@ -3216,15 +3220,27 @@ fn enumeratedir(dirpath: *u8) (**u8, i32) = {
|
||||
let nm: *u8 = buf.ptr + off + 19u64;
|
||||
let nl: u64 = cstrlen(nm);
|
||||
if (dirfilekeep(nm, nl)) {
|
||||
if (n < maxnames) {
|
||||
let cp: []u8 = alloc([], nl + 1u64)!;
|
||||
let i: u64 = 0u64;
|
||||
for (i < nl) { cp[i] = nm[i]; i += 1u64; };
|
||||
cp[nl] = 0u8;
|
||||
names[n] = cp.ptr;
|
||||
nlens[n] = nl;
|
||||
n += 1;
|
||||
if (n >= cap) {
|
||||
let ncap: i32 = cap * 2;
|
||||
let nn: []*u8 = alloc([], ncap: u64)!;
|
||||
let nl2: []u64 = alloc([], ncap: u64)!;
|
||||
let k: i32 = 0;
|
||||
for (k < n) {
|
||||
nn[k] = names[k];
|
||||
nl2[k] = nlens[k];
|
||||
k += 1;
|
||||
};
|
||||
names = nn;
|
||||
nlens = nl2;
|
||||
cap = ncap;
|
||||
};
|
||||
let cp: []u8 = alloc([], nl + 1u64)!;
|
||||
let i: u64 = 0u64;
|
||||
for (i < nl) { cp[i] = nm[i]; i += 1u64; };
|
||||
cp[nl] = 0u8;
|
||||
names[n] = cp.ptr;
|
||||
nlens[n] = nl;
|
||||
n += 1;
|
||||
};
|
||||
off += reclen;
|
||||
};
|
||||
|
||||
@@ -361,9 +361,13 @@ fn bytecmp(a: *u8, alen: u64, b: *u8, blen: u64) i32 = {
|
||||
fn enumeratedir(dirpath: *u8) (**u8, i32) = {
|
||||
let fd: i32 = os.open(pathstr(dirpath), os.flag.RDONLY, 0i32);
|
||||
if (fd < 0) { return nil: **u8, 0; };
|
||||
let maxnames: i32 = 256;
|
||||
let names: []*u8 = alloc([], maxnames: u64)!;
|
||||
let nlens: []u64 = alloc([], maxnames: u64)!;
|
||||
// #65: grow-dynamic (mirror cstage enumerate_dir_ww realloc-doubling,
|
||||
// cmd/ww/main.c:209). The old fixed 256-name cap silently dropped every
|
||||
// eligible file past it, diverging the combined.ww from cstage on a
|
||||
// module dir with >256 sources.
|
||||
let cap: i32 = 8;
|
||||
let names: []*u8 = alloc([], cap: u64)!;
|
||||
let nlens: []u64 = alloc([], cap: u64)!;
|
||||
let n: i32 = 0;
|
||||
let buf: []u8 = alloc([], 8192u64)!;
|
||||
buf.len = 8192;
|
||||
@@ -378,15 +382,27 @@ fn enumeratedir(dirpath: *u8) (**u8, i32) = {
|
||||
let nm: *u8 = buf.ptr + off + 19u64;
|
||||
let nl: u64 = cstrlen(nm);
|
||||
if (dirfilekeep(nm, nl)) {
|
||||
if (n < maxnames) {
|
||||
let cp: []u8 = alloc([], nl + 1u64)!;
|
||||
let i: u64 = 0u64;
|
||||
for (i < nl) { cp[i] = nm[i]; i += 1u64; };
|
||||
cp[nl] = 0u8;
|
||||
names[n] = cp.ptr;
|
||||
nlens[n] = nl;
|
||||
n += 1;
|
||||
if (n >= cap) {
|
||||
let ncap: i32 = cap * 2;
|
||||
let nn: []*u8 = alloc([], ncap: u64)!;
|
||||
let nl2: []u64 = alloc([], ncap: u64)!;
|
||||
let k: i32 = 0;
|
||||
for (k < n) {
|
||||
nn[k] = names[k];
|
||||
nl2[k] = nlens[k];
|
||||
k += 1;
|
||||
};
|
||||
names = nn;
|
||||
nlens = nl2;
|
||||
cap = ncap;
|
||||
};
|
||||
let cp: []u8 = alloc([], nl + 1u64)!;
|
||||
let i: u64 = 0u64;
|
||||
for (i < nl) { cp[i] = nm[i]; i += 1u64; };
|
||||
cp[nl] = 0u8;
|
||||
names[n] = cp.ptr;
|
||||
nlens[n] = nl;
|
||||
n += 1;
|
||||
};
|
||||
off += reclen;
|
||||
};
|
||||
|
||||
155
test/wcc/989_enumcap_run.c
Normal file
155
test/wcc/989_enumcap_run.c
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 989_enumcap_run (#65, F13 c3) — the driver's directory enumerator must
|
||||
* bundle EVERY eligible *.ww source, not silently cap at 256.
|
||||
*
|
||||
* THE BUG (wwstage driver only, cat-A silent wrong combined.ww under rc=0):
|
||||
* enumeratedir (selfhost/cmd/ww/main.ww) capped at maxnames=256 and the
|
||||
* `if (n < maxnames)` guard silently discarded every further eligible file —
|
||||
* no diagnostic. cstage enumerate_dir_ww (cmd/ww/main.c:209) grows via
|
||||
* realloc doubling, unbounded. A module dir with >256 sources bundled all of
|
||||
* them under cstage vs 256 under wwstage, a silently divergent combined.ww
|
||||
* (and binary) under rc=0 — invisible to the byte-id gates (no in-tree
|
||||
* module nears 256 files). THE FIX (CAP-SEMANTICS rule: the cstage twin grows,
|
||||
* so grow-dynamic): mirror the realloc-doubling growth in enumeratedir.
|
||||
*
|
||||
* row | files | result (cs==ww)
|
||||
* --------+-------+------------------------------------------
|
||||
* big_300 | 300 | both bundle 300, combined.ww byte-id
|
||||
* small_5 | 5 | both bundle 5, combined.ww byte-id (control)
|
||||
*
|
||||
* big_300 was RED pre-c3 (ww bundled 256, cs 300 — divergent combined.ww).
|
||||
* small_5 pins the common ≤cap path stays byte-identical.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Count '^package bigmod;' lines in a file. -1 on open failure. */
|
||||
static int
|
||||
count_pkgs(const char *path)
|
||||
{
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) return -1;
|
||||
char line[256];
|
||||
int n = 0;
|
||||
while (fgets(line, sizeof line, f))
|
||||
if (strncmp(line, "package bigmod;", 15) == 0) n++;
|
||||
fclose(f);
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Returns 0 if both drivers produced a combined.ww with `nfiles` package
|
||||
* lines AND byte-identical content; non-zero (and prints) otherwise. */
|
||||
static int
|
||||
one_row(const char *label, const char *cdrv, const char *wdrv, int nfiles,
|
||||
int have_ww)
|
||||
{
|
||||
char dir[64], main_ww[128], cstem[128], wstem[128];
|
||||
char ccomb[160], wcomb[160], cmd[1024], fn[160];
|
||||
snprintf(dir, sizeof dir, "/tmp/encap_%d_%s", getpid(), label);
|
||||
snprintf(cmd, sizeof cmd, "rm -rf %s && mkdir -p %s/bigmod", dir, dir);
|
||||
system(cmd);
|
||||
|
||||
for (int i = 0; i < nfiles; i++) {
|
||||
snprintf(fn, sizeof fn, "%s/bigmod/f%03d.ww", dir, i);
|
||||
FILE *f = fopen(fn, "wb");
|
||||
if (!f) return 1;
|
||||
fprintf(f, "package bigmod;\nexport fn f%03d() i32 = "
|
||||
"{ return %d; };\n", i, i);
|
||||
fclose(f);
|
||||
}
|
||||
snprintf(main_ww, sizeof main_ww, "%s/main.ww", dir);
|
||||
FILE *mf = fopen(main_ww, "wb");
|
||||
if (!mf) return 1;
|
||||
fputs("package main;\nimport bigmod;\nfn main() void = {};\n", mf);
|
||||
fclose(mf);
|
||||
|
||||
snprintf(cstem, sizeof cstem, "%s/X_c", dir);
|
||||
snprintf(wstem, sizeof wstem, "%s/X_w", dir);
|
||||
snprintf(ccomb, sizeof ccomb, "%s.combined.ww", cstem);
|
||||
snprintf(wcomb, sizeof wcomb, "%s.combined.ww", wstem);
|
||||
|
||||
int fail = 0;
|
||||
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
|
||||
cdrv, cstem, main_ww);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "enumcap[cstage][%s]: build failed\n", label);
|
||||
fail++;
|
||||
}
|
||||
int cn = count_pkgs(ccomb);
|
||||
if (cn != nfiles) {
|
||||
fprintf(stderr, "enumcap[cstage][%s]: bundled %d want %d\n",
|
||||
label, cn, nfiles);
|
||||
fail++;
|
||||
}
|
||||
|
||||
if (have_ww) {
|
||||
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
|
||||
wdrv, wstem, main_ww);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "enumcap[wwstage][%s]: build failed\n", label);
|
||||
fail++;
|
||||
}
|
||||
int wn = count_pkgs(wcomb);
|
||||
if (wn != nfiles) {
|
||||
fprintf(stderr, "enumcap[wwstage][%s]: bundled %d want %d "
|
||||
"(silent enumeratedir cap — #65)\n", label, wn, nfiles);
|
||||
fail++;
|
||||
}
|
||||
/* the decisive rule-10 assertion: identical combined.ww */
|
||||
snprintf(cmd, sizeof cmd, "cmp -s %s %s", ccomb, wcomb);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "enumcap[%s]: cs/ww combined.ww differ (#65)\n",
|
||||
label);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(cmd, sizeof cmd, "rm -rf %s", dir);
|
||||
system(cmd);
|
||||
return fail;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[1024], wdrv[1024];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
int have_ww = (access(wdrv, X_OK) == 0);
|
||||
if (!have_ww)
|
||||
fprintf(stderr, "enumcap: skip wwstage (no %s)\n", wdrv);
|
||||
|
||||
int fail = 0, total = 2;
|
||||
fail += one_row("big_300", cdrv, wdrv, 300, have_ww);
|
||||
fail += one_row("small_5", cdrv, wdrv, 5, have_ww);
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "enumcap_run: %d check(s) failed\n", fail);
|
||||
return 1;
|
||||
}
|
||||
printf("enumcap_run: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user