@ai-ecoverse/slicc-kernel 0.0.0 → 1.1.0
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.
- package/LICENSE +191 -0
- package/README.md +115 -5
- package/dist/index.js +108 -0
- package/dist/index.js.map +7 -0
- package/dist/kernel-worker.js +3076 -0
- package/dist/kernel-worker.js.map +7 -0
- package/dist/process-worker.js +2179 -0
- package/dist/process-worker.js.map +7 -0
- package/package.json +51 -1
|
@@ -0,0 +1,2179 @@
|
|
|
1
|
+
// src/kernel/protocol.ts
|
|
2
|
+
var WASM_PROCESS_INIT = "wasm-process-init";
|
|
3
|
+
var WASM_PROCESS_EXIT = "wasm-process-exit";
|
|
4
|
+
var WASM_PROCESS_ERROR = "wasm-process-error";
|
|
5
|
+
|
|
6
|
+
// src/kernel/signals.ts
|
|
7
|
+
var SIG = {
|
|
8
|
+
HUP: 1,
|
|
9
|
+
INT: 2,
|
|
10
|
+
QUIT: 3,
|
|
11
|
+
ILL: 4,
|
|
12
|
+
TRAP: 5,
|
|
13
|
+
ABRT: 6,
|
|
14
|
+
BUS: 7,
|
|
15
|
+
FPE: 8,
|
|
16
|
+
KILL: 9,
|
|
17
|
+
USR1: 10,
|
|
18
|
+
SEGV: 11,
|
|
19
|
+
USR2: 12,
|
|
20
|
+
PIPE: 13,
|
|
21
|
+
ALRM: 14,
|
|
22
|
+
TERM: 15,
|
|
23
|
+
STKFLT: 16,
|
|
24
|
+
CHLD: 17,
|
|
25
|
+
CONT: 18,
|
|
26
|
+
STOP: 19,
|
|
27
|
+
TSTP: 20,
|
|
28
|
+
TTIN: 21,
|
|
29
|
+
TTOU: 22,
|
|
30
|
+
URG: 23,
|
|
31
|
+
XCPU: 24,
|
|
32
|
+
XFSZ: 25,
|
|
33
|
+
VTALRM: 26,
|
|
34
|
+
PROF: 27,
|
|
35
|
+
WINCH: 28,
|
|
36
|
+
POLL: 29,
|
|
37
|
+
PWR: 30,
|
|
38
|
+
SYS: 31
|
|
39
|
+
};
|
|
40
|
+
var IGNORED_BY_DEFAULT = /* @__PURE__ */ new Set([SIG.CHLD, SIG.URG, SIG.WINCH]);
|
|
41
|
+
var STOPPING = /* @__PURE__ */ new Set([SIG.STOP, SIG.TSTP, SIG.TTIN, SIG.TTOU]);
|
|
42
|
+
function sigbit(sig) {
|
|
43
|
+
return 1 << sig;
|
|
44
|
+
}
|
|
45
|
+
function signalsIn(mask) {
|
|
46
|
+
const out = [];
|
|
47
|
+
for (let sig = 1; sig <= 31; sig++) if (mask & sigbit(sig)) out.push(sig);
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
var SIGNAL_BY_NAME = {
|
|
51
|
+
SIGINT: SIG.INT,
|
|
52
|
+
SIGTERM: SIG.TERM,
|
|
53
|
+
SIGKILL: SIG.KILL,
|
|
54
|
+
SIGSTOP: SIG.STOP,
|
|
55
|
+
SIGCONT: SIG.CONT
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// src/realm/live-vfs-fs.ts
|
|
59
|
+
var ERRNO_BY_CODE = {
|
|
60
|
+
EACCES: 2,
|
|
61
|
+
EBADF: 8,
|
|
62
|
+
EBUSY: 10,
|
|
63
|
+
EEXIST: 20,
|
|
64
|
+
EINVAL: 28,
|
|
65
|
+
EIO: 29,
|
|
66
|
+
EISDIR: 31,
|
|
67
|
+
ELOOP: 32,
|
|
68
|
+
ENAMETOOLONG: 37,
|
|
69
|
+
ENOENT: 44,
|
|
70
|
+
ENOSPC: 51,
|
|
71
|
+
ENOSYS: 52,
|
|
72
|
+
ENOTDIR: 54,
|
|
73
|
+
ENOTEMPTY: 55,
|
|
74
|
+
EPERM: 63,
|
|
75
|
+
EROFS: 69,
|
|
76
|
+
ETIMEDOUT: 73,
|
|
77
|
+
EXDEV: 75
|
|
78
|
+
};
|
|
79
|
+
var EIO = 29;
|
|
80
|
+
var S_IFDIR = 16384;
|
|
81
|
+
var S_IFREG = 32768;
|
|
82
|
+
var S_IFLNK = 40960;
|
|
83
|
+
var PERM_MASK = 4095;
|
|
84
|
+
var DEFAULT_DIR_PERM = 493;
|
|
85
|
+
var UMASK = 18;
|
|
86
|
+
var SEEK_CUR = 1;
|
|
87
|
+
var SEEK_END = 2;
|
|
88
|
+
function toErrno(Fs, err) {
|
|
89
|
+
if (err?.errno !== void 0 && err instanceof Fs.ErrnoError) {
|
|
90
|
+
return err;
|
|
91
|
+
}
|
|
92
|
+
const code = err?.code;
|
|
93
|
+
const errno = typeof code === "string" ? ERRNO_BY_CODE[code] ?? EIO : EIO;
|
|
94
|
+
return new Fs.ErrnoError(errno);
|
|
95
|
+
}
|
|
96
|
+
function liveNodePath(node) {
|
|
97
|
+
const parts = [];
|
|
98
|
+
let cur = node;
|
|
99
|
+
while (cur !== cur.mount.root) {
|
|
100
|
+
parts.push(cur.name);
|
|
101
|
+
cur = cur.parent;
|
|
102
|
+
}
|
|
103
|
+
const root = node.mount.opts.root.replace(/\/+$/, "");
|
|
104
|
+
return parts.length === 0 ? root || "/" : `${root}/${parts.reverse().join("/")}`;
|
|
105
|
+
}
|
|
106
|
+
function inodeOf(path) {
|
|
107
|
+
let h1 = 3735928559;
|
|
108
|
+
let h2 = 1103547991;
|
|
109
|
+
for (let i = 0; i < path.length; i++) {
|
|
110
|
+
const c = path.charCodeAt(i);
|
|
111
|
+
h1 = Math.imul(h1 ^ c, 2654435761);
|
|
112
|
+
h2 = Math.imul(h2 ^ c, 1597334677);
|
|
113
|
+
}
|
|
114
|
+
h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507) ^ Math.imul(h2 ^ h2 >>> 13, 3266489909);
|
|
115
|
+
h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507) ^ Math.imul(h1 ^ h1 >>> 13, 3266489909);
|
|
116
|
+
return 4294967296 * (2097151 & h2) + (h1 >>> 0) || 1;
|
|
117
|
+
}
|
|
118
|
+
function modeFromStat(st) {
|
|
119
|
+
const type = st.isSymbolicLink ? S_IFLNK : st.isDirectory ? S_IFDIR : S_IFREG;
|
|
120
|
+
const fallback = st.isDirectory || st.isSymbolicLink ? 511 : 438;
|
|
121
|
+
return type | ((st.mode ?? fallback) & PERM_MASK || fallback);
|
|
122
|
+
}
|
|
123
|
+
function toMs(v) {
|
|
124
|
+
if (v === void 0 || v === null) return void 0;
|
|
125
|
+
return v instanceof Date ? v.getTime() : v;
|
|
126
|
+
}
|
|
127
|
+
function createHelpers(Fs, ops) {
|
|
128
|
+
const bridgeOf = (node) => node.mount.opts.bridge;
|
|
129
|
+
function call(fn) {
|
|
130
|
+
try {
|
|
131
|
+
return fn();
|
|
132
|
+
} catch (err) {
|
|
133
|
+
throw toErrno(Fs, err);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function metadataCall(fn) {
|
|
137
|
+
try {
|
|
138
|
+
call(fn);
|
|
139
|
+
} catch (err) {
|
|
140
|
+
if (err.errno !== ERRNO_BY_CODE.ENOSYS) throw err;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function freshState(stat) {
|
|
144
|
+
return { ...stat ? { stat } : {}, len: 0, loaded: false, dirty: false, openCount: 0 };
|
|
145
|
+
}
|
|
146
|
+
function makeNode(parent, name, st) {
|
|
147
|
+
const node = Fs.createNode(parent, name, modeFromStat(st), 0);
|
|
148
|
+
node.node_ops = ops.node;
|
|
149
|
+
node.stream_ops = ops.stream;
|
|
150
|
+
node.live = freshState(st);
|
|
151
|
+
return node;
|
|
152
|
+
}
|
|
153
|
+
function statOf(node) {
|
|
154
|
+
if (node.live.orphan) {
|
|
155
|
+
return { ...node.live.stat, size: node.live.len };
|
|
156
|
+
}
|
|
157
|
+
if (!node.live.stat) {
|
|
158
|
+
const st = call(() => bridgeOf(node).lstat(liveNodePath(node)));
|
|
159
|
+
node.live.stat = st;
|
|
160
|
+
node.mode = modeFromStat(st);
|
|
161
|
+
}
|
|
162
|
+
return node.live.stat;
|
|
163
|
+
}
|
|
164
|
+
function childPath(parent, name) {
|
|
165
|
+
const base = liveNodePath(parent);
|
|
166
|
+
return base === "/" ? `/${name}` : `${base}/${name}`;
|
|
167
|
+
}
|
|
168
|
+
function ensureLoaded(node) {
|
|
169
|
+
const s = node.live;
|
|
170
|
+
if (s.loaded) return;
|
|
171
|
+
const bytes = call(() => bridgeOf(node).readFile(liveNodePath(node)));
|
|
172
|
+
s.data = bytes;
|
|
173
|
+
s.len = bytes.length;
|
|
174
|
+
s.loaded = true;
|
|
175
|
+
}
|
|
176
|
+
function ensureCapacity(node, need) {
|
|
177
|
+
const s = node.live;
|
|
178
|
+
const cur = s.data ?? new Uint8Array(0);
|
|
179
|
+
if (cur.length >= need) return cur;
|
|
180
|
+
const grown = new Uint8Array(Math.max(need, cur.length * 2, 256));
|
|
181
|
+
grown.set(cur.subarray(0, s.len));
|
|
182
|
+
s.data = grown;
|
|
183
|
+
return grown;
|
|
184
|
+
}
|
|
185
|
+
function flushNode(node) {
|
|
186
|
+
const s = node.live;
|
|
187
|
+
if (!s.dirty || !s.data || s.orphan) return;
|
|
188
|
+
const bytes = s.data.slice(0, s.len);
|
|
189
|
+
call(() => bridgeOf(node).writeFile(liveNodePath(node), bytes));
|
|
190
|
+
s.dirty = false;
|
|
191
|
+
s.stat = void 0;
|
|
192
|
+
}
|
|
193
|
+
function truncate(node, size) {
|
|
194
|
+
const s = node.live;
|
|
195
|
+
if (s.openCount > 0) {
|
|
196
|
+
if (size > 0) ensureLoaded(node);
|
|
197
|
+
else s.loaded = true;
|
|
198
|
+
const buf = ensureCapacity(node, size);
|
|
199
|
+
if (size > s.len) buf.fill(0, s.len, size);
|
|
200
|
+
s.len = size;
|
|
201
|
+
s.dirty = true;
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const path = liveNodePath(node);
|
|
205
|
+
const bytes = new Uint8Array(size);
|
|
206
|
+
if (size > 0) {
|
|
207
|
+
const cur = call(() => bridgeOf(node).readFile(path));
|
|
208
|
+
bytes.set(cur.subarray(0, Math.min(size, cur.length)));
|
|
209
|
+
}
|
|
210
|
+
call(() => bridgeOf(node).writeFile(path, bytes));
|
|
211
|
+
s.stat = void 0;
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
Fs,
|
|
215
|
+
bridgeOf,
|
|
216
|
+
call,
|
|
217
|
+
metadataCall,
|
|
218
|
+
makeNode,
|
|
219
|
+
statOf,
|
|
220
|
+
childPath,
|
|
221
|
+
ensureLoaded,
|
|
222
|
+
ensureCapacity,
|
|
223
|
+
flushNode,
|
|
224
|
+
truncate
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function createNodeOps(h) {
|
|
228
|
+
const { Fs, bridgeOf, call, metadataCall, makeNode, statOf, childPath, flushNode, truncate } = h;
|
|
229
|
+
const { ensureLoaded } = h;
|
|
230
|
+
return {
|
|
231
|
+
getattr(node) {
|
|
232
|
+
const st = statOf(node);
|
|
233
|
+
const s = node.live;
|
|
234
|
+
const size = Fs.isDir(node.mode) ? 4096 : s.loaded ? s.len : st.size;
|
|
235
|
+
const mtime = new Date(st.mtimeMs ?? 0);
|
|
236
|
+
return {
|
|
237
|
+
dev: 1,
|
|
238
|
+
ino: st.ino ?? inodeOf(liveNodePath(node)),
|
|
239
|
+
mode: node.mode,
|
|
240
|
+
nlink: 1,
|
|
241
|
+
uid: 0,
|
|
242
|
+
gid: 0,
|
|
243
|
+
rdev: 0,
|
|
244
|
+
size,
|
|
245
|
+
atime: mtime,
|
|
246
|
+
mtime,
|
|
247
|
+
ctime: mtime,
|
|
248
|
+
blksize: 4096,
|
|
249
|
+
blocks: Math.ceil(size / 4096)
|
|
250
|
+
};
|
|
251
|
+
},
|
|
252
|
+
setattr(node, attr) {
|
|
253
|
+
if (node.live.orphan) {
|
|
254
|
+
if (attr.mode !== void 0 && attr.mode !== null) {
|
|
255
|
+
node.mode = node.mode & ~PERM_MASK | attr.mode & PERM_MASK;
|
|
256
|
+
}
|
|
257
|
+
if (attr.size !== void 0 && attr.size !== null) truncate(node, attr.size);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
const path = liveNodePath(node);
|
|
261
|
+
if (attr.mode !== void 0 && attr.mode !== null) {
|
|
262
|
+
const perm = attr.mode & PERM_MASK;
|
|
263
|
+
if (perm !== (node.mode & PERM_MASK)) {
|
|
264
|
+
metadataCall(() => bridgeOf(node).chmod(path, perm));
|
|
265
|
+
node.mode = node.mode & ~PERM_MASK | perm;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (attr.size !== void 0 && attr.size !== null && Fs.isFile(node.mode)) {
|
|
269
|
+
truncate(node, attr.size);
|
|
270
|
+
}
|
|
271
|
+
const mtime = toMs(attr.mtime) ?? attr.timestamp;
|
|
272
|
+
if (mtime !== void 0) {
|
|
273
|
+
const atime = toMs(attr.atime) ?? mtime;
|
|
274
|
+
flushNode(node);
|
|
275
|
+
metadataCall(() => bridgeOf(node).utimes(path, atime, mtime));
|
|
276
|
+
}
|
|
277
|
+
node.live.stat = void 0;
|
|
278
|
+
},
|
|
279
|
+
lookup(parent, name) {
|
|
280
|
+
const st = call(() => bridgeOf(parent).lstat(childPath(parent, name)));
|
|
281
|
+
return makeNode(parent, name, st);
|
|
282
|
+
},
|
|
283
|
+
mknod(parent, name, mode) {
|
|
284
|
+
const path = childPath(parent, name);
|
|
285
|
+
if (Fs.isDir(mode)) {
|
|
286
|
+
call(() => bridgeOf(parent).mkdir(path));
|
|
287
|
+
const perm = mode & PERM_MASK & ~UMASK;
|
|
288
|
+
if (perm !== DEFAULT_DIR_PERM) metadataCall(() => bridgeOf(parent).chmod(path, perm));
|
|
289
|
+
} else if (Fs.isFile(mode)) {
|
|
290
|
+
call(() => bridgeOf(parent).writeFile(path, new Uint8Array(0)));
|
|
291
|
+
} else {
|
|
292
|
+
throw new Fs.ErrnoError(ERRNO_BY_CODE.EPERM);
|
|
293
|
+
}
|
|
294
|
+
const node = makeNode(parent, name, {
|
|
295
|
+
isFile: Fs.isFile(mode),
|
|
296
|
+
isDirectory: Fs.isDir(mode),
|
|
297
|
+
isSymbolicLink: false,
|
|
298
|
+
size: 0,
|
|
299
|
+
mode,
|
|
300
|
+
mtimeMs: Date.now()
|
|
301
|
+
});
|
|
302
|
+
node.live.stat = void 0;
|
|
303
|
+
return node;
|
|
304
|
+
},
|
|
305
|
+
rename(oldNode, newDir, newName) {
|
|
306
|
+
const from = liveNodePath(oldNode);
|
|
307
|
+
const to = childPath(newDir, newName);
|
|
308
|
+
flushNode(oldNode);
|
|
309
|
+
call(() => bridgeOf(oldNode).rename(from, to));
|
|
310
|
+
try {
|
|
311
|
+
const existing = Fs.lookupNode?.(newDir, newName);
|
|
312
|
+
if (existing && existing !== oldNode) Fs.hashRemoveNode?.(existing);
|
|
313
|
+
} catch {
|
|
314
|
+
}
|
|
315
|
+
oldNode.name = newName;
|
|
316
|
+
oldNode.parent = newDir;
|
|
317
|
+
oldNode.live.stat = void 0;
|
|
318
|
+
},
|
|
319
|
+
unlink(parent, name) {
|
|
320
|
+
let open;
|
|
321
|
+
try {
|
|
322
|
+
const node = Fs.lookupNode?.(parent, name);
|
|
323
|
+
if (node && node.live?.openCount > 0 && Fs.isFile(node.mode)) open = node;
|
|
324
|
+
} catch {
|
|
325
|
+
}
|
|
326
|
+
if (open) {
|
|
327
|
+
statOf(open);
|
|
328
|
+
ensureLoaded(open);
|
|
329
|
+
}
|
|
330
|
+
call(() => bridgeOf(parent).unlink(childPath(parent, name)));
|
|
331
|
+
if (open) open.live.orphan = true;
|
|
332
|
+
},
|
|
333
|
+
rmdir(parent, name) {
|
|
334
|
+
call(() => bridgeOf(parent).rmdir(childPath(parent, name)));
|
|
335
|
+
},
|
|
336
|
+
readdir(node) {
|
|
337
|
+
return [".", "..", ...call(() => bridgeOf(node).readdir(liveNodePath(node)))];
|
|
338
|
+
},
|
|
339
|
+
symlink(parent, newName, target) {
|
|
340
|
+
const path = childPath(parent, newName);
|
|
341
|
+
call(() => bridgeOf(parent).symlink(target, path));
|
|
342
|
+
return makeNode(parent, newName, {
|
|
343
|
+
isFile: false,
|
|
344
|
+
isDirectory: false,
|
|
345
|
+
isSymbolicLink: true,
|
|
346
|
+
size: target.length
|
|
347
|
+
});
|
|
348
|
+
},
|
|
349
|
+
readlink(node) {
|
|
350
|
+
if (!Fs.isLink(node.mode)) throw new Fs.ErrnoError(ERRNO_BY_CODE.EINVAL);
|
|
351
|
+
return call(() => bridgeOf(node).readlink(liveNodePath(node)));
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
function createStreamOps(h) {
|
|
356
|
+
const { Fs, statOf, ensureLoaded, ensureCapacity, flushNode } = h;
|
|
357
|
+
return {
|
|
358
|
+
open(stream) {
|
|
359
|
+
if (!Fs.isFile(stream.node.mode)) return;
|
|
360
|
+
stream.node.live.openCount++;
|
|
361
|
+
},
|
|
362
|
+
dup(stream) {
|
|
363
|
+
if (!Fs.isFile(stream.node.mode)) return;
|
|
364
|
+
stream.node.live.openCount++;
|
|
365
|
+
},
|
|
366
|
+
close(stream) {
|
|
367
|
+
const node = stream.node;
|
|
368
|
+
if (!Fs.isFile(node.mode)) return;
|
|
369
|
+
const s = node.live;
|
|
370
|
+
s.openCount = Math.max(0, s.openCount - 1);
|
|
371
|
+
if (s.openCount > 0) return;
|
|
372
|
+
try {
|
|
373
|
+
flushNode(node);
|
|
374
|
+
} finally {
|
|
375
|
+
s.data = void 0;
|
|
376
|
+
s.len = 0;
|
|
377
|
+
s.loaded = false;
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
read(stream, buffer, offset, length, position) {
|
|
381
|
+
const node = stream.node;
|
|
382
|
+
if (Fs.isDir(node.mode)) throw new Fs.ErrnoError(ERRNO_BY_CODE.EISDIR);
|
|
383
|
+
ensureLoaded(node);
|
|
384
|
+
const s = node.live;
|
|
385
|
+
if (position >= s.len || length <= 0) return 0;
|
|
386
|
+
const n = Math.min(length, s.len - position);
|
|
387
|
+
const out = new Uint8Array(buffer.buffer, buffer.byteOffset + offset, n);
|
|
388
|
+
out.set(s.data.subarray(position, position + n));
|
|
389
|
+
return n;
|
|
390
|
+
},
|
|
391
|
+
write(stream, buffer, offset, length, position) {
|
|
392
|
+
const node = stream.node;
|
|
393
|
+
if (Fs.isDir(node.mode)) throw new Fs.ErrnoError(ERRNO_BY_CODE.EISDIR);
|
|
394
|
+
if (length <= 0) return 0;
|
|
395
|
+
ensureLoaded(node);
|
|
396
|
+
const s = node.live;
|
|
397
|
+
const end = position + length;
|
|
398
|
+
const buf = ensureCapacity(node, end);
|
|
399
|
+
if (position > s.len) buf.fill(0, s.len, position);
|
|
400
|
+
buf.set(new Uint8Array(buffer.buffer, buffer.byteOffset + offset, length), position);
|
|
401
|
+
s.len = Math.max(s.len, end);
|
|
402
|
+
s.dirty = true;
|
|
403
|
+
return length;
|
|
404
|
+
},
|
|
405
|
+
llseek(stream, offset, whence) {
|
|
406
|
+
let pos = offset;
|
|
407
|
+
if (whence === SEEK_CUR) pos += stream.position;
|
|
408
|
+
else if (whence === SEEK_END && Fs.isFile(stream.node.mode)) {
|
|
409
|
+
const s = stream.node.live;
|
|
410
|
+
pos += s.loaded ? s.len : statOf(stream.node).size;
|
|
411
|
+
}
|
|
412
|
+
if (pos < 0) throw new Fs.ErrnoError(ERRNO_BY_CODE.EINVAL);
|
|
413
|
+
return pos;
|
|
414
|
+
},
|
|
415
|
+
fsync(stream) {
|
|
416
|
+
flushNode(stream.node);
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
function createLiveVfsPlugin(Fs) {
|
|
421
|
+
const mounts = /* @__PURE__ */ new Set();
|
|
422
|
+
const ops = {};
|
|
423
|
+
const h = createHelpers(Fs, ops);
|
|
424
|
+
const nodeOps = createNodeOps(h);
|
|
425
|
+
const streamOps = createStreamOps(h);
|
|
426
|
+
ops.node = nodeOps;
|
|
427
|
+
ops.stream = streamOps;
|
|
428
|
+
return {
|
|
429
|
+
mounts,
|
|
430
|
+
node_ops: nodeOps,
|
|
431
|
+
stream_ops: streamOps,
|
|
432
|
+
mount(mount) {
|
|
433
|
+
const st = h.call(() => mount.opts.bridge.stat(mount.opts.root));
|
|
434
|
+
if (!st.isDirectory) throw new Fs.ErrnoError(ERRNO_BY_CODE.ENOTDIR);
|
|
435
|
+
const root = h.makeNode(null, "/", st);
|
|
436
|
+
mounts.add(mount);
|
|
437
|
+
return root;
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
function ownedBy(plugin, node) {
|
|
442
|
+
return !!node && plugin.mounts.has(node.mount);
|
|
443
|
+
}
|
|
444
|
+
function flushLiveVfs(Fs, plugin) {
|
|
445
|
+
for (const head of Fs.nameTable ?? []) {
|
|
446
|
+
for (let node = head; node; node = node.name_next ?? null) {
|
|
447
|
+
if (!ownedBy(plugin, node) || !node.live.dirty || !node.live.data) continue;
|
|
448
|
+
try {
|
|
449
|
+
const bytes = node.live.data.slice(0, node.live.len);
|
|
450
|
+
node.mount.opts.bridge.writeFile(liveNodePath(node), bytes);
|
|
451
|
+
node.live.dirty = false;
|
|
452
|
+
} catch (err) {
|
|
453
|
+
throw toErrno(Fs, err);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
function invalidateLiveVfs(Fs, plugin) {
|
|
459
|
+
const table = Fs.nameTable ?? [];
|
|
460
|
+
const drop = [];
|
|
461
|
+
for (const head of table) {
|
|
462
|
+
for (let node = head; node; node = node.name_next ?? null) {
|
|
463
|
+
if (!ownedBy(plugin, node)) continue;
|
|
464
|
+
const s = node.live;
|
|
465
|
+
s.stat = void 0;
|
|
466
|
+
if (s.openCount === 0) {
|
|
467
|
+
if (node !== node.mount.root) drop.push(node);
|
|
468
|
+
} else if (s.loaded && !s.dirty) {
|
|
469
|
+
s.data = void 0;
|
|
470
|
+
s.len = 0;
|
|
471
|
+
s.loaded = false;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
if (!Fs.hashRemoveNode) return;
|
|
476
|
+
for (const node of drop) Fs.hashRemoveNode(node);
|
|
477
|
+
}
|
|
478
|
+
function outermostDirs(dirs) {
|
|
479
|
+
const norm = [...new Set(dirs.map((d) => d.replace(/\/+$/, "") || "/"))].sort();
|
|
480
|
+
return norm.filter((d) => !norm.some((o) => o !== d && (o === "/" || d.startsWith(`${o}/`))));
|
|
481
|
+
}
|
|
482
|
+
function describeMountError(err) {
|
|
483
|
+
if (err instanceof Error) return err.message;
|
|
484
|
+
const errno = err?.errno;
|
|
485
|
+
return typeof errno === "number" ? `errno ${errno}` : String(err);
|
|
486
|
+
}
|
|
487
|
+
function mountLiveVfsDirs(Fs, bridge, dirs, warn) {
|
|
488
|
+
if (typeof Fs.filesystems !== "object" || !Fs.filesystems) {
|
|
489
|
+
throw new Error(
|
|
490
|
+
"the module was linked without filesystem support, so the VFS cannot be mounted (link it with -sFORCE_FILESYSTEM=1)"
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
const plugin = Fs.filesystems.SLICC_LIVE_FS ?? createLiveVfsPlugin(Fs);
|
|
494
|
+
Fs.filesystems.SLICC_LIVE_FS = plugin;
|
|
495
|
+
const mounted = [];
|
|
496
|
+
for (const dir of outermostDirs(dirs)) {
|
|
497
|
+
if (dir === "/") continue;
|
|
498
|
+
try {
|
|
499
|
+
Fs.mkdirTree(dir);
|
|
500
|
+
Fs.mount(plugin, { root: dir, bridge }, dir);
|
|
501
|
+
mounted.push(dir);
|
|
502
|
+
} catch (err) {
|
|
503
|
+
warn(`live VFS mount of ${dir} failed: ${describeMountError(err)}`);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
return { plugin, mounted };
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// src/realm/emscripten-vfs-hook.ts
|
|
510
|
+
var MODULE_OWNED_DIRS = /* @__PURE__ */ new Set(["dev", "proc"]);
|
|
511
|
+
var SYNTHETIC_DIRS = ["/usr", "/bin"];
|
|
512
|
+
function topLevelDirs(bridge, warn) {
|
|
513
|
+
let names;
|
|
514
|
+
try {
|
|
515
|
+
names = bridge.readdir("/");
|
|
516
|
+
} catch (err) {
|
|
517
|
+
warn(`cannot list the VFS root, nothing mounted: ${String(err)}`);
|
|
518
|
+
return [];
|
|
519
|
+
}
|
|
520
|
+
const candidates = names.filter((name) => name && !name.includes("/") && !MODULE_OWNED_DIRS.has(name)).map((name) => `/${name}`);
|
|
521
|
+
for (const dir of SYNTHETIC_DIRS) if (!candidates.includes(dir)) candidates.push(dir);
|
|
522
|
+
const dirs = [];
|
|
523
|
+
for (const dir of candidates) {
|
|
524
|
+
try {
|
|
525
|
+
if (bridge.stat(dir).isDirectory) dirs.push(dir);
|
|
526
|
+
} catch {
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return dirs;
|
|
530
|
+
}
|
|
531
|
+
function mountVfsIntoEmscripten(Fs, deps) {
|
|
532
|
+
const { bridge, warn } = deps;
|
|
533
|
+
const { plugin, mounted } = mountLiveVfsDirs(Fs, bridge, topLevelDirs(bridge, warn), warn);
|
|
534
|
+
try {
|
|
535
|
+
Fs.chdir(deps.cwd);
|
|
536
|
+
} catch (err) {
|
|
537
|
+
warn(`cannot chdir to ${deps.cwd}: ${String(err)}`);
|
|
538
|
+
}
|
|
539
|
+
return {
|
|
540
|
+
mounted,
|
|
541
|
+
flush: () => flushLiveVfs(Fs, plugin),
|
|
542
|
+
invalidate: () => invalidateLiveVfs(Fs, plugin)
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// src/realm/sync-fs-wire.ts
|
|
547
|
+
var SYNC_FS_REQUEST_TIMEOUT_MS = 25e3;
|
|
548
|
+
function syncError(code, label) {
|
|
549
|
+
return Object.assign(new Error(`${code}: ${label}`), { code });
|
|
550
|
+
}
|
|
551
|
+
function parseSyncFsStat(json) {
|
|
552
|
+
const s = json;
|
|
553
|
+
if (!s || typeof s.isFile !== "boolean" || typeof s.isDirectory !== "boolean" || typeof s.size !== "number") {
|
|
554
|
+
return null;
|
|
555
|
+
}
|
|
556
|
+
return {
|
|
557
|
+
isFile: s.isFile,
|
|
558
|
+
isDirectory: s.isDirectory,
|
|
559
|
+
isSymbolicLink: s.isSymbolicLink,
|
|
560
|
+
size: s.size,
|
|
561
|
+
...typeof s.mode === "number" ? { mode: s.mode } : {},
|
|
562
|
+
...typeof s.mtimeMs === "number" ? { mtimeMs: s.mtimeMs } : {},
|
|
563
|
+
...typeof s.ino === "number" ? { ino: s.ino } : {}
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// src/realm/sync-sab-wire.ts
|
|
568
|
+
var SAB_HEADER_I32 = 16;
|
|
569
|
+
var SAB_HEADER_BYTES = SAB_HEADER_I32 * 4;
|
|
570
|
+
var SAB_I_STATE = 0;
|
|
571
|
+
var SAB_I_SEQ = 1;
|
|
572
|
+
var SAB_I_STATUS = 2;
|
|
573
|
+
var SAB_I_TOTAL = 3;
|
|
574
|
+
var SAB_I_CHUNK = 4;
|
|
575
|
+
var SAB_I_OFFSET = 5;
|
|
576
|
+
var SAB_I_SIGNALS = 8;
|
|
577
|
+
var SAB_I_TIMERS = 9;
|
|
578
|
+
var SAB_STATE_IDLE = 0;
|
|
579
|
+
var SAB_STATE_PENDING = 1;
|
|
580
|
+
var SAB_STATE_READY = 2;
|
|
581
|
+
var SAB_STATUS_BYTES = 1;
|
|
582
|
+
var SAB_STATUS_JSON = 2;
|
|
583
|
+
var SAB_STATUS_VOID = 3;
|
|
584
|
+
var SAB_STATUS_ERRNO = 4;
|
|
585
|
+
var SAB_DEFAULT_WINDOW_BYTES = 1024 * 1024;
|
|
586
|
+
var SAB_MIN_BYTES = SAB_HEADER_BYTES + 4096;
|
|
587
|
+
var SYNC_SAB_REQ_MSG = "sync-sab-req";
|
|
588
|
+
var SYNC_SAB_NEXT_MSG = "sync-sab-next";
|
|
589
|
+
var encoder = new TextEncoder();
|
|
590
|
+
var decoder = new TextDecoder();
|
|
591
|
+
function decodeSabResult(status2, payload) {
|
|
592
|
+
switch (status2) {
|
|
593
|
+
case SAB_STATUS_BYTES:
|
|
594
|
+
return { ok: true, kind: "bytes", bytes: payload };
|
|
595
|
+
case SAB_STATUS_VOID:
|
|
596
|
+
return { ok: true, kind: "void" };
|
|
597
|
+
case SAB_STATUS_JSON: {
|
|
598
|
+
try {
|
|
599
|
+
return { ok: true, kind: "json", json: JSON.parse(decoder.decode(payload)) };
|
|
600
|
+
} catch {
|
|
601
|
+
return { ok: false, errno: "EIO", message: "sync-sab: malformed json payload" };
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
case SAB_STATUS_ERRNO: {
|
|
605
|
+
try {
|
|
606
|
+
const parsed = JSON.parse(decoder.decode(payload));
|
|
607
|
+
const errno = typeof parsed.errno === "string" && /^E[A-Z]+$/.test(parsed.errno) ? parsed.errno : "EIO";
|
|
608
|
+
return { ok: false, errno, message: String(parsed.message ?? errno) };
|
|
609
|
+
} catch {
|
|
610
|
+
return { ok: false, errno: "EIO", message: "sync-sab: malformed errno payload" };
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
default:
|
|
614
|
+
return { ok: false, errno: "EIO", message: `sync-sab: unknown status ${status2}` };
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
function sabViews(sab) {
|
|
618
|
+
if (sab.byteLength < SAB_MIN_BYTES) {
|
|
619
|
+
throw new Error(`sync-sab: buffer too small (${sab.byteLength} < ${SAB_MIN_BYTES})`);
|
|
620
|
+
}
|
|
621
|
+
return {
|
|
622
|
+
header: new Int32Array(sab, 0, SAB_HEADER_I32),
|
|
623
|
+
window: new Uint8Array(sab, SAB_HEADER_BYTES)
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// src/realm/sync-sab-bridge.ts
|
|
628
|
+
function createSyncSabTransport(sab, port, deps = {}) {
|
|
629
|
+
const views = sabViews(sab);
|
|
630
|
+
const { header, window } = views;
|
|
631
|
+
const wait = deps.wait ?? ((a, i, v, t) => Atomics.wait(a, i, v, t));
|
|
632
|
+
const now = deps.now ?? (() => performance.now());
|
|
633
|
+
let seq = 0;
|
|
634
|
+
function awaitReady(id, deadline, label) {
|
|
635
|
+
for (; ; ) {
|
|
636
|
+
const remaining = deadline - now();
|
|
637
|
+
if (remaining <= 0) throw syncError("ETIMEDOUT", label);
|
|
638
|
+
wait(header, SAB_I_STATE, SAB_STATE_PENDING, remaining);
|
|
639
|
+
if (Atomics.load(header, SAB_I_STATE) === SAB_STATE_READY && Atomics.load(header, SAB_I_SEQ) === id) {
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
if (Atomics.load(header, SAB_I_STATE) === SAB_STATE_READY) {
|
|
643
|
+
Atomics.store(header, SAB_I_STATE, SAB_STATE_PENDING);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
return {
|
|
648
|
+
call(req, timeoutMs, label) {
|
|
649
|
+
const id = ++seq;
|
|
650
|
+
const deadline = now() + timeoutMs;
|
|
651
|
+
let out = null;
|
|
652
|
+
let status2 = 0;
|
|
653
|
+
let offset = 0;
|
|
654
|
+
try {
|
|
655
|
+
for (; ; ) {
|
|
656
|
+
Atomics.store(header, SAB_I_STATE, SAB_STATE_PENDING);
|
|
657
|
+
const msg = offset === 0 ? { type: SYNC_SAB_REQ_MSG, id, req } : { type: SYNC_SAB_NEXT_MSG, id, offset };
|
|
658
|
+
try {
|
|
659
|
+
port.postMessage(msg);
|
|
660
|
+
} catch {
|
|
661
|
+
throw syncError("EIO", label);
|
|
662
|
+
}
|
|
663
|
+
awaitReady(id, deadline, label);
|
|
664
|
+
const total = Atomics.load(header, SAB_I_TOTAL);
|
|
665
|
+
const chunk = Atomics.load(header, SAB_I_CHUNK);
|
|
666
|
+
const at = Atomics.load(header, SAB_I_OFFSET);
|
|
667
|
+
if (at !== offset || chunk < 0 || total < 0 || offset + chunk > total) {
|
|
668
|
+
throw syncError("EIO", label);
|
|
669
|
+
}
|
|
670
|
+
if (out === null) {
|
|
671
|
+
status2 = Atomics.load(header, SAB_I_STATUS);
|
|
672
|
+
out = new Uint8Array(total);
|
|
673
|
+
}
|
|
674
|
+
out.set(window.subarray(0, chunk), offset);
|
|
675
|
+
offset += chunk;
|
|
676
|
+
if (offset >= total) break;
|
|
677
|
+
if (chunk === 0) throw syncError("EIO", label);
|
|
678
|
+
}
|
|
679
|
+
} finally {
|
|
680
|
+
Atomics.store(header, SAB_I_STATE, SAB_STATE_IDLE);
|
|
681
|
+
}
|
|
682
|
+
return decodeSabResult(status2, out);
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
function errnoError(code, path) {
|
|
687
|
+
return syncError(code, `sync-sab bridge, '${path}'`);
|
|
688
|
+
}
|
|
689
|
+
function createSyncFsSabBridge(transport, opts = {}) {
|
|
690
|
+
const timeoutMs = opts.timeoutMs ?? SYNC_FS_REQUEST_TIMEOUT_MS;
|
|
691
|
+
function run(req, path) {
|
|
692
|
+
const result = transport.call(req, timeoutMs, `sync-sab bridge, '${path}'`);
|
|
693
|
+
if (!result.ok) throw errnoError(result.errno, path);
|
|
694
|
+
return result;
|
|
695
|
+
}
|
|
696
|
+
function bytes(req, path) {
|
|
697
|
+
const r = run(req, path);
|
|
698
|
+
if (r.ok && r.kind === "bytes") return r.bytes;
|
|
699
|
+
throw errnoError("EIO", path);
|
|
700
|
+
}
|
|
701
|
+
function json(req, path) {
|
|
702
|
+
const r = run(req, path);
|
|
703
|
+
if (r.ok && r.kind === "json") return r.json;
|
|
704
|
+
throw errnoError("EIO", path);
|
|
705
|
+
}
|
|
706
|
+
return {
|
|
707
|
+
readFile: (path) => bytes({ op: "read", path }, path),
|
|
708
|
+
writeFile: (path, data) => {
|
|
709
|
+
run({ op: "write", path, body: data }, path);
|
|
710
|
+
},
|
|
711
|
+
stat: (path) => {
|
|
712
|
+
const s = parseSyncFsStat(json({ op: "stat", path }, path));
|
|
713
|
+
if (!s) throw errnoError("EIO", path);
|
|
714
|
+
return s;
|
|
715
|
+
},
|
|
716
|
+
lstat: (path) => {
|
|
717
|
+
const s = parseSyncFsStat(json({ op: "lstat", path }, path));
|
|
718
|
+
if (!s) throw errnoError("EIO", path);
|
|
719
|
+
return s;
|
|
720
|
+
},
|
|
721
|
+
readdir: (path) => {
|
|
722
|
+
const list = json({ op: "readdir", path }, path);
|
|
723
|
+
if (!Array.isArray(list) || !list.every((s) => typeof s === "string")) {
|
|
724
|
+
throw errnoError("EIO", path);
|
|
725
|
+
}
|
|
726
|
+
return list;
|
|
727
|
+
},
|
|
728
|
+
readdirStat: (path) => {
|
|
729
|
+
const list = json({ op: "readdir-stat", path }, path);
|
|
730
|
+
if (!Array.isArray(list)) throw errnoError("EIO", path);
|
|
731
|
+
return list.map((entry) => {
|
|
732
|
+
if (!Array.isArray(entry) || typeof entry[0] !== "string") throw errnoError("EIO", path);
|
|
733
|
+
if (entry[1] === null) return [entry[0], null];
|
|
734
|
+
const stat = parseSyncFsStat(entry[1]);
|
|
735
|
+
if (!stat) throw errnoError("EIO", path);
|
|
736
|
+
return [entry[0], stat];
|
|
737
|
+
});
|
|
738
|
+
},
|
|
739
|
+
exists: (path) => {
|
|
740
|
+
const v = json({ op: "exists", path }, path);
|
|
741
|
+
if (typeof v !== "boolean") throw errnoError("EIO", path);
|
|
742
|
+
return v;
|
|
743
|
+
},
|
|
744
|
+
mkdir: (path) => {
|
|
745
|
+
run({ op: "mkdir", path }, path);
|
|
746
|
+
},
|
|
747
|
+
rm: (path) => {
|
|
748
|
+
run({ op: "rm", path }, path);
|
|
749
|
+
},
|
|
750
|
+
rename: (from, to) => {
|
|
751
|
+
run({ op: "rename", path: from, arg2: to }, from);
|
|
752
|
+
},
|
|
753
|
+
unlink: (path) => {
|
|
754
|
+
run({ op: "unlink", path }, path);
|
|
755
|
+
},
|
|
756
|
+
rmdir: (path) => {
|
|
757
|
+
run({ op: "rmdir", path }, path);
|
|
758
|
+
},
|
|
759
|
+
symlink: (target, linkPath) => {
|
|
760
|
+
run({ op: "symlink", path: linkPath, arg2: target }, linkPath);
|
|
761
|
+
},
|
|
762
|
+
readlink: (path) => {
|
|
763
|
+
const target = json({ op: "readlink", path }, path);
|
|
764
|
+
if (typeof target !== "string") throw errnoError("EIO", path);
|
|
765
|
+
return target;
|
|
766
|
+
},
|
|
767
|
+
chmod: (path, mode) => {
|
|
768
|
+
run({ op: "chmod", path, mode }, path);
|
|
769
|
+
},
|
|
770
|
+
utimes: (path, atimeMs, mtimeMs) => {
|
|
771
|
+
run({ op: "utimes", path, atimeMs, mtimeMs }, path);
|
|
772
|
+
}
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// src/process/wasi-errno.ts
|
|
777
|
+
var WASI_ERRNO = {
|
|
778
|
+
EACCES: 2,
|
|
779
|
+
EADDRINUSE: 3,
|
|
780
|
+
EADDRNOTAVAIL: 4,
|
|
781
|
+
EAFNOSUPPORT: 5,
|
|
782
|
+
EAGAIN: 6,
|
|
783
|
+
ECONNREFUSED: 14,
|
|
784
|
+
EINPROGRESS: 26,
|
|
785
|
+
EISCONN: 30,
|
|
786
|
+
ENETUNREACH: 40,
|
|
787
|
+
ENOTCONN: 53,
|
|
788
|
+
ENOTSOCK: 57,
|
|
789
|
+
EPROTONOSUPPORT: 66,
|
|
790
|
+
EOPNOTSUPP: 138,
|
|
791
|
+
EBADF: 8,
|
|
792
|
+
ECHILD: 12,
|
|
793
|
+
EFAULT: 21,
|
|
794
|
+
EINTR: 27,
|
|
795
|
+
EINVAL: 28,
|
|
796
|
+
EIO: 29,
|
|
797
|
+
EMFILE: 33,
|
|
798
|
+
ENOENT: 44,
|
|
799
|
+
ENOEXEC: 45,
|
|
800
|
+
ENOSYS: 52,
|
|
801
|
+
ENOTTY: 59,
|
|
802
|
+
ENXIO: 60,
|
|
803
|
+
EPERM: 63,
|
|
804
|
+
EPIPE: 64,
|
|
805
|
+
ESPIPE: 70,
|
|
806
|
+
ESRCH: 71
|
|
807
|
+
};
|
|
808
|
+
var EIO2 = 29;
|
|
809
|
+
function wasiErrno(code) {
|
|
810
|
+
return WASI_ERRNO[code] ?? EIO2;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// src/process/kernel-streams.ts
|
|
814
|
+
var POLLIN = 1;
|
|
815
|
+
var POLLOUT = 4;
|
|
816
|
+
var POLLERR = 8;
|
|
817
|
+
var POLLHUP = 16;
|
|
818
|
+
var POLLRDNORM = 64;
|
|
819
|
+
var POLLWRNORM = 256;
|
|
820
|
+
var O_NONBLOCK = 2048;
|
|
821
|
+
var PTY_PATH = /^\/dev\/(?:ptmx|pts\/(\d+))$/;
|
|
822
|
+
function isPtyPath(path) {
|
|
823
|
+
return PTY_PATH.test(path);
|
|
824
|
+
}
|
|
825
|
+
function ptyStat(path) {
|
|
826
|
+
const n = PTY_PATH.exec(path)?.[1];
|
|
827
|
+
const now = /* @__PURE__ */ new Date();
|
|
828
|
+
return {
|
|
829
|
+
dev: 22,
|
|
830
|
+
ino: n === void 0 ? 2 : 3 + Number(n),
|
|
831
|
+
mode: n === void 0 ? 8630 : 8592,
|
|
832
|
+
nlink: 1,
|
|
833
|
+
uid: 1e3,
|
|
834
|
+
gid: 1e3,
|
|
835
|
+
rdev: n === void 0 ? 5 << 8 | 2 : 136 << 8 | Number(n),
|
|
836
|
+
size: 0,
|
|
837
|
+
blksize: 4096,
|
|
838
|
+
blocks: 0,
|
|
839
|
+
atime: now,
|
|
840
|
+
mtime: now,
|
|
841
|
+
ctime: now
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
var O_CREAT = 64;
|
|
845
|
+
var O_EXCL = 128;
|
|
846
|
+
var O_NOCTTY = 256;
|
|
847
|
+
var O_TRUNC = 512;
|
|
848
|
+
var KILLED_BY_SIGPIPE = 128 + 13;
|
|
849
|
+
var ProcessExit = class extends Error {
|
|
850
|
+
status;
|
|
851
|
+
constructor(status2) {
|
|
852
|
+
super(`exit ${status2}`);
|
|
853
|
+
this.status = status2;
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
var SyscallError = class extends Error {
|
|
857
|
+
code;
|
|
858
|
+
constructor(code) {
|
|
859
|
+
super(code);
|
|
860
|
+
this.code = code;
|
|
861
|
+
}
|
|
862
|
+
};
|
|
863
|
+
function nonblocking(stream) {
|
|
864
|
+
return (stream.flags & O_NONBLOCK) !== 0 ? { nonblock: true } : void 0;
|
|
865
|
+
}
|
|
866
|
+
var KernelStreams = class {
|
|
867
|
+
refs = /* @__PURE__ */ new Map();
|
|
868
|
+
Fs;
|
|
869
|
+
sys;
|
|
870
|
+
options;
|
|
871
|
+
constructor(Fs, sys, options = {}) {
|
|
872
|
+
this.Fs = Fs;
|
|
873
|
+
this.sys = sys;
|
|
874
|
+
this.options = options;
|
|
875
|
+
}
|
|
876
|
+
attach(stream, kfd, terminal) {
|
|
877
|
+
this.refs.set(kfd, (this.refs.get(kfd) ?? 0) + 1);
|
|
878
|
+
stream.sliccKernelFd = kfd;
|
|
879
|
+
stream.stream_ops = this.ops(kfd, stream.stream_ops);
|
|
880
|
+
if (terminal ?? this.sys.isatty?.(kfd) ?? false) stream.tty = this.ttyOps(kfd);
|
|
881
|
+
else delete stream.tty;
|
|
882
|
+
}
|
|
883
|
+
nameTerminal(stream) {
|
|
884
|
+
const kfd = stream.sliccKernelFd;
|
|
885
|
+
if (!stream.tty || kfd === void 0) return;
|
|
886
|
+
const name = this.sys.ttyName?.(kfd);
|
|
887
|
+
if (name) this.nameStream(stream, name);
|
|
888
|
+
}
|
|
889
|
+
nameStream(stream, name) {
|
|
890
|
+
stream.path = name;
|
|
891
|
+
if (typeof this.Fs.stat !== "function") return;
|
|
892
|
+
stream.stream_ops = { ...stream.stream_ops, getattr: () => this.Fs.stat?.(name) ?? {} };
|
|
893
|
+
}
|
|
894
|
+
ttyOps(kfd) {
|
|
895
|
+
return {
|
|
896
|
+
ops: {
|
|
897
|
+
ioctl_tcgets: () => this.call(() => this.sys.tcgets?.(kfd)),
|
|
898
|
+
ioctl_tcsets: (_tty, _op, termios) => this.call(() => {
|
|
899
|
+
this.sys.tcsets?.(kfd, termios);
|
|
900
|
+
return 0;
|
|
901
|
+
}),
|
|
902
|
+
ioctl_tiocgwinsz: () => this.call(() => this.sys.winsize?.(kfd) ?? [24, 80]),
|
|
903
|
+
fsync: () => {
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
attachFile(stream, kfd) {
|
|
909
|
+
this.attach(stream, kfd, false);
|
|
910
|
+
stream.sliccKernelFile = true;
|
|
911
|
+
stream.stream_ops = {
|
|
912
|
+
...stream.stream_ops,
|
|
913
|
+
llseek: (_s, offset, whence) => this.call(() => this.sys.seek(kfd, offset, whence)),
|
|
914
|
+
fsync: () => this.call(() => {
|
|
915
|
+
this.sys.flush(kfd);
|
|
916
|
+
return 0;
|
|
917
|
+
})
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
useControllingTerminal() {
|
|
921
|
+
if (typeof this.Fs.open !== "function") return;
|
|
922
|
+
this.usePtyPaths();
|
|
923
|
+
const open = this.Fs.open.bind(this.Fs);
|
|
924
|
+
this.Fs.open = (path, flags, mode) => {
|
|
925
|
+
const pty = this.openPty(open, path, flags, mode);
|
|
926
|
+
if (pty) return pty;
|
|
927
|
+
const stream = open(path, flags, mode);
|
|
928
|
+
if (!stream.tty) return stream;
|
|
929
|
+
if (stream.path === "/dev/tty" && this.sys.openTty) {
|
|
930
|
+
let kfd;
|
|
931
|
+
try {
|
|
932
|
+
kfd = this.call(() => this.sys.openTty?.());
|
|
933
|
+
} catch (e) {
|
|
934
|
+
this.Fs.closeStream(stream.fd);
|
|
935
|
+
throw e;
|
|
936
|
+
}
|
|
937
|
+
this.attach(stream, kfd, true);
|
|
938
|
+
return stream;
|
|
939
|
+
}
|
|
940
|
+
let named;
|
|
941
|
+
try {
|
|
942
|
+
named = stream.path ? this.openNamedTerminal(stream.path) : void 0;
|
|
943
|
+
} catch (e) {
|
|
944
|
+
this.Fs.closeStream(stream.fd);
|
|
945
|
+
throw e;
|
|
946
|
+
}
|
|
947
|
+
if (named !== void 0) {
|
|
948
|
+
this.attach(stream, named, true);
|
|
949
|
+
return stream;
|
|
950
|
+
}
|
|
951
|
+
const terminal = this.stdioTerminal();
|
|
952
|
+
if (terminal !== void 0) this.attach(stream, terminal, true);
|
|
953
|
+
return stream;
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
openPty(open, path, flags, mode) {
|
|
957
|
+
const pts = /^\/dev\/pts\/(\d+)$/.exec(path);
|
|
958
|
+
if (path !== "/dev/ptmx" && !pts) return void 0;
|
|
959
|
+
const { openPty, openPts } = this.sys;
|
|
960
|
+
if (!openPty || !openPts) return void 0;
|
|
961
|
+
const kfd = this.call(
|
|
962
|
+
() => pts ? openPts(Number(pts[1]), (flags & O_NOCTTY) !== 0) : openPty()
|
|
963
|
+
);
|
|
964
|
+
let stream;
|
|
965
|
+
try {
|
|
966
|
+
stream = open("/dev/null", flags & ~(O_CREAT | O_EXCL | O_TRUNC), mode);
|
|
967
|
+
} catch (e) {
|
|
968
|
+
this.sys.close(kfd);
|
|
969
|
+
throw e;
|
|
970
|
+
}
|
|
971
|
+
this.attach(stream, kfd, true);
|
|
972
|
+
this.nameStream(stream, path);
|
|
973
|
+
return stream;
|
|
974
|
+
}
|
|
975
|
+
usePtyPaths() {
|
|
976
|
+
const fs = this.Fs;
|
|
977
|
+
for (const name of ["chown", "lchown", "chmod", "lchmod"]) {
|
|
978
|
+
const original = fs[name];
|
|
979
|
+
if (typeof original !== "function") continue;
|
|
980
|
+
fs[name] = (path, ...rest) => isPtyPath(path) ? this.existingPty(path) : original.call(fs, path, ...rest);
|
|
981
|
+
}
|
|
982
|
+
for (const name of ["stat", "lstat"]) {
|
|
983
|
+
const original = fs[name];
|
|
984
|
+
if (typeof original !== "function") continue;
|
|
985
|
+
fs[name] = (path, ...rest) => {
|
|
986
|
+
if (!isPtyPath(path)) return original.call(fs, path, ...rest);
|
|
987
|
+
this.existingPty(path);
|
|
988
|
+
return ptyStat(path);
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
existingPty(path) {
|
|
993
|
+
const n = PTY_PATH.exec(path)?.[1];
|
|
994
|
+
const exists = n === void 0 ? this.sys.openPty !== void 0 : this.sys.ptyNumbers?.().includes(Number(n)) ?? false;
|
|
995
|
+
if (!exists) throw new this.Fs.ErrnoError(wasiErrno("ENOENT"));
|
|
996
|
+
}
|
|
997
|
+
openNamedTerminal(path) {
|
|
998
|
+
const { openTty } = this.sys;
|
|
999
|
+
if (!openTty) return void 0;
|
|
1000
|
+
try {
|
|
1001
|
+
return openTty.call(this.sys, path);
|
|
1002
|
+
} catch (e) {
|
|
1003
|
+
if (e instanceof SyscallError && e.code === "ENXIO") return void 0;
|
|
1004
|
+
return this.call(() => {
|
|
1005
|
+
throw e;
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
stdioTerminal() {
|
|
1010
|
+
for (const fd of [0, 1, 2]) {
|
|
1011
|
+
const stream = this.Fs.getStream(fd);
|
|
1012
|
+
if (stream?.sliccKernelFd !== void 0 && stream.tty) return stream.sliccKernelFd;
|
|
1013
|
+
}
|
|
1014
|
+
return void 0;
|
|
1015
|
+
}
|
|
1016
|
+
usePipes(pipefs) {
|
|
1017
|
+
const createPipe = pipefs.createPipe.bind(pipefs);
|
|
1018
|
+
pipefs.createPipe = () => {
|
|
1019
|
+
const [read, write] = this.call(() => this.sys.pipe());
|
|
1020
|
+
let fds;
|
|
1021
|
+
try {
|
|
1022
|
+
fds = createPipe();
|
|
1023
|
+
} catch (e) {
|
|
1024
|
+
for (const kfd of [read, write]) {
|
|
1025
|
+
try {
|
|
1026
|
+
this.sys.close(kfd);
|
|
1027
|
+
} catch {
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
throw e;
|
|
1031
|
+
}
|
|
1032
|
+
this.attach(this.Fs.getStream(fds.readable_fd), read, false);
|
|
1033
|
+
this.attach(this.Fs.getStream(fds.writable_fd), write, false);
|
|
1034
|
+
return fds;
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
restarting(syscall) {
|
|
1038
|
+
for (; ; ) {
|
|
1039
|
+
try {
|
|
1040
|
+
return syscall();
|
|
1041
|
+
} catch (e) {
|
|
1042
|
+
if (!(e instanceof SyscallError && e.code === "EINTR" && this.options.restartable?.())) {
|
|
1043
|
+
throw e;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
call(syscall) {
|
|
1049
|
+
try {
|
|
1050
|
+
return syscall();
|
|
1051
|
+
} catch (e) {
|
|
1052
|
+
if (e instanceof SyscallError) throw new this.Fs.ErrnoError(wasiErrno(e.code));
|
|
1053
|
+
throw e;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
ops(kfd, base) {
|
|
1057
|
+
return {
|
|
1058
|
+
...base,
|
|
1059
|
+
llseek: () => this.call(() => {
|
|
1060
|
+
throw new SyscallError("ESPIPE");
|
|
1061
|
+
}),
|
|
1062
|
+
read: (s, buffer, offset, length) => this.call(() => {
|
|
1063
|
+
const bytes = this.restarting(() => this.sys.read(kfd, length, nonblocking(s)));
|
|
1064
|
+
buffer.set(bytes, offset);
|
|
1065
|
+
return bytes.length;
|
|
1066
|
+
}),
|
|
1067
|
+
write: (s, buffer, offset, length) => {
|
|
1068
|
+
try {
|
|
1069
|
+
const bytes = buffer.slice(offset, offset + length);
|
|
1070
|
+
return this.restarting(() => this.sys.write(kfd, bytes, nonblocking(s)));
|
|
1071
|
+
} catch (e) {
|
|
1072
|
+
if (e instanceof SyscallError && e.code === "EPIPE" && !this.options.sigpipe?.()) {
|
|
1073
|
+
throw new ProcessExit(KILLED_BY_SIGPIPE);
|
|
1074
|
+
}
|
|
1075
|
+
return this.call(() => {
|
|
1076
|
+
throw e;
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
},
|
|
1080
|
+
fsync: () => 0,
|
|
1081
|
+
poll: () => {
|
|
1082
|
+
const state = this.call(() => this.sys.poll(kfd));
|
|
1083
|
+
let mask = 0;
|
|
1084
|
+
if (state.readable) mask |= POLLIN | POLLRDNORM;
|
|
1085
|
+
if (state.writable) mask |= POLLOUT | POLLWRNORM;
|
|
1086
|
+
if (state.hangup) mask |= state.readable ? POLLHUP : POLLERR;
|
|
1087
|
+
return mask;
|
|
1088
|
+
},
|
|
1089
|
+
dup: (stream) => {
|
|
1090
|
+
base.dup?.(stream);
|
|
1091
|
+
this.refs.set(kfd, (this.refs.get(kfd) ?? 0) + 1);
|
|
1092
|
+
},
|
|
1093
|
+
close: (stream) => {
|
|
1094
|
+
try {
|
|
1095
|
+
base.close?.(stream);
|
|
1096
|
+
} catch {
|
|
1097
|
+
}
|
|
1098
|
+
const left = (this.refs.get(kfd) ?? 1) - 1;
|
|
1099
|
+
if (left > 0) {
|
|
1100
|
+
this.refs.set(kfd, left);
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
this.refs.delete(kfd);
|
|
1104
|
+
try {
|
|
1105
|
+
this.sys.close(kfd);
|
|
1106
|
+
} catch {
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
};
|
|
1112
|
+
|
|
1113
|
+
// src/process/process-children.ts
|
|
1114
|
+
var POLLIN2 = 1;
|
|
1115
|
+
var POLLOUT2 = 4;
|
|
1116
|
+
var POLLERR2 = 8;
|
|
1117
|
+
var POLLHUP2 = 16;
|
|
1118
|
+
var POLLNVAL = 32;
|
|
1119
|
+
var SELECT_SLICE_MS = 20;
|
|
1120
|
+
var WUNTRACED = 2;
|
|
1121
|
+
var WCONTINUED = 8;
|
|
1122
|
+
function status(r) {
|
|
1123
|
+
return r.ok ? 0 : -wasiErrno(r.errno);
|
|
1124
|
+
}
|
|
1125
|
+
function number(r) {
|
|
1126
|
+
if (!r.ok) return -wasiErrno(r.errno);
|
|
1127
|
+
return r.kind === "json" && typeof r.json === "number" ? r.json : -wasiErrno("EIO");
|
|
1128
|
+
}
|
|
1129
|
+
function drain(Fs, stream) {
|
|
1130
|
+
const chunks = [];
|
|
1131
|
+
const buffer = new Uint8Array(65536);
|
|
1132
|
+
try {
|
|
1133
|
+
for (let n = Fs.read(stream, buffer, 0, buffer.length); n > 0; ) {
|
|
1134
|
+
chunks.push(buffer.slice(0, n));
|
|
1135
|
+
n = Fs.read(stream, buffer, 0, buffer.length);
|
|
1136
|
+
}
|
|
1137
|
+
} catch {
|
|
1138
|
+
}
|
|
1139
|
+
const out = new Uint8Array(chunks.reduce((n, c) => n + c.length, 0));
|
|
1140
|
+
let offset = 0;
|
|
1141
|
+
for (const chunk of chunks) {
|
|
1142
|
+
out.set(chunk, offset);
|
|
1143
|
+
offset += chunk.length;
|
|
1144
|
+
}
|
|
1145
|
+
return out;
|
|
1146
|
+
}
|
|
1147
|
+
function kernelSelect(Fs, transport, read, write, timeoutMs) {
|
|
1148
|
+
const kernel = (fd) => Fs.getStream(fd)?.sliccKernelFd;
|
|
1149
|
+
const kr = read.map(kernel);
|
|
1150
|
+
const kw = write.map(kernel);
|
|
1151
|
+
const r = transport.call({ op: "fd-select", read: kr, write: kw, timeoutMs }, Infinity, "select");
|
|
1152
|
+
if (!r.ok) return -wasiErrno(r.errno);
|
|
1153
|
+
const got = r.kind === "json" ? r.json : { read: [], write: [] };
|
|
1154
|
+
return {
|
|
1155
|
+
read: read.filter((_, i) => got.read.includes(kr[i])),
|
|
1156
|
+
write: write.filter((_, i) => got.write.includes(kw[i]))
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
function ownReady(Fs, read, write) {
|
|
1160
|
+
const flags = (fd) => {
|
|
1161
|
+
const stream = Fs.getStream(fd);
|
|
1162
|
+
if (!stream) return POLLNVAL;
|
|
1163
|
+
return stream.stream_ops.poll ? stream.stream_ops.poll(stream) : POLLIN2 | POLLOUT2;
|
|
1164
|
+
};
|
|
1165
|
+
return {
|
|
1166
|
+
read: read.filter((fd) => flags(fd) & (POLLIN2 | POLLHUP2 | POLLERR2 | POLLNVAL)),
|
|
1167
|
+
write: write.filter((fd) => flags(fd) & (POLLOUT2 | POLLERR2 | POLLNVAL))
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
function createProcessKernel(deps) {
|
|
1171
|
+
const { transport, Fs } = deps;
|
|
1172
|
+
const reaped = /* @__PURE__ */ new Map();
|
|
1173
|
+
const call = (req, label) => transport.call(req, Infinity, label);
|
|
1174
|
+
const slot = (fd, n, promote) => {
|
|
1175
|
+
const stream = fd >= 0 ? Fs.getStream(fd) : null;
|
|
1176
|
+
if (!stream) return { none: true };
|
|
1177
|
+
promote?.(stream);
|
|
1178
|
+
if (stream.sliccKernelFd !== void 0) return { fd: stream.sliccKernelFd };
|
|
1179
|
+
if (stream.path === "/dev/null") return { none: true };
|
|
1180
|
+
return n === 0 ? { input: drain(Fs, stream) } : { capture: true };
|
|
1181
|
+
};
|
|
1182
|
+
const kernelWait = (pid, nohang, options = 0) => {
|
|
1183
|
+
const req = {
|
|
1184
|
+
op: "proc-wait",
|
|
1185
|
+
pid,
|
|
1186
|
+
nohang,
|
|
1187
|
+
...options & WUNTRACED ? { untraced: true } : {},
|
|
1188
|
+
...options & WCONTINUED ? { continued: true } : {}
|
|
1189
|
+
};
|
|
1190
|
+
let r = transport.call(req, Infinity, `proc-wait ${pid}`);
|
|
1191
|
+
while (!r.ok && r.errno === "EINTR" && deps.restartable?.()) {
|
|
1192
|
+
r = transport.call(req, Infinity, `proc-wait ${pid}`);
|
|
1193
|
+
}
|
|
1194
|
+
if (!r.ok) return -wasiErrno(r.errno);
|
|
1195
|
+
const waited = r.kind === "json" ? r.json : [0, 0];
|
|
1196
|
+
if (waited[0] > 0) deps.afterChild();
|
|
1197
|
+
return [waited[0], waited[1]];
|
|
1198
|
+
};
|
|
1199
|
+
const deliver = (pid, n, fd) => {
|
|
1200
|
+
const r = transport.call(
|
|
1201
|
+
{ op: "proc-captured", pid, slot: n },
|
|
1202
|
+
Infinity,
|
|
1203
|
+
`proc-captured ${pid}`
|
|
1204
|
+
);
|
|
1205
|
+
const stream = Fs.getStream(fd);
|
|
1206
|
+
if (r.ok && r.kind === "bytes" && r.bytes.length > 0 && stream) {
|
|
1207
|
+
Fs.write(stream, r.bytes, 0, r.bytes.length);
|
|
1208
|
+
}
|
|
1209
|
+
};
|
|
1210
|
+
return {
|
|
1211
|
+
spawn(file, argv, env, cwd, fds, actions) {
|
|
1212
|
+
deps.beforeSpawn();
|
|
1213
|
+
const promote = deps.stdioPromoter?.();
|
|
1214
|
+
const stdio = [0, 1, 2].map((n) => slot(fds[n] ?? -1, n, promote));
|
|
1215
|
+
const inherit = deps.inherit?.(actions) ?? [];
|
|
1216
|
+
const r = transport.call(
|
|
1217
|
+
{
|
|
1218
|
+
op: "proc-spawn",
|
|
1219
|
+
file,
|
|
1220
|
+
argv,
|
|
1221
|
+
env: env ?? deps.env,
|
|
1222
|
+
cwd: cwd ?? Fs.cwd(),
|
|
1223
|
+
stdio,
|
|
1224
|
+
...inherit.length > 0 ? { inherit } : {}
|
|
1225
|
+
},
|
|
1226
|
+
Infinity,
|
|
1227
|
+
`proc-spawn ${file}`
|
|
1228
|
+
);
|
|
1229
|
+
if (!r.ok) return -wasiErrno(r.errno);
|
|
1230
|
+
const pid = r.kind === "json" ? r.json : 0;
|
|
1231
|
+
const captures = [1, 2].filter((n) => "capture" in stdio[n]);
|
|
1232
|
+
if (captures.length === 0) return pid;
|
|
1233
|
+
const waited = kernelWait(pid, false);
|
|
1234
|
+
if (typeof waited === "number") return waited;
|
|
1235
|
+
for (const n of captures) deliver(pid, n, fds[n]);
|
|
1236
|
+
reaped.set(pid, waited[1]);
|
|
1237
|
+
return pid;
|
|
1238
|
+
},
|
|
1239
|
+
fork(state) {
|
|
1240
|
+
deps.beforeSpawn();
|
|
1241
|
+
const streams = deps.describeFork();
|
|
1242
|
+
const r = transport.call(
|
|
1243
|
+
{ op: "proc-fork", state: { ...state, streams, cwd: Fs.cwd() } },
|
|
1244
|
+
Infinity,
|
|
1245
|
+
"proc-fork"
|
|
1246
|
+
);
|
|
1247
|
+
if (!r.ok) return -wasiErrno(r.errno);
|
|
1248
|
+
return r.kind === "json" ? r.json : -wasiErrno("EIO");
|
|
1249
|
+
},
|
|
1250
|
+
select(read, write, timeoutMs) {
|
|
1251
|
+
const kernel = (fd) => Fs.getStream(fd)?.sliccKernelFd;
|
|
1252
|
+
const own = (fd) => kernel(fd) === void 0;
|
|
1253
|
+
if (![...read, ...write].some(own))
|
|
1254
|
+
return kernelSelect(Fs, transport, read, write, timeoutMs);
|
|
1255
|
+
const deadline = timeoutMs < 0 ? Number.POSITIVE_INFINITY : Date.now() + timeoutMs;
|
|
1256
|
+
const kernelRead = read.filter((fd) => !own(fd));
|
|
1257
|
+
const kernelWrite = write.filter((fd) => !own(fd));
|
|
1258
|
+
for (; ; ) {
|
|
1259
|
+
const local = ownReady(Fs, read.filter(own), write.filter(own));
|
|
1260
|
+
const pending = local.read.length > 0 || local.write.length > 0;
|
|
1261
|
+
const slice = pending ? 0 : Math.min(SELECT_SLICE_MS, Math.max(0, deadline - Date.now()));
|
|
1262
|
+
const got = kernelSelect(Fs, transport, kernelRead, kernelWrite, slice);
|
|
1263
|
+
if (typeof got === "number") return got;
|
|
1264
|
+
const ready = { read: [...local.read, ...got.read], write: [...local.write, ...got.write] };
|
|
1265
|
+
if (ready.read.length > 0 || ready.write.length > 0 || Date.now() >= deadline) {
|
|
1266
|
+
return ready;
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
},
|
|
1270
|
+
execWait(pid) {
|
|
1271
|
+
const r = transport.call({ op: "proc-exec", pid }, Infinity, `exec ${pid}`);
|
|
1272
|
+
if (!r.ok) return -wasiErrno(r.errno);
|
|
1273
|
+
deps.afterChild();
|
|
1274
|
+
return r.kind === "json" ? r.json[1] : 0;
|
|
1275
|
+
},
|
|
1276
|
+
pause() {
|
|
1277
|
+
return status(call({ op: "sig-pause" }, "pause"));
|
|
1278
|
+
},
|
|
1279
|
+
kill(pid, sig) {
|
|
1280
|
+
if (pid === deps.pid) {
|
|
1281
|
+
if (sig !== 0) deps.raise?.(sig);
|
|
1282
|
+
return 0;
|
|
1283
|
+
}
|
|
1284
|
+
return status(call({ op: "proc-kill", pid, sig }, `kill ${pid}`));
|
|
1285
|
+
},
|
|
1286
|
+
setpgid: (pid, pgid) => status(call({ op: "proc-setpgid", pid, pgid }, "setpgid")),
|
|
1287
|
+
setsid: () => number(call({ op: "proc-setsid" }, "setsid")),
|
|
1288
|
+
getpgid: (pid) => number(call({ op: "proc-getpgid", pid }, "getpgid")),
|
|
1289
|
+
getsid: (pid) => number(call({ op: "proc-getsid", pid }, "getsid")),
|
|
1290
|
+
tcgetpgrp(fd) {
|
|
1291
|
+
const kfd = Fs.getStream(fd)?.sliccKernelFd;
|
|
1292
|
+
if (kfd === void 0) return -wasiErrno("ENOTTY");
|
|
1293
|
+
return number(call({ op: "tty-pgrp-get", fd: kfd }, "tcgetpgrp"));
|
|
1294
|
+
},
|
|
1295
|
+
tcsetpgrp(fd, pgrp) {
|
|
1296
|
+
const kfd = Fs.getStream(fd)?.sliccKernelFd;
|
|
1297
|
+
if (kfd === void 0) return -wasiErrno("ENOTTY");
|
|
1298
|
+
return status(call({ op: "tty-pgrp-set", fd: kfd, pgrp }, "tcsetpgrp"));
|
|
1299
|
+
},
|
|
1300
|
+
wait(pid, nohang, options) {
|
|
1301
|
+
const key = pid > 0 ? reaped.has(pid) ? pid : void 0 : reaped.keys().next().value;
|
|
1302
|
+
if (key !== void 0) {
|
|
1303
|
+
const status2 = reaped.get(key);
|
|
1304
|
+
reaped.delete(key);
|
|
1305
|
+
return [key, status2];
|
|
1306
|
+
}
|
|
1307
|
+
return kernelWait(pid, nohang, options);
|
|
1308
|
+
}
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
// src/process/process-pty.ts
|
|
1313
|
+
var TIOCGPTN = 2147767344;
|
|
1314
|
+
var TIOCSPTLCK = 1074025521;
|
|
1315
|
+
var TIOCSCTTY = 21518;
|
|
1316
|
+
var TIOCSWINSZ = 21524;
|
|
1317
|
+
var TIOCPKT = 21536;
|
|
1318
|
+
function failed(e) {
|
|
1319
|
+
const code = e?.code;
|
|
1320
|
+
if (typeof code === "string") return -wasiErrno(code);
|
|
1321
|
+
throw e;
|
|
1322
|
+
}
|
|
1323
|
+
function ptyIoctl(ioctl, deps) {
|
|
1324
|
+
return (fd, op, varargs) => {
|
|
1325
|
+
const request = op >>> 0;
|
|
1326
|
+
if (request !== TIOCGPTN && request !== TIOCSPTLCK && request !== TIOCSCTTY && request !== TIOCSWINSZ && request !== TIOCPKT) {
|
|
1327
|
+
return ioctl(fd, op, varargs);
|
|
1328
|
+
}
|
|
1329
|
+
const stream = deps.fs()?.getStream(fd);
|
|
1330
|
+
const kfd = stream?.sliccKernelFd;
|
|
1331
|
+
if (kfd === void 0) return ioctl(fd, op, varargs);
|
|
1332
|
+
const heap = deps.heap();
|
|
1333
|
+
if (!heap) return -wasiErrno("EFAULT");
|
|
1334
|
+
const argp = (heap[varargs >> 2] ?? 0) >>> 0;
|
|
1335
|
+
try {
|
|
1336
|
+
return answer(request, kfd, argp, heap, deps.kernel);
|
|
1337
|
+
} catch (e) {
|
|
1338
|
+
return failed(e);
|
|
1339
|
+
}
|
|
1340
|
+
};
|
|
1341
|
+
}
|
|
1342
|
+
function answer(request, kfd, argp, heap, kernel) {
|
|
1343
|
+
switch (request) {
|
|
1344
|
+
case TIOCGPTN:
|
|
1345
|
+
heap[argp >> 2] = kernel.ptyNumber(kfd);
|
|
1346
|
+
return 0;
|
|
1347
|
+
case TIOCSPTLCK:
|
|
1348
|
+
kernel.ptyLock(kfd, (heap[argp >> 2] ?? 0) !== 0);
|
|
1349
|
+
return 0;
|
|
1350
|
+
case TIOCSCTTY:
|
|
1351
|
+
kernel.setControllingTerminal(kfd);
|
|
1352
|
+
return 0;
|
|
1353
|
+
case TIOCPKT:
|
|
1354
|
+
kernel.setPacketMode(kfd, (heap[argp >> 2] ?? 0) !== 0);
|
|
1355
|
+
return 0;
|
|
1356
|
+
default: {
|
|
1357
|
+
const word = heap[argp >> 2] ?? 0;
|
|
1358
|
+
kernel.setWinsize(kfd, word & 65535, word >>> 16 & 65535);
|
|
1359
|
+
return 0;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
// src/process/process-fds.ts
|
|
1365
|
+
var O_CLOEXEC = 524288;
|
|
1366
|
+
var FD_CLOEXEC = 1;
|
|
1367
|
+
var F_DUPFD = 0;
|
|
1368
|
+
var F_GETFD = 1;
|
|
1369
|
+
var F_SETFD = 2;
|
|
1370
|
+
var F_GETFL = 3;
|
|
1371
|
+
var F_DUPFD_CLOEXEC = 1030;
|
|
1372
|
+
function closesOnExec(stream) {
|
|
1373
|
+
return stream.sliccCloexec === true;
|
|
1374
|
+
}
|
|
1375
|
+
function setCloseOnExec(stream, on) {
|
|
1376
|
+
if (on) stream.sliccCloexec = true;
|
|
1377
|
+
else delete stream.sliccCloexec;
|
|
1378
|
+
}
|
|
1379
|
+
var tracked = /* @__PURE__ */ new WeakSet();
|
|
1380
|
+
function trackCloseOnExec(Fs) {
|
|
1381
|
+
if (tracked.has(Fs)) return;
|
|
1382
|
+
tracked.add(Fs);
|
|
1383
|
+
if (typeof Fs.dupStream === "function") {
|
|
1384
|
+
const dupStream = Fs.dupStream.bind(Fs);
|
|
1385
|
+
Fs.dupStream = (stream, fd) => {
|
|
1386
|
+
const copy = dupStream(stream, fd);
|
|
1387
|
+
setCloseOnExec(copy, false);
|
|
1388
|
+
return copy;
|
|
1389
|
+
};
|
|
1390
|
+
}
|
|
1391
|
+
if (typeof Fs.open === "function") {
|
|
1392
|
+
const open = Fs.open.bind(Fs);
|
|
1393
|
+
Fs.open = (path, flags, mode) => {
|
|
1394
|
+
const cloexec = typeof flags === "number" && (flags & O_CLOEXEC) !== 0;
|
|
1395
|
+
const stream = open(path, cloexec ? flags & ~O_CLOEXEC : flags, mode);
|
|
1396
|
+
setCloseOnExec(stream, cloexec);
|
|
1397
|
+
return stream;
|
|
1398
|
+
};
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
function marker(deps) {
|
|
1402
|
+
return (fd, on) => {
|
|
1403
|
+
const stream = deps.fs()?.getStream(fd);
|
|
1404
|
+
if (stream) setCloseOnExec(stream, on);
|
|
1405
|
+
};
|
|
1406
|
+
}
|
|
1407
|
+
function cloexecFcntl(fcntl, deps) {
|
|
1408
|
+
const mark = marker(deps);
|
|
1409
|
+
return (fd, cmd, varargs) => {
|
|
1410
|
+
if (cmd === F_DUPFD_CLOEXEC) {
|
|
1411
|
+
const copy = fcntl(fd, F_DUPFD, varargs);
|
|
1412
|
+
if (copy >= 0) mark(copy, true);
|
|
1413
|
+
return copy;
|
|
1414
|
+
}
|
|
1415
|
+
if (cmd === F_GETFL) {
|
|
1416
|
+
const flags = fcntl(fd, cmd, varargs);
|
|
1417
|
+
return flags < 0 ? flags : flags & ~O_CLOEXEC;
|
|
1418
|
+
}
|
|
1419
|
+
if (cmd !== F_GETFD && cmd !== F_SETFD) return fcntl(fd, cmd, varargs);
|
|
1420
|
+
const stream = deps.fs()?.getStream(fd);
|
|
1421
|
+
if (!stream) return -wasiErrno("EBADF");
|
|
1422
|
+
if (cmd === F_GETFD) return closesOnExec(stream) ? FD_CLOEXEC : 0;
|
|
1423
|
+
const arg = deps.heap()?.[varargs >> 2];
|
|
1424
|
+
if (arg === void 0) return -wasiErrno("EINVAL");
|
|
1425
|
+
setCloseOnExec(stream, (arg & FD_CLOEXEC) !== 0);
|
|
1426
|
+
return 0;
|
|
1427
|
+
};
|
|
1428
|
+
}
|
|
1429
|
+
function cloexecPipe2(pipe2, deps) {
|
|
1430
|
+
const mark = marker(deps);
|
|
1431
|
+
return (fdPtr, flags) => {
|
|
1432
|
+
const r = pipe2(fdPtr, flags & ~O_CLOEXEC);
|
|
1433
|
+
const heap = deps.heap();
|
|
1434
|
+
if (r === 0 && flags & O_CLOEXEC && heap) {
|
|
1435
|
+
mark(heap[fdPtr >> 2], true);
|
|
1436
|
+
mark(heap[(fdPtr >> 2) + 1], true);
|
|
1437
|
+
}
|
|
1438
|
+
return r;
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1441
|
+
function cloexecByFlag(syscall, flagArg, deps) {
|
|
1442
|
+
const mark = marker(deps);
|
|
1443
|
+
return (...args) => {
|
|
1444
|
+
const cloexec = ((args[flagArg] ?? 0) & O_CLOEXEC) !== 0;
|
|
1445
|
+
if (cloexec) args[flagArg] = args[flagArg] & ~O_CLOEXEC;
|
|
1446
|
+
const fd = syscall(...args);
|
|
1447
|
+
if (fd >= 0) mark(fd, cloexec);
|
|
1448
|
+
return fd;
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
var IMPORT_NAMES = {
|
|
1452
|
+
fcntl: "__syscall_fcntl64",
|
|
1453
|
+
pipe2: "__syscall_pipe2",
|
|
1454
|
+
dup3: "__syscall_dup3",
|
|
1455
|
+
socket: "__syscall_socket",
|
|
1456
|
+
accept4: "__syscall_accept4",
|
|
1457
|
+
ioctl: "__syscall_ioctl",
|
|
1458
|
+
setitimer: "_setitimer_js"
|
|
1459
|
+
};
|
|
1460
|
+
function wrapperFactories(deps) {
|
|
1461
|
+
const { pty, timer } = deps;
|
|
1462
|
+
return {
|
|
1463
|
+
...timer ? { setitimer: (f) => kernelTimer(f, timer) } : {},
|
|
1464
|
+
fcntl: (f) => cloexecFcntl(f, deps),
|
|
1465
|
+
pipe2: (f) => cloexecPipe2(f, deps),
|
|
1466
|
+
dup3: (f) => cloexecByFlag(f, 2, deps),
|
|
1467
|
+
socket: (f) => cloexecByFlag(f, 1, deps),
|
|
1468
|
+
accept4: (f) => cloexecByFlag(f, 3, deps),
|
|
1469
|
+
...pty ? { ioctl: (f) => ptyIoctl(f, { fs: deps.fs, heap: deps.heap, kernel: pty }) } : {}
|
|
1470
|
+
};
|
|
1471
|
+
}
|
|
1472
|
+
function kernelTimer(setitimer, timer) {
|
|
1473
|
+
return (which, ms) => {
|
|
1474
|
+
if (which !== 0) return setitimer(which, ms);
|
|
1475
|
+
timer.arm(ms ?? 0);
|
|
1476
|
+
return 0;
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1479
|
+
function syscallOf(glue, name, value) {
|
|
1480
|
+
const names = Object.keys(IMPORT_NAMES);
|
|
1481
|
+
const own = names.find((key) => glue[key] !== void 0 && glue[key] === value);
|
|
1482
|
+
if (own) return own;
|
|
1483
|
+
return typeof value === "function" ? names.find((key) => glue[key] !== void 0 && IMPORT_NAMES[key] === name) : void 0;
|
|
1484
|
+
}
|
|
1485
|
+
function wrapCloexecSyscalls(imports, glue, deps) {
|
|
1486
|
+
if (!glue) return;
|
|
1487
|
+
const factories = wrapperFactories(deps);
|
|
1488
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1489
|
+
for (const namespace of Object.values(imports)) {
|
|
1490
|
+
if (!namespace || typeof namespace !== "object" || seen.has(namespace)) continue;
|
|
1491
|
+
seen.add(namespace);
|
|
1492
|
+
for (const [name, value] of Object.entries(namespace)) {
|
|
1493
|
+
const key = syscallOf(glue, name, value);
|
|
1494
|
+
const wrap = key && factories[key];
|
|
1495
|
+
if (wrap) namespace[name] = wrap(value);
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
function wasmMemory(instance, imports) {
|
|
1500
|
+
const isMemory = (v) => v instanceof WebAssembly.Memory;
|
|
1501
|
+
const exported = Object.values(instance.exports).find(isMemory);
|
|
1502
|
+
if (exported) return exported;
|
|
1503
|
+
for (const namespace of Object.values(imports)) {
|
|
1504
|
+
const found = Object.values(namespace ?? {}).find(isMemory);
|
|
1505
|
+
if (found) return found;
|
|
1506
|
+
}
|
|
1507
|
+
return void 0;
|
|
1508
|
+
}
|
|
1509
|
+
function fdOfPath(path, cwd) {
|
|
1510
|
+
const parts = [];
|
|
1511
|
+
for (const part of `${path.startsWith("/") ? "" : cwd}/${path}`.split("/")) {
|
|
1512
|
+
if (part === "" || part === ".") continue;
|
|
1513
|
+
if (part === "..") parts.pop();
|
|
1514
|
+
else parts.push(part);
|
|
1515
|
+
}
|
|
1516
|
+
const abs = `/${parts.join("/")}`;
|
|
1517
|
+
const std = ["/dev/stdin", "/dev/stdout", "/dev/stderr"].indexOf(abs);
|
|
1518
|
+
if (std >= 0) return std;
|
|
1519
|
+
const m = /^\/(?:dev|proc\/self)\/fd\/(\d+)$/.exec(abs);
|
|
1520
|
+
return m ? Number(m[1]) : void 0;
|
|
1521
|
+
}
|
|
1522
|
+
var DIR_MODE = 16749;
|
|
1523
|
+
var LINK_MODE = 41408;
|
|
1524
|
+
function syntheticAttr(mode, ino) {
|
|
1525
|
+
const now = /* @__PURE__ */ new Date();
|
|
1526
|
+
return {
|
|
1527
|
+
dev: 1,
|
|
1528
|
+
ino,
|
|
1529
|
+
mode,
|
|
1530
|
+
nlink: 1,
|
|
1531
|
+
uid: 0,
|
|
1532
|
+
gid: 0,
|
|
1533
|
+
rdev: 0,
|
|
1534
|
+
size: 0,
|
|
1535
|
+
atime: now,
|
|
1536
|
+
mtime: now,
|
|
1537
|
+
ctime: now,
|
|
1538
|
+
blksize: 4096,
|
|
1539
|
+
blocks: 0
|
|
1540
|
+
};
|
|
1541
|
+
}
|
|
1542
|
+
function describeFdDir(Fs) {
|
|
1543
|
+
let dir;
|
|
1544
|
+
try {
|
|
1545
|
+
dir = Fs.lookupPath?.("/proc/self/fd", { follow: true })?.node;
|
|
1546
|
+
} catch {
|
|
1547
|
+
return;
|
|
1548
|
+
}
|
|
1549
|
+
const ops = dir?.node_ops;
|
|
1550
|
+
const lookup = ops?.lookup;
|
|
1551
|
+
if (!dir || !lookup || ops.getattr) return;
|
|
1552
|
+
dir.node_ops = {
|
|
1553
|
+
...ops,
|
|
1554
|
+
getattr: () => syntheticAttr(DIR_MODE, 1),
|
|
1555
|
+
lookup: (parent, name) => {
|
|
1556
|
+
const entry = lookup(parent, name);
|
|
1557
|
+
entry.node_ops = {
|
|
1558
|
+
...entry.node_ops,
|
|
1559
|
+
getattr: () => syntheticAttr(LINK_MODE, entry.id ?? 0)
|
|
1560
|
+
};
|
|
1561
|
+
return entry;
|
|
1562
|
+
}
|
|
1563
|
+
};
|
|
1564
|
+
}
|
|
1565
|
+
function useDevFd(Fs) {
|
|
1566
|
+
if (typeof Fs.open !== "function") return;
|
|
1567
|
+
try {
|
|
1568
|
+
Fs.symlink?.("/proc/self/fd", "/dev/fd");
|
|
1569
|
+
} catch {
|
|
1570
|
+
}
|
|
1571
|
+
describeFdDir(Fs);
|
|
1572
|
+
const fdOf = (path) => typeof path === "string" ? fdOfPath(path, Fs.cwd()) : void 0;
|
|
1573
|
+
const target = (fd) => {
|
|
1574
|
+
const stream = Fs.getStream(fd);
|
|
1575
|
+
if (!stream) throw new Fs.ErrnoError(wasiErrno("EBADF"));
|
|
1576
|
+
return stream;
|
|
1577
|
+
};
|
|
1578
|
+
const open = Fs.open.bind(Fs);
|
|
1579
|
+
Fs.open = (path, flags, mode) => {
|
|
1580
|
+
const fd = fdOf(path);
|
|
1581
|
+
if (fd === void 0) return open(path, flags, mode);
|
|
1582
|
+
const copy = Fs.dupStream(target(fd), -1);
|
|
1583
|
+
setCloseOnExec(copy, typeof flags === "number" && (flags & O_CLOEXEC) !== 0);
|
|
1584
|
+
return copy;
|
|
1585
|
+
};
|
|
1586
|
+
const { stat, fstat } = Fs;
|
|
1587
|
+
if (typeof stat !== "function" || typeof fstat !== "function") return;
|
|
1588
|
+
Fs.stat = (path, dontFollow) => {
|
|
1589
|
+
const fd = dontFollow ? void 0 : fdOf(path);
|
|
1590
|
+
if (fd === void 0) return stat.call(Fs, path, dontFollow);
|
|
1591
|
+
target(fd);
|
|
1592
|
+
return fstat.call(Fs, fd);
|
|
1593
|
+
};
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
// src/process/process-fork.ts
|
|
1597
|
+
var O_RDWR = 2;
|
|
1598
|
+
var O_ACCMODE = 3;
|
|
1599
|
+
var O_CREAT2 = 64;
|
|
1600
|
+
var O_EXCL2 = 128;
|
|
1601
|
+
var O_TRUNC2 = 512;
|
|
1602
|
+
var PLACEHOLDER_DIR = "/dev/slicc-fd";
|
|
1603
|
+
function isVfsFile(Fs, stream) {
|
|
1604
|
+
const node = stream.node;
|
|
1605
|
+
return node.live !== void 0 && Fs.isFile(node.mode);
|
|
1606
|
+
}
|
|
1607
|
+
function orphanContents(stream) {
|
|
1608
|
+
const live = stream.node.live;
|
|
1609
|
+
if (!live?.orphan) return void 0;
|
|
1610
|
+
return live.data?.slice(0, live.len ?? live.data.length) ?? new Uint8Array(0);
|
|
1611
|
+
}
|
|
1612
|
+
function vfsPromoter(Fs, sys, streams, livePath) {
|
|
1613
|
+
const promoted = /* @__PURE__ */ new Map();
|
|
1614
|
+
return (stream) => {
|
|
1615
|
+
if (stream.sliccKernelFd !== void 0 || !isVfsFile(Fs, stream)) return;
|
|
1616
|
+
let kfd = promoted.get(stream.shared);
|
|
1617
|
+
if (kfd === void 0) {
|
|
1618
|
+
const contents = orphanContents(stream);
|
|
1619
|
+
kfd = sys.openVfs(
|
|
1620
|
+
livePath(stream),
|
|
1621
|
+
stream.flags,
|
|
1622
|
+
stream.position,
|
|
1623
|
+
contents !== void 0 ? { contents, orphan: true } : void 0
|
|
1624
|
+
);
|
|
1625
|
+
promoted.set(stream.shared, kfd);
|
|
1626
|
+
}
|
|
1627
|
+
streams.attachFile(stream, kfd);
|
|
1628
|
+
};
|
|
1629
|
+
}
|
|
1630
|
+
function describeForFork(Fs, sys, streams, livePath) {
|
|
1631
|
+
const promote = vfsPromoter(Fs, sys, streams, livePath);
|
|
1632
|
+
const out = [];
|
|
1633
|
+
for (const stream of Fs.streams) {
|
|
1634
|
+
if (!stream) continue;
|
|
1635
|
+
promote(stream);
|
|
1636
|
+
if (stream.sliccKernelFd !== void 0) {
|
|
1637
|
+
out.push(kernelEntry(stream, stream.sliccKernelFd));
|
|
1638
|
+
} else if (stream.path) {
|
|
1639
|
+
const flags = stream.flags | (closesOnExec(stream) ? O_CLOEXEC : 0);
|
|
1640
|
+
out.push({ fd: stream.fd, path: stream.path, flags });
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
return out;
|
|
1644
|
+
}
|
|
1645
|
+
function kernelEntry(stream, kernel) {
|
|
1646
|
+
const cloexec = closesOnExec(stream) ? { cloexec: true } : {};
|
|
1647
|
+
const kind = stream.sliccKernelFile ? "file" : stream.tty ? "tty" : "stream";
|
|
1648
|
+
return { fd: stream.fd, kernel, kind, ...cloexec };
|
|
1649
|
+
}
|
|
1650
|
+
var ACCESS = { 0: "read", 1: "write" };
|
|
1651
|
+
var DEVICES = {
|
|
1652
|
+
"/dev/null": "null",
|
|
1653
|
+
"/dev/zero": "zero",
|
|
1654
|
+
"/dev/urandom": "urandom",
|
|
1655
|
+
"/dev/random": "urandom"
|
|
1656
|
+
};
|
|
1657
|
+
function describeInherited(Fs, sys, streams, livePath, actions = []) {
|
|
1658
|
+
const table = /* @__PURE__ */ new Map();
|
|
1659
|
+
for (const stream of Fs.streams) {
|
|
1660
|
+
if (stream) table.set(stream.fd, { stream, cloexec: closesOnExec(stream) });
|
|
1661
|
+
}
|
|
1662
|
+
for (const [target, source] of actions) {
|
|
1663
|
+
if (target <= 2) continue;
|
|
1664
|
+
const from = source >= 0 ? table.get(source) : void 0;
|
|
1665
|
+
if (from) table.set(target, { stream: from.stream, cloexec: false });
|
|
1666
|
+
else table.delete(target);
|
|
1667
|
+
}
|
|
1668
|
+
const slots = /* @__PURE__ */ new Map();
|
|
1669
|
+
for (const [fd, { stream, cloexec }] of table) {
|
|
1670
|
+
if (fd > 2 && !cloexec) slots.set(fd, stream);
|
|
1671
|
+
}
|
|
1672
|
+
const promote = vfsPromoter(Fs, sys, streams, livePath);
|
|
1673
|
+
const out = [];
|
|
1674
|
+
for (const [fd, stream] of slots) {
|
|
1675
|
+
promote(stream);
|
|
1676
|
+
const slot = inheritedSlot(fd, stream);
|
|
1677
|
+
if (slot) out.push(slot);
|
|
1678
|
+
}
|
|
1679
|
+
return out;
|
|
1680
|
+
}
|
|
1681
|
+
function inheritedSlot(fd, stream) {
|
|
1682
|
+
if (stream.sliccKernelFd === void 0) {
|
|
1683
|
+
const device = stream.path === void 0 ? void 0 : DEVICES[stream.path];
|
|
1684
|
+
if (!device) return void 0;
|
|
1685
|
+
const access = ACCESS[stream.flags & O_ACCMODE];
|
|
1686
|
+
return { fd, device, ...access ? { access } : {} };
|
|
1687
|
+
}
|
|
1688
|
+
return { fd, kernel: stream.sliccKernelFd };
|
|
1689
|
+
}
|
|
1690
|
+
function place(Fs, stream, fd) {
|
|
1691
|
+
if (stream.fd === fd) return stream;
|
|
1692
|
+
const moved = Fs.dupStream(stream, fd);
|
|
1693
|
+
Fs.closeStream(stream.fd);
|
|
1694
|
+
return moved;
|
|
1695
|
+
}
|
|
1696
|
+
function placeholder(Fs, entry) {
|
|
1697
|
+
if (entry.kind === "tty") return Fs.open("/dev/tty", O_RDWR);
|
|
1698
|
+
if (entry.kind === "stream") return Fs.open("/dev/null", O_RDWR);
|
|
1699
|
+
Fs.mkdirTree(PLACEHOLDER_DIR);
|
|
1700
|
+
return Fs.open(`${PLACEHOLDER_DIR}/${entry.fd}`, O_RDWR | O_CREAT2);
|
|
1701
|
+
}
|
|
1702
|
+
var FIFO_MODE = 4480;
|
|
1703
|
+
var nextStreamIno = 1073741824;
|
|
1704
|
+
var streamInos = /* @__PURE__ */ new WeakMap();
|
|
1705
|
+
function asFifo(stream, streams, identity) {
|
|
1706
|
+
const inos = streamInos.get(streams) ?? /* @__PURE__ */ new Map();
|
|
1707
|
+
streamInos.set(streams, inos);
|
|
1708
|
+
const fixed = inos.get(identity) ?? nextStreamIno++;
|
|
1709
|
+
inos.set(identity, fixed);
|
|
1710
|
+
const node = stream.node;
|
|
1711
|
+
stream.stream_ops = {
|
|
1712
|
+
...stream.stream_ops,
|
|
1713
|
+
getattr: () => ({ ...node.node_ops?.getattr?.(node), mode: FIFO_MODE, ino: fixed, size: 0 })
|
|
1714
|
+
};
|
|
1715
|
+
}
|
|
1716
|
+
function placeKernelStream(Fs, streams, entry) {
|
|
1717
|
+
const stream = place(Fs, placeholder(Fs, entry), entry.fd);
|
|
1718
|
+
if (entry.kind === "file") streams.attachFile(stream, entry.kernel);
|
|
1719
|
+
else streams.attach(stream, entry.kernel, entry.kind === "tty");
|
|
1720
|
+
if (entry.kind === "tty") streams.nameTerminal(stream);
|
|
1721
|
+
if (entry.kind === "stream") {
|
|
1722
|
+
asFifo(stream, streams, entry.desc !== void 0 ? `d${entry.desc}` : `k${entry.kernel}`);
|
|
1723
|
+
}
|
|
1724
|
+
setCloseOnExec(stream, entry.cloexec === true);
|
|
1725
|
+
return stream;
|
|
1726
|
+
}
|
|
1727
|
+
function restoreForkedStreams(Fs, streams, table) {
|
|
1728
|
+
for (const stream of Fs.streams) if (stream) Fs.closeStream(stream.fd);
|
|
1729
|
+
for (const entry of table) {
|
|
1730
|
+
try {
|
|
1731
|
+
if ("kernel" in entry) {
|
|
1732
|
+
placeKernelStream(Fs, streams, entry);
|
|
1733
|
+
} else {
|
|
1734
|
+
const placed = place(
|
|
1735
|
+
Fs,
|
|
1736
|
+
Fs.open(entry.path, entry.flags & ~(O_CREAT2 | O_EXCL2 | O_TRUNC2)),
|
|
1737
|
+
entry.fd
|
|
1738
|
+
);
|
|
1739
|
+
setCloseOnExec(placed, (entry.flags & O_CLOEXEC) !== 0);
|
|
1740
|
+
}
|
|
1741
|
+
} catch {
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
// src/process/process-signals.ts
|
|
1747
|
+
var SignalGate = class {
|
|
1748
|
+
reported = { caught: 0, ignored: 0 };
|
|
1749
|
+
restart = 0;
|
|
1750
|
+
lastDelivered = 0;
|
|
1751
|
+
depth = 0;
|
|
1752
|
+
reporting = false;
|
|
1753
|
+
raw;
|
|
1754
|
+
header;
|
|
1755
|
+
hooks;
|
|
1756
|
+
constructor(raw, header, hooks) {
|
|
1757
|
+
this.raw = raw;
|
|
1758
|
+
this.header = header;
|
|
1759
|
+
this.hooks = hooks;
|
|
1760
|
+
}
|
|
1761
|
+
transport() {
|
|
1762
|
+
return {
|
|
1763
|
+
call: (req, timeoutMs, label) => {
|
|
1764
|
+
this.report();
|
|
1765
|
+
this.depth++;
|
|
1766
|
+
try {
|
|
1767
|
+
const result = this.raw.call(req, timeoutMs, label);
|
|
1768
|
+
this.deliver();
|
|
1769
|
+
return result;
|
|
1770
|
+
} finally {
|
|
1771
|
+
this.depth--;
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
};
|
|
1775
|
+
}
|
|
1776
|
+
restartable() {
|
|
1777
|
+
return this.lastDelivered !== 0 && (this.lastDelivered & ~this.restart) === 0;
|
|
1778
|
+
}
|
|
1779
|
+
report() {
|
|
1780
|
+
if (this.reporting) return;
|
|
1781
|
+
this.reporting = true;
|
|
1782
|
+
let masks;
|
|
1783
|
+
try {
|
|
1784
|
+
masks = this.hooks.masks();
|
|
1785
|
+
} catch {
|
|
1786
|
+
masks = null;
|
|
1787
|
+
} finally {
|
|
1788
|
+
this.reporting = false;
|
|
1789
|
+
}
|
|
1790
|
+
if (!masks) return;
|
|
1791
|
+
this.restart = masks.restart;
|
|
1792
|
+
const { caught, ignored } = masks;
|
|
1793
|
+
if (caught === this.reported.caught && ignored === this.reported.ignored) return;
|
|
1794
|
+
this.reported = { caught, ignored };
|
|
1795
|
+
this.raw.call({ op: "sig-mask", caught, ignored }, Number.POSITIVE_INFINITY, "sig-mask");
|
|
1796
|
+
}
|
|
1797
|
+
deliver() {
|
|
1798
|
+
const timers = Atomics.exchange(this.header, SAB_I_TIMERS, 0);
|
|
1799
|
+
const pending = Atomics.exchange(this.header, SAB_I_SIGNALS, 0);
|
|
1800
|
+
if (this.depth <= 1) this.lastDelivered = pending | (timers & 1 ? sigbit(SIG.ALRM) : 0);
|
|
1801
|
+
for (let which = 0; which < 3; which++) {
|
|
1802
|
+
if (timers & 1 << which) this.hooks.timer?.(which);
|
|
1803
|
+
}
|
|
1804
|
+
for (const sig of signalsIn(pending)) this.hooks.raise(sig);
|
|
1805
|
+
}
|
|
1806
|
+
};
|
|
1807
|
+
|
|
1808
|
+
// src/process/realm-user.ts
|
|
1809
|
+
var REALM_UID = 1e3;
|
|
1810
|
+
var REALM_GID = 1e3;
|
|
1811
|
+
function ownByRealmUser(Fs) {
|
|
1812
|
+
const { stat, fstat } = Fs;
|
|
1813
|
+
const owned = (attr) => ({ ...attr, uid: REALM_UID, gid: REALM_GID });
|
|
1814
|
+
if (typeof stat === "function") {
|
|
1815
|
+
Fs.stat = (path, dontFollow) => owned(stat.call(Fs, path, dontFollow));
|
|
1816
|
+
}
|
|
1817
|
+
if (typeof fstat === "function") Fs.fstat = (fd) => owned(fstat.call(Fs, fd));
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
// src/process/process-runtime.ts
|
|
1821
|
+
function kernelSys(transport) {
|
|
1822
|
+
const call = (req, label) => {
|
|
1823
|
+
const r = transport.call(req, Number.POSITIVE_INFINITY, label);
|
|
1824
|
+
if (!r.ok) throw new SyscallError(r.errno);
|
|
1825
|
+
return r;
|
|
1826
|
+
};
|
|
1827
|
+
const json = (r) => r.ok && r.kind === "json" ? r.json : void 0;
|
|
1828
|
+
return {
|
|
1829
|
+
read(fd, max, opts) {
|
|
1830
|
+
const flags = {
|
|
1831
|
+
...opts?.nonblock ? { nonblock: true } : {},
|
|
1832
|
+
...opts?.peek ? { peek: true } : {}
|
|
1833
|
+
};
|
|
1834
|
+
const r = call({ op: "fd-read", fd, max, ...flags }, `fd-read ${fd}`);
|
|
1835
|
+
return r.ok && r.kind === "bytes" ? r.bytes : new Uint8Array(0);
|
|
1836
|
+
},
|
|
1837
|
+
write(fd, bytes, opts) {
|
|
1838
|
+
const req = {
|
|
1839
|
+
op: "fd-write",
|
|
1840
|
+
fd,
|
|
1841
|
+
body: bytes,
|
|
1842
|
+
...opts?.nonblock ? { nonblock: true } : {}
|
|
1843
|
+
};
|
|
1844
|
+
const n = json(call(req, `fd-write ${fd}`));
|
|
1845
|
+
return typeof n === "number" ? n : bytes.length;
|
|
1846
|
+
},
|
|
1847
|
+
close(fd) {
|
|
1848
|
+
call({ op: "fd-close", fd }, `fd-close ${fd}`);
|
|
1849
|
+
},
|
|
1850
|
+
pipe() {
|
|
1851
|
+
return json(call({ op: "fd-pipe" }, "fd-pipe"));
|
|
1852
|
+
},
|
|
1853
|
+
poll(fd) {
|
|
1854
|
+
return json(call({ op: "fd-poll", fd }, `fd-poll ${fd}`));
|
|
1855
|
+
},
|
|
1856
|
+
openVfs(path, flags, position, opts) {
|
|
1857
|
+
return json(
|
|
1858
|
+
call(
|
|
1859
|
+
{
|
|
1860
|
+
op: "fd-open-vfs",
|
|
1861
|
+
path,
|
|
1862
|
+
flags,
|
|
1863
|
+
position,
|
|
1864
|
+
...opts?.contents !== void 0 ? { contents: opts.contents } : {},
|
|
1865
|
+
...opts?.orphan ? { orphan: true } : {},
|
|
1866
|
+
...opts?.truncate ? { truncate: true } : {},
|
|
1867
|
+
...opts?.create ? { create: true } : {}
|
|
1868
|
+
},
|
|
1869
|
+
`fd-open-vfs ${path}`
|
|
1870
|
+
)
|
|
1871
|
+
);
|
|
1872
|
+
},
|
|
1873
|
+
seek(fd, offset, whence) {
|
|
1874
|
+
return json(call({ op: "fd-seek", fd, offset, whence }, `fd-seek ${fd}`));
|
|
1875
|
+
},
|
|
1876
|
+
flush(fd) {
|
|
1877
|
+
call({ op: "fd-flush", fd }, `fd-flush ${fd}`);
|
|
1878
|
+
},
|
|
1879
|
+
pread(fd, max, at) {
|
|
1880
|
+
const r = call({ op: "fd-pread", fd, max, offset: at }, `fd-pread ${fd}`);
|
|
1881
|
+
return r.ok && r.kind === "bytes" ? r.bytes : new Uint8Array(0);
|
|
1882
|
+
},
|
|
1883
|
+
isatty(fd) {
|
|
1884
|
+
return json(call({ op: "fd-info", fd }, `fd-info ${fd}`))?.tty === true;
|
|
1885
|
+
},
|
|
1886
|
+
ttyName(fd) {
|
|
1887
|
+
return json(call({ op: "fd-info", fd }, `fd-info ${fd}`))?.name;
|
|
1888
|
+
},
|
|
1889
|
+
openTty(name) {
|
|
1890
|
+
const req = name === void 0 ? { op: "fd-open-tty" } : { op: "fd-open-tty", name };
|
|
1891
|
+
return json(call(req, `fd-open-tty ${name ?? ""}`));
|
|
1892
|
+
},
|
|
1893
|
+
tcgets(fd) {
|
|
1894
|
+
return json(call({ op: "tty-get", fd }, `tty-get ${fd}`));
|
|
1895
|
+
},
|
|
1896
|
+
tcsets(fd, termios) {
|
|
1897
|
+
call({ op: "tty-set", fd, termios }, `tty-set ${fd}`);
|
|
1898
|
+
},
|
|
1899
|
+
winsize(fd) {
|
|
1900
|
+
return json(call({ op: "tty-winsz", fd }, `tty-winsz ${fd}`));
|
|
1901
|
+
},
|
|
1902
|
+
openPty() {
|
|
1903
|
+
return json(call({ op: "pty-open" }, "pty-open"));
|
|
1904
|
+
},
|
|
1905
|
+
openPts(n, noctty) {
|
|
1906
|
+
return json(call({ op: "pty-slave-open", n, noctty }, `pty-slave-open ${n}`));
|
|
1907
|
+
},
|
|
1908
|
+
ptyNumbers() {
|
|
1909
|
+
return json(call({ op: "pty-list" }, "pty-list"));
|
|
1910
|
+
},
|
|
1911
|
+
ptyNumber(fd) {
|
|
1912
|
+
return json(call({ op: "pty-number", fd }, `pty-number ${fd}`));
|
|
1913
|
+
},
|
|
1914
|
+
ptyLock(fd, lock) {
|
|
1915
|
+
call({ op: "pty-lock", fd, lock }, `pty-lock ${fd}`);
|
|
1916
|
+
},
|
|
1917
|
+
setControllingTerminal(fd) {
|
|
1918
|
+
call({ op: "pty-ctty", fd }, `pty-ctty ${fd}`);
|
|
1919
|
+
},
|
|
1920
|
+
setPacketMode(fd, on) {
|
|
1921
|
+
call({ op: "pty-packet", fd, on }, `pty-packet ${fd}`);
|
|
1922
|
+
},
|
|
1923
|
+
setWinsize(fd, rows, cols) {
|
|
1924
|
+
call({ op: "pty-winsz-set", fd, rows, cols }, `pty-winsz-set ${fd}`);
|
|
1925
|
+
}
|
|
1926
|
+
};
|
|
1927
|
+
}
|
|
1928
|
+
function wireKernelFd(Fs, streams, entry) {
|
|
1929
|
+
if (entry.device && openDevice(Fs, entry.fd, entry.device)) return;
|
|
1930
|
+
placeKernelStream(Fs, streams, {
|
|
1931
|
+
...entry,
|
|
1932
|
+
kind: entry.device ? "stream" : entry.kind,
|
|
1933
|
+
kernel: entry.fd
|
|
1934
|
+
});
|
|
1935
|
+
}
|
|
1936
|
+
var O_RDONLY = 0;
|
|
1937
|
+
var O_WRONLY = 1;
|
|
1938
|
+
var O_RDWR2 = 2;
|
|
1939
|
+
function openDevice(Fs, fd, meta) {
|
|
1940
|
+
const flags = meta.access === "read" ? O_RDONLY : meta.access === "write" ? O_WRONLY : O_RDWR2;
|
|
1941
|
+
let stream;
|
|
1942
|
+
try {
|
|
1943
|
+
stream = Fs.open(`/dev/${meta.device}`, flags);
|
|
1944
|
+
} catch {
|
|
1945
|
+
return false;
|
|
1946
|
+
}
|
|
1947
|
+
if (stream.fd !== fd) {
|
|
1948
|
+
Fs.dupStream(stream, fd);
|
|
1949
|
+
Fs.closeStream(stream.fd);
|
|
1950
|
+
}
|
|
1951
|
+
return true;
|
|
1952
|
+
}
|
|
1953
|
+
function wireKernelStdio(Fs, streams) {
|
|
1954
|
+
for (const fd of [0, 1, 2]) {
|
|
1955
|
+
const stream = Fs.getStream(fd);
|
|
1956
|
+
if (!stream) continue;
|
|
1957
|
+
streams.attach(stream, fd);
|
|
1958
|
+
streams.nameTerminal(stream);
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
function glueBody(glue) {
|
|
1962
|
+
return glue.startsWith("#!") ? glue.slice(glue.indexOf("\n") + 1) : glue;
|
|
1963
|
+
}
|
|
1964
|
+
var GLUE_TRAILER = [
|
|
1965
|
+
"if (typeof ENV !== 'undefined') Object.assign(ENV, Module.sliccEnv);",
|
|
1966
|
+
"const __sliccTake = (name, value) => {",
|
|
1967
|
+
" const own = Object.getOwnPropertyDescriptor(Module, name);",
|
|
1968
|
+
" if (own && 'value' in own && own.value != null) return;",
|
|
1969
|
+
" Object.defineProperty(Module, name, { value, writable: true, configurable: true, enumerable: true });",
|
|
1970
|
+
"};",
|
|
1971
|
+
"if (typeof FS !== 'undefined') __sliccTake('FS', FS);",
|
|
1972
|
+
"if (typeof callMain === 'function') __sliccTake('callMain', callMain);",
|
|
1973
|
+
"if (typeof sliccRunMain === 'function') __sliccTake('sliccRunMain', sliccRunMain);",
|
|
1974
|
+
"if (typeof sliccForkChild === 'function') __sliccTake('sliccForkChild', sliccForkChild);",
|
|
1975
|
+
"if (typeof PIPEFS !== 'undefined') __sliccTake('PIPEFS', PIPEFS);",
|
|
1976
|
+
"Module.sliccSyscalls ??= {",
|
|
1977
|
+
" fcntl: typeof ___syscall_fcntl64 === 'function' ? ___syscall_fcntl64 : undefined,",
|
|
1978
|
+
" pipe2: typeof ___syscall_pipe2 === 'function' ? ___syscall_pipe2 : undefined,",
|
|
1979
|
+
" dup3: typeof ___syscall_dup3 === 'function' ? ___syscall_dup3 : undefined,",
|
|
1980
|
+
" socket: typeof ___syscall_socket === 'function' ? ___syscall_socket : undefined,",
|
|
1981
|
+
" accept4: typeof ___syscall_accept4 === 'function' ? ___syscall_accept4 : undefined,",
|
|
1982
|
+
" ioctl: typeof ___syscall_ioctl === 'function' ? ___syscall_ioctl : undefined,",
|
|
1983
|
+
" setitimer: typeof __setitimer_js === 'function' ? __setitimer_js : undefined,",
|
|
1984
|
+
"};",
|
|
1985
|
+
"const __sliccUp = () =>",
|
|
1986
|
+
" (typeof runtimeInitialized === 'undefined' || runtimeInitialized) &&",
|
|
1987
|
+
" !(typeof runtimeExited !== 'undefined' && runtimeExited) &&",
|
|
1988
|
+
" !(typeof ABORT !== 'undefined' && ABORT);",
|
|
1989
|
+
"Module.sliccSigpipe ??= () => (__sliccUp() && typeof _slicc_sigpipe === 'function' ? _slicc_sigpipe() : -1);",
|
|
1990
|
+
"Module.sliccSigMask ??= (w) => (__sliccUp() && typeof _slicc_sig_mask === 'function' ? _slicc_sig_mask(w) : -1);",
|
|
1991
|
+
"Module.sliccRaise ??= (sig) => { if (__sliccUp() && typeof _slicc_raise === 'function') _slicc_raise(sig); };",
|
|
1992
|
+
"Module.sliccTimerFire ??= (which) => {",
|
|
1993
|
+
" if (!__sliccUp() || typeof __emscripten_timeout !== 'function') return false;",
|
|
1994
|
+
" __emscripten_timeout(which, typeof _emscripten_get_now === 'function' ? _emscripten_get_now() : performance.now());",
|
|
1995
|
+
" return true;",
|
|
1996
|
+
"};",
|
|
1997
|
+
"if (typeof SliccFork !== 'undefined' && !SliccFork.balancesKeepalive && typeof runtimeKeepalivePop === 'function') {",
|
|
1998
|
+
" let __sliccForking = SliccFork.forking === true;",
|
|
1999
|
+
" Object.defineProperty(SliccFork, 'forking', {",
|
|
2000
|
+
" get: () => __sliccForking,",
|
|
2001
|
+
" set: (on) => { if (__sliccForking && !on) runtimeKeepalivePop(); __sliccForking = on; },",
|
|
2002
|
+
" configurable: true,",
|
|
2003
|
+
" });",
|
|
2004
|
+
"}"
|
|
2005
|
+
].join("\n");
|
|
2006
|
+
function ownValue(module, name) {
|
|
2007
|
+
const own = Object.getOwnPropertyDescriptor(module, name);
|
|
2008
|
+
return own && "value" in own ? own.value : void 0;
|
|
2009
|
+
}
|
|
2010
|
+
var evaluateGlue = (glue, module) => {
|
|
2011
|
+
let run;
|
|
2012
|
+
try {
|
|
2013
|
+
run = new Function("Module", `${glueBody(glue)}
|
|
2014
|
+
;${GLUE_TRAILER}`);
|
|
2015
|
+
} catch (e) {
|
|
2016
|
+
throw e instanceof EvalError ? new Error(EVAL_BLOCKED) : e;
|
|
2017
|
+
}
|
|
2018
|
+
run(module);
|
|
2019
|
+
};
|
|
2020
|
+
var EVAL_BLOCKED = "the wasm realm evaluates the program's Emscripten glue, and this page's CSP forbids eval (no 'unsafe-eval')";
|
|
2021
|
+
function signalMasks(m) {
|
|
2022
|
+
const mask = m.sliccSigMask;
|
|
2023
|
+
const caught = mask?.(0) ?? -1;
|
|
2024
|
+
if (!mask || caught === -1) return null;
|
|
2025
|
+
return { caught, ignored: mask(1), restart: mask(2) };
|
|
2026
|
+
}
|
|
2027
|
+
async function runWasmProcess(init, port, deps = {}) {
|
|
2028
|
+
const signals = new SignalGate(
|
|
2029
|
+
createSyncSabTransport(init.sab, port),
|
|
2030
|
+
new Int32Array(init.sab, 0, SAB_HEADER_I32),
|
|
2031
|
+
{
|
|
2032
|
+
masks: () => signalMasks(module),
|
|
2033
|
+
raise: (sig) => module.sliccRaise?.(sig),
|
|
2034
|
+
timer: (which) => {
|
|
2035
|
+
const running2 = module;
|
|
2036
|
+
if (!running2.sliccTimerFire?.(which)) running2.sliccRaise?.(SIG.ALRM);
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
);
|
|
2040
|
+
const transport = signals.transport();
|
|
2041
|
+
const sys = kernelSys(transport);
|
|
2042
|
+
const encoder2 = new TextEncoder();
|
|
2043
|
+
const say = (fd) => (text) => sys.write(fd, encoder2.encode(`${text}
|
|
2044
|
+
`));
|
|
2045
|
+
let ready;
|
|
2046
|
+
let failed2;
|
|
2047
|
+
let memory;
|
|
2048
|
+
const initialized = new Promise((resolve, reject) => {
|
|
2049
|
+
ready = resolve;
|
|
2050
|
+
failed2 = reject;
|
|
2051
|
+
});
|
|
2052
|
+
const module = {
|
|
2053
|
+
noInitialRun: true,
|
|
2054
|
+
thisProgram: init.argv0,
|
|
2055
|
+
sliccPid: init.pid,
|
|
2056
|
+
...init.ppid !== void 0 ? { sliccPpid: init.ppid } : {},
|
|
2057
|
+
sliccEnv: init.env,
|
|
2058
|
+
print: say(1),
|
|
2059
|
+
printErr: say(2),
|
|
2060
|
+
instantiateWasm(imports, done) {
|
|
2061
|
+
Promise.resolve().then(() => {
|
|
2062
|
+
wrapCloexecSyscalls(imports, ownValue(module, "sliccSyscalls"), {
|
|
2063
|
+
fs: () => ownValue(module, "FS"),
|
|
2064
|
+
heap: () => memory ? new Int32Array(memory.buffer) : void 0,
|
|
2065
|
+
pty: sys,
|
|
2066
|
+
timer: {
|
|
2067
|
+
arm: (ms) => {
|
|
2068
|
+
transport.call(
|
|
2069
|
+
{ op: "proc-alarm", sig: SIG.ALRM, ms, repeat: false, timer: 0 },
|
|
2070
|
+
Number.POSITIVE_INFINITY,
|
|
2071
|
+
"alarm"
|
|
2072
|
+
);
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
});
|
|
2076
|
+
return WebAssembly.instantiate(init.program.module, imports);
|
|
2077
|
+
}).then((instance) => {
|
|
2078
|
+
memory = wasmMemory(instance, imports);
|
|
2079
|
+
done(instance, init.program.module);
|
|
2080
|
+
}, failed2);
|
|
2081
|
+
return {};
|
|
2082
|
+
},
|
|
2083
|
+
preRun: [
|
|
2084
|
+
(m) => {
|
|
2085
|
+
if (!hasStreams(m)) return;
|
|
2086
|
+
const fs = m.FS;
|
|
2087
|
+
trackCloseOnExec(fs);
|
|
2088
|
+
try {
|
|
2089
|
+
fs.mkdirTree(init.cwd);
|
|
2090
|
+
fs.chdir(init.cwd);
|
|
2091
|
+
} catch {
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
],
|
|
2095
|
+
onRuntimeInitialized: () => ready()
|
|
2096
|
+
};
|
|
2097
|
+
(deps.evaluate ?? evaluateGlue)(init.program.glue, module);
|
|
2098
|
+
await initialized;
|
|
2099
|
+
const running = module;
|
|
2100
|
+
if (!hasStreams(running)) return runStdioOnly(running, init);
|
|
2101
|
+
const vfs = mountVfsIntoEmscripten(running.FS, {
|
|
2102
|
+
bridge: createSyncFsSabBridge(transport),
|
|
2103
|
+
cwd: init.cwd,
|
|
2104
|
+
warn: deps.warn ?? say(2)
|
|
2105
|
+
});
|
|
2106
|
+
const sigpipe = () => running.sliccSigpipe?.() === 1;
|
|
2107
|
+
const restartable = () => signals.restartable();
|
|
2108
|
+
const streams = new KernelStreams(running.FS, sys, { sigpipe, restartable });
|
|
2109
|
+
trackCloseOnExec(running.FS);
|
|
2110
|
+
if (init.fork) restoreForkedStreams(running.FS, streams, init.fork.streams ?? []);
|
|
2111
|
+
else {
|
|
2112
|
+
wireKernelStdio(running.FS, streams);
|
|
2113
|
+
for (const entry of init.fds ?? []) wireKernelFd(running.FS, streams, entry);
|
|
2114
|
+
}
|
|
2115
|
+
const pipefs = ownValue(running, "PIPEFS");
|
|
2116
|
+
if (pipefs) streams.usePipes(pipefs);
|
|
2117
|
+
streams.useControllingTerminal();
|
|
2118
|
+
useDevFd(running.FS);
|
|
2119
|
+
ownByRealmUser(running.FS);
|
|
2120
|
+
const livePath = (s) => liveNodePath(s.node);
|
|
2121
|
+
running.sliccKernel = createProcessKernel({
|
|
2122
|
+
transport,
|
|
2123
|
+
Fs: running.FS,
|
|
2124
|
+
env: init.env,
|
|
2125
|
+
beforeSpawn: () => vfs.flush(),
|
|
2126
|
+
afterChild: () => vfs.invalidate(),
|
|
2127
|
+
pid: init.pid,
|
|
2128
|
+
raise: (sig) => running.sliccRaise?.(sig),
|
|
2129
|
+
restartable,
|
|
2130
|
+
describeFork: () => describeForFork(running.FS, sys, streams, livePath),
|
|
2131
|
+
inherit: (actions) => describeInherited(running.FS, sys, streams, livePath, actions),
|
|
2132
|
+
stdioPromoter: () => vfsPromoter(running.FS, sys, streams, livePath)
|
|
2133
|
+
});
|
|
2134
|
+
try {
|
|
2135
|
+
return runMain(running, init);
|
|
2136
|
+
} finally {
|
|
2137
|
+
vfs.flush();
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
function hasStreams(module) {
|
|
2141
|
+
return typeof ownValue(module, "FS")?.getStream === "function";
|
|
2142
|
+
}
|
|
2143
|
+
function runStdioOnly(running, init) {
|
|
2144
|
+
if (init.fork) throw new Error(`${init.argv0} cannot resume a fork: it has no filesystem`);
|
|
2145
|
+
return runMain(running, init);
|
|
2146
|
+
}
|
|
2147
|
+
function runMain(running, init) {
|
|
2148
|
+
try {
|
|
2149
|
+
if (init.fork) {
|
|
2150
|
+
if (!running.sliccForkChild) throw new Error(`${init.argv0} cannot resume a fork`);
|
|
2151
|
+
return running.sliccForkChild({ ...init.fork, pid: init.pid }) ?? 0;
|
|
2152
|
+
}
|
|
2153
|
+
return (running.sliccRunMain ?? running.callMain)(init.args) ?? 0;
|
|
2154
|
+
} catch (e) {
|
|
2155
|
+
const status2 = e?.status;
|
|
2156
|
+
if (typeof status2 !== "number") throw e;
|
|
2157
|
+
return status2;
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
// src/process/process-entry.ts
|
|
2162
|
+
function processEntry(port, run = runWasmProcess) {
|
|
2163
|
+
return (data) => {
|
|
2164
|
+
if (data?.type !== WASM_PROCESS_INIT) return;
|
|
2165
|
+
run(data, port).then(
|
|
2166
|
+
(code) => port.postMessage({ type: WASM_PROCESS_EXIT, code }),
|
|
2167
|
+
(err) => port.postMessage({
|
|
2168
|
+
type: WASM_PROCESS_ERROR,
|
|
2169
|
+
message: err instanceof Error ? err.stack ?? err.message : String(err)
|
|
2170
|
+
})
|
|
2171
|
+
);
|
|
2172
|
+
};
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
// src/process-worker.ts
|
|
2176
|
+
var scope = globalThis;
|
|
2177
|
+
var onMessage = processEntry({ postMessage: (message) => scope.postMessage(message) });
|
|
2178
|
+
scope.addEventListener("message", (event) => onMessage(event.data));
|
|
2179
|
+
//# sourceMappingURL=process-worker.js.map
|