@ai-ecoverse/slicc-kernel 1.54.2 → 1.54.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1188,6 +1188,32 @@ function bytesSource(data) {
1188
1188
  }
1189
1189
  });
1190
1190
  }
1191
+ function memoryFile(data, path, position = 0) {
1192
+ let offset = position;
1193
+ return new OpenFile({
1194
+ read: async (max) => {
1195
+ const out = data.subarray(offset, offset + max);
1196
+ offset += out.length;
1197
+ return out;
1198
+ },
1199
+ pread: async (max, at) => data.subarray(at, at + max),
1200
+ seek: async (by, whence) => {
1201
+ const at = (whence === 1 ? offset : whence === 2 ? data.length : 0) + by;
1202
+ if (at < 0) throw new KernelError("EINVAL");
1203
+ offset = at;
1204
+ return at;
1205
+ },
1206
+ pwrite: async () => {
1207
+ throw new KernelError("EBADF");
1208
+ },
1209
+ resize: async () => {
1210
+ throw new KernelError("EBADF");
1211
+ },
1212
+ stat: async () => ({ path, size: data.length }),
1213
+ close: () => {
1214
+ }
1215
+ });
1216
+ }
1191
1217
  function sinkFile(onData) {
1192
1218
  return new OpenFile({
1193
1219
  write: async (bytes) => {
@@ -7459,150 +7485,728 @@ function attachSyncSabResponder(port, sab, token, opts) {
7459
7485
  };
7460
7486
  }
7461
7487
 
7462
- // src/kernel/host-ops.ts
7463
- var HOST_OPS = [
7464
- "lock",
7465
- "unlock",
7466
- "async-submit",
7467
- "async-wait",
7468
- "async-take",
7469
- "async-resolve",
7470
- "async-hold",
7471
- "async-cancel",
7472
- "async-close"
7473
- ];
7474
- var json3 = (value) => ({ ok: true, kind: "json", json: value });
7475
- var done2 = { ok: true, kind: "void" };
7476
- var LockTable = class {
7477
- held = /* @__PURE__ */ new Map();
7478
- lock(pid, path, exclusive, fd) {
7479
- const holders = this.held.get(path) ?? /* @__PURE__ */ new Map();
7480
- for (const [other, h] of holders) if (other !== pid && (exclusive || h.exclusive)) return false;
7481
- const mine = holders.get(pid) ?? { exclusive, fds: /* @__PURE__ */ new Set() };
7482
- mine.exclusive = exclusive;
7483
- mine.fds.add(fd);
7484
- holders.set(pid, mine);
7485
- this.held.set(path, holders);
7486
- return true;
7488
+ // src/process/process-fds.ts
7489
+ var field2 = (text) => (text || "none").replace(/[\s\\]/g, (c) => `\\${c.charCodeAt(0).toString(8).padStart(3, "0")}`);
7490
+ function mountTable(mounts = []) {
7491
+ const root = globalThis.navigator?.storage ? "opfs" : "memory";
7492
+ const lines2 = [`${root} / ${root} rw 0 0`, "devfs /dev devfs rw 0 0", "proc /proc proc rw 0 0"];
7493
+ for (const m of mounts) {
7494
+ const options = Object.entries(m.options).map(([k, v]) => v === "" ? k : `${k}=${v}`);
7495
+ if (m.state !== "ok") options.push(m.state);
7496
+ const access = options.includes("ro") ? [] : ["rw"];
7497
+ lines2.push(
7498
+ `${field2(m.source)} ${field2(m.target)} ${m.type} ${[...access, ...options].join(",")} 0 0`
7499
+ );
7487
7500
  }
7488
- unlock(pid, path) {
7489
- const holders = this.held.get(path);
7490
- holders?.delete(pid);
7491
- if (holders?.size === 0) this.held.delete(path);
7501
+ return `${lines2.join("\n")}
7502
+ `;
7503
+ }
7504
+
7505
+ // src/process/procfs.ts
7506
+ var HZ = 100;
7507
+ var KIB = 1024;
7508
+ var FALLBACK_MEMORY = 4 * KIB * KIB * KIB;
7509
+ var PID_FILES = ["cmdline", "comm", "stat", "statm", "status"];
7510
+ var SYSTEM_FILES = ["loadavg", "meminfo", "stat", "uptime"];
7511
+ function commOf(info) {
7512
+ const name2 = info.argv[0] ?? "";
7513
+ return name2.slice(name2.lastIndexOf("/") + 1).slice(0, 15);
7514
+ }
7515
+ function ttyNumber(tty) {
7516
+ const pts = tty && /^\/dev\/pts\/(\d+)$/.exec(tty);
7517
+ if (pts) return 136 << 8 | Number(pts[1]);
7518
+ const vt = tty && /^\/dev\/tty(\d+)$/.exec(tty);
7519
+ return vt ? 4 << 8 | Number(vt[1]) : 0;
7520
+ }
7521
+ function pages(bytes) {
7522
+ return Math.ceil(bytes / 4096);
7523
+ }
7524
+ function ticks(ms) {
7525
+ return Math.max(0, Math.floor(ms * HZ / 1e3));
7526
+ }
7527
+ function pidStat(info, boot) {
7528
+ const fields = [
7529
+ info.pid,
7530
+ `(${commOf(info)})`,
7531
+ info.state,
7532
+ info.ppid,
7533
+ info.pgid,
7534
+ info.sid,
7535
+ ttyNumber(info.tty),
7536
+ info.tty ? info.pgid : -1,
7537
+ 4194304,
7538
+ 0,
7539
+ 0,
7540
+ 0,
7541
+ 0,
7542
+ 0,
7543
+ 0,
7544
+ 0,
7545
+ 0,
7546
+ 20,
7547
+ 0,
7548
+ 1,
7549
+ 0,
7550
+ ticks(info.started - boot),
7551
+ info.memory,
7552
+ pages(info.memory),
7553
+ "18446744073709551615",
7554
+ ...Array(12).fill(0),
7555
+ 17,
7556
+ 0,
7557
+ 0,
7558
+ 0,
7559
+ 0,
7560
+ 0,
7561
+ 0,
7562
+ ...Array(7).fill(0),
7563
+ 0
7564
+ ];
7565
+ return `${fields.join(" ")}
7566
+ `;
7567
+ }
7568
+ var sigMask = (mask = 0) => (mask >>> 1).toString(16).padStart(16, "0");
7569
+ function pidStatus(info) {
7570
+ const state = info.state === "Z" ? "Z (zombie)" : "S (sleeping)";
7571
+ const c = info.cred ?? ROOT;
7572
+ const ids = (r, e, s) => `${r} ${e} ${s} ${e}`;
7573
+ return [
7574
+ `Name: ${commOf(info)}`,
7575
+ `Umask: ${(info.umask ?? 18).toString(8).padStart(4, "0")}`,
7576
+ `State: ${state}`,
7577
+ `Tgid: ${info.pid}`,
7578
+ "Ngid: 0",
7579
+ `Pid: ${info.pid}`,
7580
+ `PPid: ${info.ppid}`,
7581
+ "TracerPid: 0",
7582
+ `Uid: ${ids(c.ruid, c.euid, c.suid)}`,
7583
+ `Gid: ${ids(c.rgid, c.egid, c.sgid)}`,
7584
+ "FDSize: 64",
7585
+ `VmSize: ${Math.ceil(info.memory / KIB)} kB`,
7586
+ `VmRSS: ${Math.ceil(info.memory / KIB)} kB`,
7587
+ `Groups: ${c.groups.join(" ")}`,
7588
+ `NSpid: ${info.pid}`,
7589
+ `NSpgid: ${info.pgid}`,
7590
+ `NSsid: ${info.sid}`,
7591
+ "Threads: 1",
7592
+ "SigQ: 0/0",
7593
+ "SigPnd: 0000000000000000",
7594
+ "ShdPnd: 0000000000000000",
7595
+ "SigBlk: 0000000000000000",
7596
+ `SigIgn: ${sigMask(info.sigIgn)}`,
7597
+ `SigCgt: ${sigMask(info.sigCgt)}`,
7598
+ ""
7599
+ ].join("\n");
7600
+ }
7601
+ function pidFile(name2, info, boot) {
7602
+ if (name2 === "cmdline") return info.state === "Z" ? "" : `${info.argv.join("\0")}\0`;
7603
+ if (name2 === "comm") return `${commOf(info)}
7604
+ `;
7605
+ if (name2 === "stat") return pidStat(info, boot);
7606
+ if (name2 === "statm") {
7607
+ const p = pages(info.memory);
7608
+ return `${p} ${p} 0 0 0 ${p} 0
7609
+ `;
7492
7610
  }
7493
- closed(pid, fd) {
7494
- for (const [path, holders] of [...this.held]) {
7495
- const mine = holders.get(pid);
7496
- if (mine?.fds.delete(fd) && mine.fds.size === 0) this.unlock(pid, path);
7497
- }
7611
+ return pidStatus(info);
7612
+ }
7613
+ function memoryInfo(source, used = 0) {
7614
+ const total = source.deviceMemory ? source.deviceMemory * KIB * KIB * KIB : FALLBACK_MEMORY;
7615
+ const free = Math.max(0, total - used - (source.usedHeap ?? 0));
7616
+ const kb = (bytes) => `${Math.floor(bytes / KIB)} kB`;
7617
+ return [
7618
+ ["MemTotal", kb(total)],
7619
+ ["MemFree", kb(free)],
7620
+ ["MemAvailable", kb(free)],
7621
+ ["Buffers", kb(0)],
7622
+ ["Cached", kb(0)],
7623
+ ["SwapCached", kb(0)],
7624
+ ["Shmem", kb(0)],
7625
+ ["SReclaimable", kb(0)],
7626
+ ["SwapTotal", kb(0)],
7627
+ ["SwapFree", kb(0)]
7628
+ ].map(([key, value]) => `${`${key}:`.padEnd(16)}${value.padStart(12)}`).concat("").join("\n");
7629
+ }
7630
+ function systemFile(name2, listing, now, memory) {
7631
+ const up = Math.max(0, now - listing.boot) / 1e3;
7632
+ if (name2 === "uptime") return `${up.toFixed(2)} ${up.toFixed(2)}
7633
+ `;
7634
+ if (name2 === "meminfo") {
7635
+ return memoryInfo(
7636
+ memory,
7637
+ listing.processes.reduce((sum, p) => sum + p.memory, 0)
7638
+ );
7498
7639
  }
7499
- release(pid) {
7500
- for (const path of [...this.held.keys()]) this.unlock(pid, path);
7640
+ const live = listing.processes.filter((p) => p.state !== "Z");
7641
+ if (name2 === "loadavg") {
7642
+ const last = Math.max(0, ...listing.processes.map((p) => p.pid));
7643
+ return `0.00 0.00 0.00 1/${live.length} ${last}
7644
+ `;
7501
7645
  }
7502
- };
7503
- var AsyncOps = class {
7504
- next = 1;
7505
- results = /* @__PURE__ */ new Map();
7506
- ready = [];
7507
- running = /* @__PURE__ */ new Map();
7508
- waiters = /* @__PURE__ */ new Set();
7509
- closed = false;
7510
- run;
7511
- onReady;
7512
- constructor(run2, onReady) {
7513
- this.run = run2;
7514
- this.onReady = onReady;
7646
+ const idle = ticks(now - listing.boot);
7647
+ return [
7648
+ `cpu 0 0 0 ${idle} 0 0 0 0 0 0`,
7649
+ `cpu0 0 0 0 ${idle} 0 0 0 0 0 0`,
7650
+ "intr 0",
7651
+ "ctxt 0",
7652
+ `btime ${Math.floor(listing.boot / 1e3)}`,
7653
+ `processes ${listing.processes.length}`,
7654
+ "procs_running 1",
7655
+ "procs_blocked 0",
7656
+ ""
7657
+ ].join("\n");
7658
+ }
7659
+ function memorySource(scope = globalThis) {
7660
+ const nav = scope.navigator;
7661
+ const perf = scope.performance;
7662
+ const deviceMemory = nav?.deviceMemory;
7663
+ const used = perf?.memory?.usedJSHeapSize;
7664
+ return {
7665
+ ...typeof deviceMemory === "number" && deviceMemory > 0 ? { deviceMemory } : {},
7666
+ ...typeof used === "number" ? { usedHeap: used } : {}
7667
+ };
7668
+ }
7669
+
7670
+ // src/fs/pages.ts
7671
+ var PAGE = 64 * 1024;
7672
+ var READAHEAD = 1024 * 1024;
7673
+ var CLEAN_LIMIT = 4 * 1024 * 1024;
7674
+ var WRITEBACK = 8 * 1024 * 1024;
7675
+ var RUN = 1024 * 1024;
7676
+ var Pages = class {
7677
+ length;
7678
+ stored;
7679
+ shrink;
7680
+ pages = /* @__PURE__ */ new Map();
7681
+ dirty = /* @__PURE__ */ new Set();
7682
+ free = [];
7683
+ pinned = false;
7684
+ constructor(size) {
7685
+ this.length = size;
7686
+ this.stored = size;
7515
7687
  }
7516
- submit(req) {
7517
- const id2 = this.hold();
7518
- const running = new AbortController();
7519
- this.running.set(id2, running);
7520
- this.run(req, running.signal).then(
7521
- (result) => this.complete(id2, result),
7522
- (err) => this.complete(id2, { ok: false, errno: "EIO", message: String(err) })
7523
- );
7524
- return id2;
7688
+ get dirtyBytes() {
7689
+ return this.dirty.size * PAGE;
7525
7690
  }
7526
- resolve(value) {
7527
- const id2 = this.hold();
7528
- this.complete(id2, json3(value));
7529
- return id2;
7691
+ get changed() {
7692
+ return this.dirty.size > 0 || this.shrink !== void 0 || this.length !== this.stored;
7530
7693
  }
7531
- hold() {
7532
- const id2 = this.next++;
7533
- this.results.set(id2, void 0);
7534
- return id2;
7694
+ missing(at, length2) {
7695
+ this.evict();
7696
+ const end = Math.min(this.stored, at + length2, this.length);
7697
+ const runs = [];
7698
+ for (let page = Math.floor(at / PAGE); page * PAGE < end; page++) {
7699
+ if (this.pages.has(page)) continue;
7700
+ const last2 = runs[runs.length - 1];
7701
+ if (last2 && last2[0] + last2[1] === page * PAGE) last2[1] += PAGE;
7702
+ else runs.push([page * PAGE, PAGE]);
7703
+ }
7704
+ const last = runs[runs.length - 1];
7705
+ if (last) last[1] = Math.max(last[1], Math.min(READAHEAD, this.stored - last[0]));
7706
+ return runs.map(([start2, n]) => [start2, Math.min(n, this.stored - start2)]);
7535
7707
  }
7536
- take(id2) {
7537
- if (!this.results.has(id2)) throw new KernelError("EINVAL");
7538
- const result = this.results.get(id2);
7539
- if (!result) throw new KernelError("EAGAIN");
7540
- this.forget(id2);
7541
- return result;
7708
+ partial(at, length2) {
7709
+ if (length2 === 0) return [];
7710
+ this.evict();
7711
+ const edges = /* @__PURE__ */ new Set([Math.floor(at / PAGE), Math.floor((at + length2 - 1) / PAGE)]);
7712
+ const runs = [];
7713
+ for (const page of edges) {
7714
+ const start2 = page * PAGE;
7715
+ const covered = at <= start2 && at + length2 >= start2 + PAGE;
7716
+ if (covered || this.pages.has(page) || start2 >= this.stored) continue;
7717
+ runs.push([start2, Math.min(PAGE, this.stored - start2)]);
7718
+ }
7719
+ return runs;
7542
7720
  }
7543
- cancel(id2) {
7544
- this.running.get(id2)?.abort();
7545
- this.forget(id2);
7721
+ fill(start2, bytes) {
7722
+ for (let at = 0; at < bytes.length; at += PAGE) {
7723
+ const page = (start2 + at) / PAGE;
7724
+ if (this.pages.has(page)) continue;
7725
+ const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
7726
+ const fresh = this.take(part.length);
7727
+ fresh.set(part);
7728
+ this.pages.set(page, fresh);
7729
+ }
7546
7730
  }
7547
- forget(id2) {
7548
- this.running.delete(id2);
7549
- this.results.delete(id2);
7550
- const at = this.ready.indexOf(id2);
7551
- if (at >= 0) this.ready.splice(at, 1);
7731
+ read(at, length2) {
7732
+ const out = new Uint8Array(Math.max(0, Math.min(this.length, at + length2) - at));
7733
+ this.readInto(at, out);
7734
+ return out;
7552
7735
  }
7553
- async wait(timeoutMs, signal) {
7554
- for (; ; ) {
7555
- if (this.closed) return -1;
7556
- const id2 = this.ready.shift();
7557
- if (id2 !== void 0) return id2;
7558
- if (timeoutMs !== void 0 && timeoutMs <= 0) return 0;
7559
- const woke = await this.sleep(timeoutMs, signal);
7560
- if (!woke) return 0;
7736
+ readInto(at, out) {
7737
+ const end = Math.min(this.length, at + out.length);
7738
+ for (let pos = at; pos < end; ) {
7739
+ const page = Math.floor(pos / PAGE);
7740
+ const from = pos - page * PAGE;
7741
+ const n = Math.min(PAGE - from, end - pos);
7742
+ const bytes = this.pages.get(page);
7743
+ if (bytes) out.set(bytes.subarray(from, from + n), pos - at);
7744
+ else out.fill(0, pos - at, pos - at + n);
7745
+ pos += n;
7561
7746
  }
7747
+ return Math.max(0, end - at);
7562
7748
  }
7563
- close() {
7564
- this.closed = true;
7565
- this.wake();
7566
- }
7567
- sleep(timeoutMs, signal) {
7568
- return new Promise((resolve, reject) => {
7569
- let timer;
7570
- const cleanup = () => {
7571
- clearTimeout(timer);
7572
- this.waiters.delete(wake);
7573
- signal.removeEventListener("abort", abort);
7574
- };
7575
- const wake = () => {
7576
- cleanup();
7577
- resolve(true);
7578
- };
7579
- const abort = () => {
7580
- cleanup();
7581
- reject(new KernelError("EINTR"));
7582
- };
7583
- this.waiters.add(wake);
7584
- signal.addEventListener("abort", abort, { once: true });
7585
- if (timeoutMs !== void 0) {
7586
- timer = setTimeout(() => {
7587
- cleanup();
7588
- resolve(false);
7589
- }, timeoutMs);
7749
+ write(at, bytes) {
7750
+ for (let done4 = 0; done4 < bytes.length; ) {
7751
+ const pos = at + done4;
7752
+ const page = Math.floor(pos / PAGE);
7753
+ const from = pos - page * PAGE;
7754
+ const n = Math.min(PAGE - from, bytes.length - done4);
7755
+ let target = this.pages.get(page);
7756
+ if (!target) {
7757
+ target = this.take(0);
7758
+ this.pages.set(page, target);
7590
7759
  }
7591
- });
7592
- }
7593
- complete(id2, result) {
7594
- this.running.delete(id2);
7595
- if (!this.results.has(id2)) return;
7596
- this.results.set(id2, result);
7597
- this.ready.push(id2);
7598
- this.wake();
7599
- this.onReady?.();
7760
+ target.set(bytes.subarray(done4, done4 + n), from);
7761
+ this.dirty.add(page);
7762
+ done4 += n;
7763
+ }
7764
+ this.length = Math.max(this.length, at + bytes.length);
7600
7765
  }
7601
- wake() {
7602
- for (const wake of [...this.waiters]) wake();
7766
+ truncate(size) {
7767
+ if (size < this.stored) {
7768
+ this.stored = size;
7769
+ this.shrink = Math.min(this.shrink ?? size, size);
7770
+ }
7771
+ if (size < this.length) {
7772
+ const edge = Math.floor(size / PAGE);
7773
+ for (const page of [...this.pages.keys()]) {
7774
+ if (page > edge || page === edge && size === edge * PAGE) this.drop(page);
7775
+ }
7776
+ this.pages.get(edge)?.fill(0, size - edge * PAGE);
7777
+ }
7778
+ this.length = size;
7603
7779
  }
7604
- };
7605
- async function hostSyscall(req, ctx) {
7780
+ plan() {
7781
+ const runs = [];
7782
+ const sorted = [...this.dirty].sort((a, b) => a - b);
7783
+ for (let i = 0; i < sorted.length; ) {
7784
+ let j = i + 1;
7785
+ const first = sorted[i];
7786
+ while (j < sorted.length && sorted[j] === first + (j - i) && sorted[j] * PAGE % RUN !== 0) {
7787
+ j++;
7788
+ }
7789
+ const start2 = first * PAGE;
7790
+ const end = Math.min(this.length, (first + (j - i)) * PAGE);
7791
+ const bytes = new Uint8Array(end - start2);
7792
+ for (let k = i; k < j; k++) {
7793
+ const page = this.pages.get(sorted[k]);
7794
+ bytes.set(page.subarray(0, Math.min(PAGE, end - (k - i) * PAGE - start2)), (k - i) * PAGE);
7795
+ }
7796
+ runs.push({ offset: start2, bytes });
7797
+ i = j;
7798
+ }
7799
+ const written2 = Math.max(this.stored, ...runs.map((r) => r.offset + r.bytes.length));
7800
+ return {
7801
+ ...this.shrink !== void 0 ? { shrink: this.shrink } : {},
7802
+ runs,
7803
+ ...this.length > written2 ? { extend: this.length } : {}
7804
+ };
7805
+ }
7806
+ flushed() {
7807
+ this.dirty.clear();
7808
+ this.shrink = void 0;
7809
+ this.stored = this.length;
7810
+ this.evict();
7811
+ }
7812
+ pin() {
7813
+ this.pinned = true;
7814
+ }
7815
+ reset(size) {
7816
+ if (this.pinned) return;
7817
+ for (const page of [...this.pages.keys()]) if (!this.dirty.has(page)) this.drop(page);
7818
+ if (this.changed) return;
7819
+ this.length = size;
7820
+ this.stored = size;
7821
+ }
7822
+ evict() {
7823
+ if (this.pinned) return;
7824
+ let clean = (this.pages.size - this.dirty.size) * PAGE;
7825
+ for (const page of this.pages.keys()) {
7826
+ if (clean <= CLEAN_LIMIT) return;
7827
+ if (this.dirty.has(page)) continue;
7828
+ this.drop(page);
7829
+ clean -= PAGE;
7830
+ }
7831
+ }
7832
+ take(used) {
7833
+ const page = this.free.pop();
7834
+ if (!page) return new Uint8Array(PAGE);
7835
+ page.fill(0, used);
7836
+ return page;
7837
+ }
7838
+ drop(page) {
7839
+ const bytes = this.pages.get(page);
7840
+ this.pages.delete(page);
7841
+ this.dirty.delete(page);
7842
+ if (this.free.length < CLEAN_LIMIT / PAGE) this.free.push(bytes);
7843
+ }
7844
+ };
7845
+
7846
+ // src/fs/ranged.ts
7847
+ var AsyncRangedFile = class {
7848
+ pages;
7849
+ io;
7850
+ pinned = false;
7851
+ version;
7852
+ path;
7853
+ constructor(io, path, size, version) {
7854
+ this.io = io;
7855
+ this.path = path;
7856
+ this.version = version;
7857
+ this.pages = new Pages(size);
7858
+ }
7859
+ size() {
7860
+ return this.pages.length;
7861
+ }
7862
+ async fetch(runs) {
7863
+ for (const [start2, n] of runs) {
7864
+ this.pages.fill(start2, await this.io.pread(this.path, start2, n, this.version));
7865
+ }
7866
+ }
7867
+ async read(at, length2) {
7868
+ await this.fetch(this.pages.missing(at, length2));
7869
+ return this.pages.read(at, length2);
7870
+ }
7871
+ async write(at, bytes) {
7872
+ await this.fetch(this.pages.partial(at, bytes.length));
7873
+ this.pages.write(at, bytes);
7874
+ if (!this.pinned && this.pages.dirtyBytes >= WRITEBACK) await this.flush();
7875
+ }
7876
+ truncate(size) {
7877
+ this.pages.truncate(size);
7878
+ }
7879
+ async flush() {
7880
+ if (this.pinned || !this.pages.changed) return;
7881
+ const plan = this.pages.plan();
7882
+ if (plan.shrink !== void 0) await this.io.truncate(this.path, plan.shrink);
7883
+ for (const run2 of plan.runs) await this.io.pwrite(this.path, run2.offset, run2.bytes, true);
7884
+ if (plan.extend !== void 0) await this.io.truncate(this.path, plan.extend);
7885
+ this.version = void 0;
7886
+ this.pages.flushed();
7887
+ }
7888
+ async pin() {
7889
+ this.pinned = true;
7890
+ this.pages.pin();
7891
+ await this.fetch(this.pages.missing(0, this.pages.length));
7892
+ }
7893
+ };
7894
+
7895
+ // src/process/wasi/wasi-files.ts
7896
+ function normalize(path) {
7897
+ const out = [];
7898
+ for (const seg of path.split("/")) {
7899
+ if (seg === "" || seg === ".") continue;
7900
+ if (seg === "..") out.pop();
7901
+ else out.push(seg);
7902
+ }
7903
+ return `/${out.join("/")}`;
7904
+ }
7905
+
7906
+ // src/process/wasi/wasi-procfs.ts
7907
+ var DIR_MODE = 16749;
7908
+ var FILE_MODE = 33060;
7909
+ var LINK_MODE = 41471;
7910
+ var PID_NAMES = PID_FILES;
7911
+ var SYSTEM_NAMES = SYSTEM_FILES;
7912
+ function inProc(path) {
7913
+ return path === "/proc" || path.startsWith("/proc/");
7914
+ }
7915
+ var ProcTree = class {
7916
+ source;
7917
+ pid;
7918
+ memory;
7919
+ constructor(source, pid, memory = memorySource()) {
7920
+ this.source = source;
7921
+ this.pid = pid;
7922
+ this.memory = memory;
7923
+ }
7924
+ listing() {
7925
+ return this.source.procList();
7926
+ }
7927
+ alive(pid) {
7928
+ return this.listing().processes.some((p) => p.pid === pid);
7929
+ }
7930
+ node(parts) {
7931
+ const [top, leaf, ...rest2] = parts;
7932
+ if (top === void 0) {
7933
+ return {
7934
+ kind: "dir",
7935
+ names: () => [
7936
+ ...SYSTEM_NAMES,
7937
+ "mounts",
7938
+ "self",
7939
+ ...this.listing().processes.map((p) => String(p.pid))
7940
+ ]
7941
+ };
7942
+ }
7943
+ if (top === "self") {
7944
+ return leaf === void 0 ? { kind: "link", target: String(this.pid) } : void 0;
7945
+ }
7946
+ if (leaf === void 0 && top === "mounts") {
7947
+ return { kind: "file", content: () => mountTable(this.source.mountList()) };
7948
+ }
7949
+ if (leaf === void 0 && SYSTEM_NAMES.includes(top)) {
7950
+ return {
7951
+ kind: "file",
7952
+ content: () => systemFile(top, this.listing(), Date.now(), this.memory)
7953
+ };
7954
+ }
7955
+ if (!/^[1-9]\d*$/.test(top) || rest2.length > 0 || !this.alive(Number(top))) return void 0;
7956
+ if (leaf === void 0) return { kind: "dir", names: () => [...PID_NAMES] };
7957
+ if (!PID_NAMES.includes(leaf)) return void 0;
7958
+ return {
7959
+ kind: "file",
7960
+ content: () => {
7961
+ const listing = this.listing();
7962
+ const info = listing.processes.find((p) => p.pid === Number(top));
7963
+ if (!info) throw syncError("ENOENT", `/proc/${top}/${leaf}`);
7964
+ return pidFile(leaf, info, listing.boot);
7965
+ }
7966
+ };
7967
+ }
7968
+ parts(path, follow2) {
7969
+ const parts = path.split("/").filter((p) => p !== "").slice(1);
7970
+ if (parts[0] === "self" && (follow2 || parts.length > 1)) parts[0] = String(this.pid);
7971
+ return parts;
7972
+ }
7973
+ find(path, follow2) {
7974
+ const node = this.node(this.parts(path, follow2));
7975
+ if (!node) throw syncError("ENOENT", path);
7976
+ return node;
7977
+ }
7978
+ stat(path, follow2 = true) {
7979
+ const node = this.find(path, follow2);
7980
+ const base = {
7981
+ isFile: false,
7982
+ isDirectory: false,
7983
+ size: 0,
7984
+ mtimeMs: Date.now(),
7985
+ readonly: true
7986
+ };
7987
+ if (node.kind === "dir") return { ...base, isDirectory: true, mode: DIR_MODE };
7988
+ if (node.kind === "link") {
7989
+ return { ...base, isSymbolicLink: true, mode: LINK_MODE, size: node.target.length };
7990
+ }
7991
+ return { ...base, isFile: true, mode: FILE_MODE, size: this.bytes(node).length };
7992
+ }
7993
+ bytes(node) {
7994
+ return new TextEncoder().encode(node.kind === "file" ? node.content() : "");
7995
+ }
7996
+ read(path) {
7997
+ const node = this.find(path, true);
7998
+ if (node.kind !== "file") throw syncError("EISDIR", path);
7999
+ return this.bytes(node);
8000
+ }
8001
+ readdir(path) {
8002
+ const node = this.find(path, true);
8003
+ if (node.kind !== "dir") throw syncError("ENOTDIR", path);
8004
+ return node.names();
8005
+ }
8006
+ readlink(path) {
8007
+ const node = this.find(path, false);
8008
+ if (node.kind !== "link") throw syncError("EINVAL", path);
8009
+ return node.target;
8010
+ }
8011
+ exists(path) {
8012
+ try {
8013
+ this.find(path, true);
8014
+ return true;
8015
+ } catch {
8016
+ return false;
8017
+ }
8018
+ }
8019
+ };
8020
+ var MAX_HOPS2 = 40;
8021
+ function* procWalk(path, follow2) {
8022
+ let done4 = [];
8023
+ let todo = path.split("/").filter((part) => part !== "");
8024
+ let hops = 0;
8025
+ while (todo.length > 0) {
8026
+ const part = todo.shift();
8027
+ if (part === ".") continue;
8028
+ if (part === "..") {
8029
+ done4.pop();
8030
+ continue;
8031
+ }
8032
+ const at = `/${[...done4, part].join("/")}`;
8033
+ const whole = normalize(`/${[...done4, part, ...todo].join("/")}`);
8034
+ if (inProc(at) && inProc(whole)) return whole;
8035
+ if (!follow2 && todo.length === 0) return void 0;
8036
+ const target = yield at;
8037
+ if (target === null) return void 0;
8038
+ if (target === void 0) done4.push(part);
8039
+ else {
8040
+ if (++hops > MAX_HOPS2) return void 0;
8041
+ if (target.startsWith("/")) done4 = [];
8042
+ todo = [...target.split("/").filter((part2) => part2 !== ""), ...todo];
8043
+ }
8044
+ }
8045
+ return void 0;
8046
+ }
8047
+ async function procAlias(fs, path, follow2) {
8048
+ const { readlink, lstat } = fs;
8049
+ if (!readlink || !lstat) return void 0;
8050
+ const physical = await lstat.call(fs, "/proc").then(
8051
+ () => true,
8052
+ () => false
8053
+ );
8054
+ if (!physical) return void 0;
8055
+ const walk = procWalk(path, follow2);
8056
+ let step = walk.next();
8057
+ while (!step.done) {
8058
+ const target = await readlink.call(fs, step.value).catch(() => void 0);
8059
+ step = walk.next(target);
8060
+ }
8061
+ return step.value;
8062
+ }
8063
+
8064
+ // src/kernel/host-ops.ts
8065
+ var HOST_OPS = [
8066
+ "lock",
8067
+ "unlock",
8068
+ "async-submit",
8069
+ "async-wait",
8070
+ "async-take",
8071
+ "async-resolve",
8072
+ "async-hold",
8073
+ "async-cancel",
8074
+ "async-close"
8075
+ ];
8076
+ var json3 = (value) => ({ ok: true, kind: "json", json: value });
8077
+ var done2 = { ok: true, kind: "void" };
8078
+ var LockTable = class {
8079
+ held = /* @__PURE__ */ new Map();
8080
+ lock(pid, path, exclusive, fd) {
8081
+ const holders = this.held.get(path) ?? /* @__PURE__ */ new Map();
8082
+ for (const [other, h] of holders) if (other !== pid && (exclusive || h.exclusive)) return false;
8083
+ const mine = holders.get(pid) ?? { exclusive, fds: /* @__PURE__ */ new Set() };
8084
+ mine.exclusive = exclusive;
8085
+ mine.fds.add(fd);
8086
+ holders.set(pid, mine);
8087
+ this.held.set(path, holders);
8088
+ return true;
8089
+ }
8090
+ unlock(pid, path) {
8091
+ const holders = this.held.get(path);
8092
+ holders?.delete(pid);
8093
+ if (holders?.size === 0) this.held.delete(path);
8094
+ }
8095
+ closed(pid, fd) {
8096
+ for (const [path, holders] of [...this.held]) {
8097
+ const mine = holders.get(pid);
8098
+ if (mine?.fds.delete(fd) && mine.fds.size === 0) this.unlock(pid, path);
8099
+ }
8100
+ }
8101
+ release(pid) {
8102
+ for (const path of [...this.held.keys()]) this.unlock(pid, path);
8103
+ }
8104
+ };
8105
+ var AsyncOps = class {
8106
+ next = 1;
8107
+ results = /* @__PURE__ */ new Map();
8108
+ ready = [];
8109
+ running = /* @__PURE__ */ new Map();
8110
+ waiters = /* @__PURE__ */ new Set();
8111
+ closed = false;
8112
+ run;
8113
+ onReady;
8114
+ constructor(run2, onReady) {
8115
+ this.run = run2;
8116
+ this.onReady = onReady;
8117
+ }
8118
+ submit(req) {
8119
+ const id2 = this.hold();
8120
+ const running = new AbortController();
8121
+ this.running.set(id2, running);
8122
+ this.run(req, running.signal).then(
8123
+ (result) => this.complete(id2, result),
8124
+ (err) => this.complete(id2, { ok: false, errno: "EIO", message: String(err) })
8125
+ );
8126
+ return id2;
8127
+ }
8128
+ resolve(value) {
8129
+ const id2 = this.hold();
8130
+ this.complete(id2, json3(value));
8131
+ return id2;
8132
+ }
8133
+ hold() {
8134
+ const id2 = this.next++;
8135
+ this.results.set(id2, void 0);
8136
+ return id2;
8137
+ }
8138
+ take(id2) {
8139
+ if (!this.results.has(id2)) throw new KernelError("EINVAL");
8140
+ const result = this.results.get(id2);
8141
+ if (!result) throw new KernelError("EAGAIN");
8142
+ this.forget(id2);
8143
+ return result;
8144
+ }
8145
+ cancel(id2) {
8146
+ this.running.get(id2)?.abort();
8147
+ this.forget(id2);
8148
+ }
8149
+ forget(id2) {
8150
+ this.running.delete(id2);
8151
+ this.results.delete(id2);
8152
+ const at = this.ready.indexOf(id2);
8153
+ if (at >= 0) this.ready.splice(at, 1);
8154
+ }
8155
+ async wait(timeoutMs, blocking) {
8156
+ let signal;
8157
+ for (; ; ) {
8158
+ if (this.closed) return -1;
8159
+ const id2 = this.ready.shift();
8160
+ if (id2 !== void 0) return id2;
8161
+ if (timeoutMs !== void 0 && timeoutMs <= 0) return 0;
8162
+ signal ??= blocking();
8163
+ const woke = await this.sleep(timeoutMs, signal);
8164
+ if (!woke) return 0;
8165
+ }
8166
+ }
8167
+ close() {
8168
+ this.closed = true;
8169
+ this.wake();
8170
+ }
8171
+ sleep(timeoutMs, signal) {
8172
+ return new Promise((resolve, reject) => {
8173
+ let timer;
8174
+ const cleanup = () => {
8175
+ clearTimeout(timer);
8176
+ this.waiters.delete(wake);
8177
+ signal.removeEventListener("abort", abort);
8178
+ };
8179
+ const wake = () => {
8180
+ cleanup();
8181
+ resolve(true);
8182
+ };
8183
+ const abort = () => {
8184
+ cleanup();
8185
+ reject(new KernelError("EINTR"));
8186
+ };
8187
+ this.waiters.add(wake);
8188
+ signal.addEventListener("abort", abort, { once: true });
8189
+ if (timeoutMs !== void 0) {
8190
+ timer = setTimeout(() => {
8191
+ cleanup();
8192
+ resolve(false);
8193
+ }, timeoutMs);
8194
+ }
8195
+ });
8196
+ }
8197
+ complete(id2, result) {
8198
+ this.running.delete(id2);
8199
+ if (!this.results.has(id2)) return;
8200
+ this.results.set(id2, result);
8201
+ this.ready.push(id2);
8202
+ this.wake();
8203
+ this.onReady?.();
8204
+ }
8205
+ wake() {
8206
+ for (const wake of [...this.waiters]) wake();
8207
+ }
8208
+ };
8209
+ async function hostSyscall(req, ctx) {
7606
8210
  switch (req.op) {
7607
8211
  case "lock":
7608
8212
  if (!ctx.locks) throw new KernelError("ENOSYS");
@@ -7616,7 +8220,7 @@ async function hostSyscall(req, ctx) {
7616
8220
  if (!ctx.valid(req.req)) throw new KernelError("EINVAL");
7617
8221
  return json3(ctx.ops.submit(req.req));
7618
8222
  case "async-wait":
7619
- return json3(await ctx.ops.wait(req.timeoutMs, ctx.blocking()));
8223
+ return json3(await ctx.ops.wait(req.timeoutMs, ctx.blocking));
7620
8224
  case "async-take":
7621
8225
  return ctx.ops.take(req.id);
7622
8226
  case "async-resolve":
@@ -8504,343 +9108,118 @@ async function selectFds(fds, read, write, timeoutMs, interrupt) {
8504
9108
  try {
8505
9109
  return await waitReady(fds, read, write, deadline, interrupted2.promise);
8506
9110
  } finally {
8507
- interrupted2.done();
8508
- }
8509
- }
8510
- async function waitReady(fds, read, write, deadline, interrupted2) {
8511
- for (; ; ) {
8512
- const now = ready(fds, read, write);
8513
- if (now.read.length > 0 || now.write.length > 0 || Date.now() >= deadline) return now;
8514
- const round = new AbortController();
8515
- const changes = [.../* @__PURE__ */ new Set([...read, ...write])].flatMap((fd) => {
8516
- const file = fds.get(fd).file;
8517
- return file.changed === void 0 ? [] : [file.changed(round.signal).catch(() => void 0)];
8518
- });
8519
- let timer;
8520
- const expiry = deadline === Number.POSITIVE_INFINITY ? [] : [new Promise((resolve) => timer = setTimeout(resolve, deadline - Date.now()))];
8521
- try {
8522
- await Promise.race([...changes, ...expiry, interrupted2]);
8523
- } finally {
8524
- round.abort();
8525
- clearTimeout(timer);
8526
- }
8527
- }
8528
- }
8529
-
8530
- // src/kernel/socket-syscalls.ts
8531
- var SOCKET_OPS = [
8532
- "sock-open",
8533
- "sock-pair",
8534
- "sock-bind",
8535
- "sock-listen",
8536
- "sock-accept",
8537
- "sock-connect",
8538
- "sock-shutdown",
8539
- "sock-name",
8540
- "sock-getopt",
8541
- "sock-setopt",
8542
- "sock-resolve"
8543
- ];
8544
- function socketAt(fds, fd) {
8545
- const file = fds.get(fd).file;
8546
- if (!(file instanceof KernelSocket)) throw new KernelError("ENOTSOCK");
8547
- return file;
8548
- }
8549
- var ok = (json5) => json5 === void 0 ? { ok: true, kind: "void" } : { ok: true, kind: "json", json: json5 };
8550
- async function socketSyscall(req, proc) {
8551
- const { fds, net } = proc;
8552
- switch (req.op) {
8553
- case "sock-open":
8554
- return ok(fds.install(new OpenFile(net.socket(req.domain)), 3));
8555
- case "sock-pair": {
8556
- const [a, b] = KernelSocket.pair(net, req.domain, proc.cred?.());
8557
- const first = fds.install(new OpenFile(a), 3);
8558
- try {
8559
- return ok([first, fds.install(new OpenFile(b), 3)]);
8560
- } catch (e) {
8561
- await Promise.resolve(fds.close(first));
8562
- throw e;
8563
- }
8564
- }
8565
- case "sock-bind":
8566
- socketAt(fds, req.fd).bind(req.addr);
8567
- return ok();
8568
- case "sock-listen":
8569
- socketAt(fds, req.fd).listen(req.backlog, proc.cred?.());
8570
- return ok();
8571
- case "sock-accept":
8572
- return ok(await accept(proc, req.fd, req.nonblock));
8573
- case "sock-connect": {
8574
- const dialing = socketAt(fds, req.fd).connect(req.addr, true, proc.cred?.());
8575
- if (req.nonblock) {
8576
- dialing?.catch(() => void 0);
8577
- throw new KernelError("EINPROGRESS");
8578
- }
8579
- if (dialing !== void 0) await interruptible(dialing, proc.blocking());
8580
- return ok();
8581
- }
8582
- case "sock-shutdown":
8583
- socketAt(fds, req.fd).shutdown(req.how);
8584
- return ok();
8585
- case "sock-name": {
8586
- const socket = socketAt(fds, req.fd);
8587
- const addr = req.peer ? socket.peer : socket.local;
8588
- if (req.peer && !addr) throw new KernelError("ENOTCONN");
8589
- return ok(addr ?? unnamed(socket.domain));
8590
- }
8591
- case "sock-getopt":
8592
- return ok(socketAt(fds, req.fd).getOption(req.level, req.name));
8593
- case "sock-setopt":
8594
- socketAt(fds, req.fd).setOption(req.level, req.name, req.value);
8595
- return ok();
8596
- case "sock-resolve": {
8597
- const addresses = await proc.resolve(req.name, req.family, () => proc.blocking());
8598
- if (addresses.length === 0) throw new KernelError("ENOENT");
8599
- return ok(addresses);
8600
- }
8601
- }
8602
- }
8603
- function interruptible(dialing, signal) {
8604
- return new Promise((resolve, reject) => {
8605
- const abort = () => reject(new KernelError("EINTR"));
8606
- signal.addEventListener("abort", abort, { once: true });
8607
- dialing.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
8608
- });
8609
- }
8610
- function unnamed(domain) {
8611
- return domain === "inet" ? { family: "inet", host: "0.0.0.0", port: 0 } : { family: "unix", path: "" };
8612
- }
8613
- async function accept(proc, fd, nonblock) {
8614
- const listener = socketAt(proc.fds, fd);
8615
- const waits = !nonblock && !pollFile(listener).readable;
8616
- const socket = await listener.accept(waits ? proc.blocking() : void 0, nonblock);
8617
- return { fd: proc.fds.install(new OpenFile(socket), 3), peer: socket.peer };
8618
- }
8619
-
8620
- // src/fs/pages.ts
8621
- var PAGE = 64 * 1024;
8622
- var READAHEAD = 1024 * 1024;
8623
- var CLEAN_LIMIT = 4 * 1024 * 1024;
8624
- var WRITEBACK = 8 * 1024 * 1024;
8625
- var RUN = 1024 * 1024;
8626
- var Pages = class {
8627
- length;
8628
- stored;
8629
- shrink;
8630
- pages = /* @__PURE__ */ new Map();
8631
- dirty = /* @__PURE__ */ new Set();
8632
- free = [];
8633
- pinned = false;
8634
- constructor(size) {
8635
- this.length = size;
8636
- this.stored = size;
8637
- }
8638
- get dirtyBytes() {
8639
- return this.dirty.size * PAGE;
8640
- }
8641
- get changed() {
8642
- return this.dirty.size > 0 || this.shrink !== void 0 || this.length !== this.stored;
8643
- }
8644
- missing(at, length2) {
8645
- this.evict();
8646
- const end = Math.min(this.stored, at + length2, this.length);
8647
- const runs = [];
8648
- for (let page = Math.floor(at / PAGE); page * PAGE < end; page++) {
8649
- if (this.pages.has(page)) continue;
8650
- const last2 = runs[runs.length - 1];
8651
- if (last2 && last2[0] + last2[1] === page * PAGE) last2[1] += PAGE;
8652
- else runs.push([page * PAGE, PAGE]);
8653
- }
8654
- const last = runs[runs.length - 1];
8655
- if (last) last[1] = Math.max(last[1], Math.min(READAHEAD, this.stored - last[0]));
8656
- return runs.map(([start2, n]) => [start2, Math.min(n, this.stored - start2)]);
8657
- }
8658
- partial(at, length2) {
8659
- if (length2 === 0) return [];
8660
- this.evict();
8661
- const edges = /* @__PURE__ */ new Set([Math.floor(at / PAGE), Math.floor((at + length2 - 1) / PAGE)]);
8662
- const runs = [];
8663
- for (const page of edges) {
8664
- const start2 = page * PAGE;
8665
- const covered = at <= start2 && at + length2 >= start2 + PAGE;
8666
- if (covered || this.pages.has(page) || start2 >= this.stored) continue;
8667
- runs.push([start2, Math.min(PAGE, this.stored - start2)]);
8668
- }
8669
- return runs;
8670
- }
8671
- fill(start2, bytes) {
8672
- for (let at = 0; at < bytes.length; at += PAGE) {
8673
- const page = (start2 + at) / PAGE;
8674
- if (this.pages.has(page)) continue;
8675
- const part = bytes.subarray(at, Math.min(at + PAGE, bytes.length));
8676
- const fresh = this.take(part.length);
8677
- fresh.set(part);
8678
- this.pages.set(page, fresh);
8679
- }
8680
- }
8681
- read(at, length2) {
8682
- const out = new Uint8Array(Math.max(0, Math.min(this.length, at + length2) - at));
8683
- this.readInto(at, out);
8684
- return out;
8685
- }
8686
- readInto(at, out) {
8687
- const end = Math.min(this.length, at + out.length);
8688
- for (let pos = at; pos < end; ) {
8689
- const page = Math.floor(pos / PAGE);
8690
- const from = pos - page * PAGE;
8691
- const n = Math.min(PAGE - from, end - pos);
8692
- const bytes = this.pages.get(page);
8693
- if (bytes) out.set(bytes.subarray(from, from + n), pos - at);
8694
- else out.fill(0, pos - at, pos - at + n);
8695
- pos += n;
8696
- }
8697
- return Math.max(0, end - at);
8698
- }
8699
- write(at, bytes) {
8700
- for (let done4 = 0; done4 < bytes.length; ) {
8701
- const pos = at + done4;
8702
- const page = Math.floor(pos / PAGE);
8703
- const from = pos - page * PAGE;
8704
- const n = Math.min(PAGE - from, bytes.length - done4);
8705
- let target = this.pages.get(page);
8706
- if (!target) {
8707
- target = this.take(0);
8708
- this.pages.set(page, target);
8709
- }
8710
- target.set(bytes.subarray(done4, done4 + n), from);
8711
- this.dirty.add(page);
8712
- done4 += n;
8713
- }
8714
- this.length = Math.max(this.length, at + bytes.length);
9111
+ interrupted2.done();
8715
9112
  }
8716
- truncate(size) {
8717
- if (size < this.stored) {
8718
- this.stored = size;
8719
- this.shrink = Math.min(this.shrink ?? size, size);
8720
- }
8721
- if (size < this.length) {
8722
- const edge = Math.floor(size / PAGE);
8723
- for (const page of [...this.pages.keys()]) {
8724
- if (page > edge || page === edge && size === edge * PAGE) this.drop(page);
8725
- }
8726
- this.pages.get(edge)?.fill(0, size - edge * PAGE);
9113
+ }
9114
+ async function waitReady(fds, read, write, deadline, interrupted2) {
9115
+ for (; ; ) {
9116
+ const now = ready(fds, read, write);
9117
+ if (now.read.length > 0 || now.write.length > 0 || Date.now() >= deadline) return now;
9118
+ const round = new AbortController();
9119
+ const changes = [.../* @__PURE__ */ new Set([...read, ...write])].flatMap((fd) => {
9120
+ const file = fds.get(fd).file;
9121
+ return file.changed === void 0 ? [] : [file.changed(round.signal).catch(() => void 0)];
9122
+ });
9123
+ let timer;
9124
+ const expiry = deadline === Number.POSITIVE_INFINITY ? [] : [new Promise((resolve) => timer = setTimeout(resolve, deadline - Date.now()))];
9125
+ try {
9126
+ await Promise.race([...changes, ...expiry, interrupted2]);
9127
+ } finally {
9128
+ round.abort();
9129
+ clearTimeout(timer);
8727
9130
  }
8728
- this.length = size;
8729
9131
  }
8730
- plan() {
8731
- const runs = [];
8732
- const sorted = [...this.dirty].sort((a, b) => a - b);
8733
- for (let i = 0; i < sorted.length; ) {
8734
- let j = i + 1;
8735
- const first = sorted[i];
8736
- while (j < sorted.length && sorted[j] === first + (j - i) && sorted[j] * PAGE % RUN !== 0) {
8737
- j++;
9132
+ }
9133
+
9134
+ // src/kernel/socket-syscalls.ts
9135
+ var SOCKET_OPS = [
9136
+ "sock-open",
9137
+ "sock-pair",
9138
+ "sock-bind",
9139
+ "sock-listen",
9140
+ "sock-accept",
9141
+ "sock-connect",
9142
+ "sock-shutdown",
9143
+ "sock-name",
9144
+ "sock-getopt",
9145
+ "sock-setopt",
9146
+ "sock-resolve"
9147
+ ];
9148
+ function socketAt(fds, fd) {
9149
+ const file = fds.get(fd).file;
9150
+ if (!(file instanceof KernelSocket)) throw new KernelError("ENOTSOCK");
9151
+ return file;
9152
+ }
9153
+ var ok = (json5) => json5 === void 0 ? { ok: true, kind: "void" } : { ok: true, kind: "json", json: json5 };
9154
+ async function socketSyscall(req, proc) {
9155
+ const { fds, net } = proc;
9156
+ switch (req.op) {
9157
+ case "sock-open":
9158
+ return ok(fds.install(new OpenFile(net.socket(req.domain)), 3));
9159
+ case "sock-pair": {
9160
+ const [a, b] = KernelSocket.pair(net, req.domain, proc.cred?.());
9161
+ const first = fds.install(new OpenFile(a), 3);
9162
+ try {
9163
+ return ok([first, fds.install(new OpenFile(b), 3)]);
9164
+ } catch (e) {
9165
+ await Promise.resolve(fds.close(first));
9166
+ throw e;
8738
9167
  }
8739
- const start2 = first * PAGE;
8740
- const end = Math.min(this.length, (first + (j - i)) * PAGE);
8741
- const bytes = new Uint8Array(end - start2);
8742
- for (let k = i; k < j; k++) {
8743
- const page = this.pages.get(sorted[k]);
8744
- bytes.set(page.subarray(0, Math.min(PAGE, end - (k - i) * PAGE - start2)), (k - i) * PAGE);
9168
+ }
9169
+ case "sock-bind":
9170
+ socketAt(fds, req.fd).bind(req.addr);
9171
+ return ok();
9172
+ case "sock-listen":
9173
+ socketAt(fds, req.fd).listen(req.backlog, proc.cred?.());
9174
+ return ok();
9175
+ case "sock-accept":
9176
+ return ok(await accept(proc, req.fd, req.nonblock));
9177
+ case "sock-connect": {
9178
+ const dialing = socketAt(fds, req.fd).connect(req.addr, true, proc.cred?.());
9179
+ if (req.nonblock) {
9180
+ dialing?.catch(() => void 0);
9181
+ throw new KernelError("EINPROGRESS");
8745
9182
  }
8746
- runs.push({ offset: start2, bytes });
8747
- i = j;
9183
+ if (dialing !== void 0) await interruptible(dialing, proc.blocking());
9184
+ return ok();
8748
9185
  }
8749
- const written2 = Math.max(this.stored, ...runs.map((r) => r.offset + r.bytes.length));
8750
- return {
8751
- ...this.shrink !== void 0 ? { shrink: this.shrink } : {},
8752
- runs,
8753
- ...this.length > written2 ? { extend: this.length } : {}
8754
- };
8755
- }
8756
- flushed() {
8757
- this.dirty.clear();
8758
- this.shrink = void 0;
8759
- this.stored = this.length;
8760
- this.evict();
8761
- }
8762
- pin() {
8763
- this.pinned = true;
8764
- }
8765
- reset(size) {
8766
- if (this.pinned) return;
8767
- for (const page of [...this.pages.keys()]) if (!this.dirty.has(page)) this.drop(page);
8768
- if (this.changed) return;
8769
- this.length = size;
8770
- this.stored = size;
8771
- }
8772
- evict() {
8773
- if (this.pinned) return;
8774
- let clean = (this.pages.size - this.dirty.size) * PAGE;
8775
- for (const page of this.pages.keys()) {
8776
- if (clean <= CLEAN_LIMIT) return;
8777
- if (this.dirty.has(page)) continue;
8778
- this.drop(page);
8779
- clean -= PAGE;
9186
+ case "sock-shutdown":
9187
+ socketAt(fds, req.fd).shutdown(req.how);
9188
+ return ok();
9189
+ case "sock-name": {
9190
+ const socket = socketAt(fds, req.fd);
9191
+ const addr = req.peer ? socket.peer : socket.local;
9192
+ if (req.peer && !addr) throw new KernelError("ENOTCONN");
9193
+ return ok(addr ?? unnamed(socket.domain));
8780
9194
  }
8781
- }
8782
- take(used) {
8783
- const page = this.free.pop();
8784
- if (!page) return new Uint8Array(PAGE);
8785
- page.fill(0, used);
8786
- return page;
8787
- }
8788
- drop(page) {
8789
- const bytes = this.pages.get(page);
8790
- this.pages.delete(page);
8791
- this.dirty.delete(page);
8792
- if (this.free.length < CLEAN_LIMIT / PAGE) this.free.push(bytes);
8793
- }
8794
- };
8795
-
8796
- // src/fs/ranged.ts
8797
- var AsyncRangedFile = class {
8798
- pages;
8799
- io;
8800
- pinned = false;
8801
- version;
8802
- path;
8803
- constructor(io, path, size, version) {
8804
- this.io = io;
8805
- this.path = path;
8806
- this.version = version;
8807
- this.pages = new Pages(size);
8808
- }
8809
- size() {
8810
- return this.pages.length;
8811
- }
8812
- async fetch(runs) {
8813
- for (const [start2, n] of runs) {
8814
- this.pages.fill(start2, await this.io.pread(this.path, start2, n, this.version));
9195
+ case "sock-getopt":
9196
+ return ok(socketAt(fds, req.fd).getOption(req.level, req.name));
9197
+ case "sock-setopt":
9198
+ socketAt(fds, req.fd).setOption(req.level, req.name, req.value);
9199
+ return ok();
9200
+ case "sock-resolve": {
9201
+ const addresses = await proc.resolve(req.name, req.family, () => proc.blocking());
9202
+ if (addresses.length === 0) throw new KernelError("ENOENT");
9203
+ return ok(addresses);
8815
9204
  }
8816
9205
  }
8817
- async read(at, length2) {
8818
- await this.fetch(this.pages.missing(at, length2));
8819
- return this.pages.read(at, length2);
8820
- }
8821
- async write(at, bytes) {
8822
- await this.fetch(this.pages.partial(at, bytes.length));
8823
- this.pages.write(at, bytes);
8824
- if (!this.pinned && this.pages.dirtyBytes >= WRITEBACK) await this.flush();
8825
- }
8826
- truncate(size) {
8827
- this.pages.truncate(size);
8828
- }
8829
- async flush() {
8830
- if (this.pinned || !this.pages.changed) return;
8831
- const plan = this.pages.plan();
8832
- if (plan.shrink !== void 0) await this.io.truncate(this.path, plan.shrink);
8833
- for (const run2 of plan.runs) await this.io.pwrite(this.path, run2.offset, run2.bytes, true);
8834
- if (plan.extend !== void 0) await this.io.truncate(this.path, plan.extend);
8835
- this.version = void 0;
8836
- this.pages.flushed();
8837
- }
8838
- async pin() {
8839
- this.pinned = true;
8840
- this.pages.pin();
8841
- await this.fetch(this.pages.missing(0, this.pages.length));
8842
- }
8843
- };
9206
+ }
9207
+ function interruptible(dialing, signal) {
9208
+ return new Promise((resolve, reject) => {
9209
+ const abort = () => reject(new KernelError("EINTR"));
9210
+ signal.addEventListener("abort", abort, { once: true });
9211
+ dialing.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
9212
+ });
9213
+ }
9214
+ function unnamed(domain) {
9215
+ return domain === "inet" ? { family: "inet", host: "0.0.0.0", port: 0 } : { family: "unix", path: "" };
9216
+ }
9217
+ async function accept(proc, fd, nonblock) {
9218
+ const listener = socketAt(proc.fds, fd);
9219
+ const waits = !nonblock && !pollFile(listener).readable;
9220
+ const socket = await listener.accept(waits ? proc.blocking() : void 0, nonblock);
9221
+ return { fd: proc.fds.install(new OpenFile(socket), 3), peer: socket.peer };
9222
+ }
8844
9223
 
8845
9224
  // src/kernel/vfs-file.ts
8846
9225
  var O_ACCMODE = 3;
@@ -9443,6 +9822,9 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
9443
9822
  "dl-closure",
9444
9823
  "dl-closure-free",
9445
9824
  "dl-reserve",
9825
+ "dl-load-reserve",
9826
+ "dl-lock",
9827
+ "dl-unlock",
9446
9828
  "fd-renumber",
9447
9829
  "fd-promote",
9448
9830
  "fd-open-tty",
@@ -9481,6 +9863,7 @@ function isWasmSyscall(req) {
9481
9863
  return typeof op === "string" && SYSCALL_OPS.has(op);
9482
9864
  }
9483
9865
  var MAX_READ = 1024 * 1024;
9866
+ var WRITE_ACCESS = 3;
9484
9867
  var KEYBOARD = /* @__PURE__ */ new Set([SIG.INT, SIG.QUIT, SIG.TSTP]);
9485
9868
  var DEFAULT_UMASK = 18;
9486
9869
  var DYING = { ok: false, errno: "EINTR", message: "the process is being killed" };
@@ -9501,7 +9884,7 @@ var WasmProcess = class {
9501
9884
  stopped = 0;
9502
9885
  stops = 0;
9503
9886
  owed = false;
9504
- waits = 0;
9887
+ waiting = /* @__PURE__ */ new Set();
9505
9888
  resumed = Promise.resolve();
9506
9889
  wake;
9507
9890
  stateListeners = [];
@@ -9557,7 +9940,8 @@ var WasmProcess = class {
9557
9940
  return "terminate";
9558
9941
  }
9559
9942
  this.options.onPending?.(sig);
9560
- if (this.options.jobs?.keyboard && KEYBOARD.has(sig)) this.owed = true;
9943
+ if (this.options.jobs?.keyboard && KEYBOARD.has(sig) && this.waiting.size === 0)
9944
+ this.owed = true;
9561
9945
  const blocked = this.interrupt;
9562
9946
  this.interrupt = new AbortController();
9563
9947
  blocked.abort();
@@ -9602,10 +9986,15 @@ var WasmProcess = class {
9602
9986
  await this.resumed;
9603
9987
  if (this.dying) return DYING;
9604
9988
  const stops = this.stops;
9605
- const waits = this.waits;
9606
- const result = await this.dispatch(req, cancelled);
9989
+ const scope = new AbortController();
9990
+ const forward = () => scope.abort();
9991
+ if (cancelled?.aborted) scope.abort();
9992
+ cancelled?.addEventListener("abort", forward, { once: true });
9993
+ const result = await this.dispatch(req, scope.signal).finally(() => {
9994
+ cancelled?.removeEventListener("abort", forward);
9995
+ this.waiting.delete(scope.signal);
9996
+ });
9607
9997
  if (this.dying) return DYING;
9608
- if (!result.ok && result.errno === "EINTR" && this.waits !== waits) this.owed = false;
9609
9998
  const restart = !result.ok && result.errno === "EINTR" && this.stops !== stops;
9610
9999
  if (restart && !this.options.hasPending?.()) continue;
9611
10000
  await this.resumed;
@@ -9658,12 +10047,31 @@ var WasmProcess = class {
9658
10047
  refuseIfPending(restart) {
9659
10048
  if (restart && this.options.hasPending?.()) throw new KernelError("EINTR");
9660
10049
  }
10050
+ async procWait(req, cancelled) {
10051
+ const flags = {
10052
+ untraced: req.untraced,
10053
+ continued: req.continued,
10054
+ inGroup: this.waitGroup(req.pid)
10055
+ };
10056
+ if (this.owed && !req.nohang) {
10057
+ const now = await this.children.wait(req.pid, true, this.interrupt.signal, flags);
10058
+ if (now[0] !== 0) return now;
10059
+ }
10060
+ const signal = req.nohang ? this.interrupt.signal : this.blockingSignal(cancelled);
10061
+ const waited = await this.children.wait(req.pid, req.nohang, signal, flags);
10062
+ return waited;
10063
+ }
9661
10064
  blockingSignal(cancelled, owing = true) {
9662
- if (owing) this.waits++;
9663
- if (this.options.hasPending?.() || cancelled?.aborted || owing && this.owed) {
10065
+ if (owing && this.owed) {
10066
+ this.owed = false;
9664
10067
  throw new KernelError("EINTR");
9665
10068
  }
9666
- return cancelled ? AbortSignal.any([this.interrupt.signal, cancelled]) : this.interrupt.signal;
10069
+ if (this.options.hasPending?.() || cancelled.aborted) throw new KernelError("EINTR");
10070
+ if (owing) this.waiting.add(cancelled);
10071
+ return this.cancelSignal(cancelled);
10072
+ }
10073
+ cancelSignal(cancelled) {
10074
+ return AbortSignal.any([this.interrupt.signal, cancelled]);
9667
10075
  }
9668
10076
  async read(req, cancelled) {
9669
10077
  const file = this.fds.get(req.fd).file;
@@ -9687,15 +10095,22 @@ var WasmProcess = class {
9687
10095
  if (nonblock) return file.write(body, AbortSignal.abort());
9688
10096
  return file.write(
9689
10097
  body,
9690
- this.options.hasPending?.() ? AbortSignal.abort() : this.interrupt.signal
10098
+ this.options.hasPending?.() ? AbortSignal.abort() : this.cancelSignal(cancelled)
9691
10099
  );
9692
10100
  }
9693
10101
  async openVfs(req) {
10102
+ if (inProc(req.path)) return this.openProc(req, req.path);
10103
+ const alias = req.orphan ? void 0 : await procAlias(this.options.fs, req.path, !req.exclusive);
10104
+ if (alias !== void 0) return this.openProc(req, alias);
9694
10105
  await refuseReadonly(this.options.fs, req.path, req.flags, req);
9695
10106
  const fd = this.fds.install(heldFile(), 3);
9696
10107
  try {
9697
10108
  const nodes = this.nodes;
9698
10109
  const path = req.orphan ? req.path : req.exclusive ? await nodes.entryKey(req.path) : req.create ? await nodes.targetKey(req.path) : await this.keptOr(await nodes.targetKey(req.path), req.handle);
10110
+ if (inProc(path)) {
10111
+ this.fds.installAt(fd, await this.procFile(req, path));
10112
+ return fd;
10113
+ }
9699
10114
  const open = { ...req, path };
9700
10115
  if (req.exclusive) {
9701
10116
  await nodes.createExclusive(path);
@@ -9717,6 +10132,22 @@ var WasmProcess = class {
9717
10132
  }
9718
10133
  return fd;
9719
10134
  }
10135
+ async openProc(req, path) {
10136
+ return this.fds.install(await this.procFile(req, path), 3);
10137
+ }
10138
+ async procFile(req, path) {
10139
+ if (req.flags & WRITE_ACCESS || req.create || req.truncate || req.exclusive) {
10140
+ throw new KernelError("EROFS");
10141
+ }
10142
+ const tree = new ProcTree(
10143
+ {
10144
+ procList: () => this.options.processes?.() ?? { boot: 0, processes: [] },
10145
+ mountList: () => this.options.mounts?.() ?? []
10146
+ },
10147
+ (await this.options.identity?.())?.pid ?? this.pid
10148
+ );
10149
+ return memoryFile(tree.read(path), path, req.position);
10150
+ }
9720
10151
  openVfsFile(req) {
9721
10152
  return vfsFile(
9722
10153
  this.options.fs,
@@ -9808,6 +10239,11 @@ var WasmProcess = class {
9808
10239
  return { ok: true, kind: "json", json: this.options.jobs?.terminalNames() ?? [] };
9809
10240
  case "fd-select": {
9810
10241
  const { read, write, timeoutMs } = req;
10242
+ if (this.owed && timeoutMs !== 0) {
10243
+ const ready2 = await selectFds(this.fds, read, write, 0, this.interrupt.signal);
10244
+ if (ready2.read.length + ready2.write.length > 0)
10245
+ return { ok: true, kind: "json", json: ready2 };
10246
+ }
9811
10247
  const signal = this.blockingSignal(cancelled, timeoutMs !== 0);
9812
10248
  const selected = await selectFds(this.fds, read, write, timeoutMs, signal);
9813
10249
  return { ok: true, kind: "json", json: selected };
@@ -10005,16 +10441,8 @@ var WasmProcess = class {
10005
10441
  case "proc-spawn":
10006
10442
  this.refuseIfPending(req.restart);
10007
10443
  return { ok: true, kind: "json", json: await this.spawn(req) };
10008
- case "proc-wait": {
10009
- const signal = req.nohang ? this.interrupt.signal : this.blockingSignal(cancelled);
10010
- const flags = {
10011
- untraced: req.untraced,
10012
- continued: req.continued,
10013
- inGroup: this.waitGroup(req.pid)
10014
- };
10015
- const waited = await this.children.wait(req.pid, req.nohang, signal, flags);
10016
- return { ok: true, kind: "json", json: waited };
10017
- }
10444
+ case "proc-wait":
10445
+ return { ok: true, kind: "json", json: await this.procWait(req, cancelled) };
10018
10446
  case "proc-identity":
10019
10447
  return { ok: true, kind: "json", json: await this.options.identity?.() ?? null };
10020
10448
  case "proc-exec": {
@@ -10046,7 +10474,7 @@ var WasmProcess = class {
10046
10474
  return { ok: true, kind: "json", json: this.options.mounts?.() ?? [] };
10047
10475
  case "mount":
10048
10476
  case "umount":
10049
- return this.mountSyscall(req);
10477
+ return this.mountSyscall(req, cancelled);
10050
10478
  case "proc-alarm":
10051
10479
  this.setAlarm(req.sig, req.firstMs ?? req.ms, req.repeat ? req.ms : 0, req.timer);
10052
10480
  return { ok: true, kind: "void" };
@@ -10067,6 +10495,18 @@ var WasmProcess = class {
10067
10495
  return { ok: true, kind: "void" };
10068
10496
  case "dl-reserve":
10069
10497
  return { ok: true, kind: "json", json: this.reserveSlots(req.floor, req.size) };
10498
+ case "dl-load-reserve":
10499
+ return {
10500
+ ok: true,
10501
+ kind: "json",
10502
+ json: { base: this.reserveSlots(req.floor, req.size), handle: this.closures.handle++ }
10503
+ };
10504
+ case "dl-lock":
10505
+ await this.lockDl(req.owner);
10506
+ return { ok: true, kind: "void" };
10507
+ case "dl-unlock":
10508
+ this.unlockDl(req.owner);
10509
+ return { ok: true, kind: "void" };
10070
10510
  case "sig-mask":
10071
10511
  this.caught = req.caught;
10072
10512
  this.ignored = req.ignored;
@@ -10081,14 +10521,14 @@ var WasmProcess = class {
10081
10521
  return { ok: true, kind: "bytes", bytes: this.children.captured(req.pid, req.slot) };
10082
10522
  }
10083
10523
  }
10084
- async mountSyscall(req) {
10524
+ async mountSyscall(req, cancelled) {
10085
10525
  const { mount, umount } = this.options;
10086
10526
  if (!mount || !umount) throw new KernelError("ENOSYS");
10087
10527
  try {
10088
10528
  if (req.op === "umount") await umount(req.target, req.flags);
10089
10529
  else {
10090
10530
  const { source, target, type, flags, data } = req;
10091
- const signal = this.blockingSignal();
10531
+ const signal = this.blockingSignal(cancelled);
10092
10532
  await Promise.race([mount({ source, target, type, flags, data }, signal), aborted(signal)]);
10093
10533
  }
10094
10534
  } catch (err) {
@@ -10103,7 +10543,12 @@ var WasmProcess = class {
10103
10543
  }
10104
10544
  alarm;
10105
10545
  dlLog = [];
10106
- closures = { next: 0, free: [], live: /* @__PURE__ */ new Set() };
10546
+ closures = {
10547
+ next: 0,
10548
+ handle: 1,
10549
+ free: [],
10550
+ live: /* @__PURE__ */ new Set()
10551
+ };
10107
10552
  allocateClosure(floor) {
10108
10553
  const index = this.closures.free.pop() ?? Math.max(floor, this.closures.next);
10109
10554
  this.closures.next = Math.max(this.closures.next, index + 1);
@@ -10116,6 +10561,47 @@ var WasmProcess = class {
10116
10561
  this.closures.free.push(index);
10117
10562
  this.dlLog.push({ kind: "closure-free", index });
10118
10563
  }
10564
+ dlLock = {
10565
+ owner: 0,
10566
+ depth: 0,
10567
+ waiters: /* @__PURE__ */ new Set()
10568
+ };
10569
+ async lockDl(owner) {
10570
+ while (this.dlLock.depth > 0 && this.dlLock.owner !== owner) {
10571
+ if (this.dying) throw new KernelError("EINTR");
10572
+ const interrupt = this.interrupt.signal;
10573
+ const gone = await new Promise((resolve) => {
10574
+ const waiter = {
10575
+ owner,
10576
+ wake: (ended) => {
10577
+ this.dlLock.waiters.delete(waiter);
10578
+ interrupt.removeEventListener("abort", recheck);
10579
+ resolve(ended);
10580
+ }
10581
+ };
10582
+ const recheck = () => waiter.wake(false);
10583
+ this.dlLock.waiters.add(waiter);
10584
+ interrupt.addEventListener("abort", recheck, { once: true });
10585
+ });
10586
+ if (gone) throw new KernelError("EINTR");
10587
+ }
10588
+ this.dlLock.owner = owner;
10589
+ this.dlLock.depth++;
10590
+ }
10591
+ threadEnded(tid) {
10592
+ for (const waiter of [...this.dlLock.waiters]) if (waiter.owner === tid) waiter.wake(true);
10593
+ if (this.dlLock.depth === 0 || this.dlLock.owner !== tid) return;
10594
+ this.dlLock.depth = 0;
10595
+ this.wakeDl();
10596
+ }
10597
+ unlockDl(owner) {
10598
+ if (this.dlLock.depth === 0 || this.dlLock.owner !== owner) throw new KernelError("EPERM");
10599
+ this.dlLock.depth--;
10600
+ if (this.dlLock.depth === 0) this.wakeDl();
10601
+ }
10602
+ wakeDl() {
10603
+ for (const waiter of [...this.dlLock.waiters]) waiter.wake(false);
10604
+ }
10119
10605
  reserveSlots(floor, size) {
10120
10606
  const base = Math.max(floor, this.closures.next);
10121
10607
  this.closures.next = base + size;
@@ -10427,6 +10913,7 @@ function spawnWasmProcess(opts) {
10427
10913
  const endThread = (tid) => {
10428
10914
  const end = threads.get(tid);
10429
10915
  threads.delete(tid);
10916
+ process.threadEnded(tid);
10430
10917
  end?.();
10431
10918
  };
10432
10919
  const spawnThread = (thread) => {
@@ -11219,8 +11706,8 @@ function umountCall(target, flags) {
11219
11706
  var FSTAB_PATH = "/etc/fstab";
11220
11707
  var FSTAB_RETRIES = [1e3, 4e3, 16e3];
11221
11708
  var FINAL = /* @__PURE__ */ new Set(["EINVAL", "EBUSY", "EPERM", "EACCES"]);
11222
- function unescape(field2) {
11223
- return field2.replace(
11709
+ function unescape(field3) {
11710
+ return field3.replace(
11224
11711
  /\\([0-7]{3})/g,
11225
11712
  (_, octal) => String.fromCharCode(Number.parseInt(octal, 8))
11226
11713
  );
@@ -12040,7 +12527,7 @@ function importedMemory(bytes) {
12040
12527
  const count = u32();
12041
12528
  for (let i = 0; i < count; i++) {
12042
12529
  const module = name2();
12043
- const field2 = name2();
12530
+ const field3 = name2();
12044
12531
  const kind = bytes[at++];
12045
12532
  if (kind === 0) u32();
12046
12533
  else if (kind === 1) {
@@ -12048,7 +12535,7 @@ function importedMemory(bytes) {
12048
12535
  limits();
12049
12536
  } else if (kind === 2) {
12050
12537
  const l = limits();
12051
- return { module, name: field2, ...l };
12538
+ return { module, name: field3, ...l };
12052
12539
  } else if (kind === 3) at += 2;
12053
12540
  else if (kind === 4) {
12054
12541
  at++;
@@ -12139,12 +12626,12 @@ function readImports(r, types) {
12139
12626
  const foreign = {};
12140
12627
  for (let i = r.u32(); i > 0; i--) {
12141
12628
  const module = r.name();
12142
- const field2 = r.name();
12629
+ const field3 = r.name();
12143
12630
  const kind = r.byte();
12144
12631
  if (kind !== 0) skipImport(r, kind);
12145
12632
  else {
12146
12633
  const type = types[r.u32()] ?? "other";
12147
- if (!RESERVED_NAMESPACES.has(module)) (foreign[module] ??= {})[field2] = type;
12634
+ if (!RESERVED_NAMESPACES.has(module)) (foreign[module] ??= {})[field3] = type;
12148
12635
  }
12149
12636
  }
12150
12637
  return foreign;