@daugasauron/dolly 0.1.4 → 0.1.5-rc.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/README.md +134 -14
- package/coi-serviceworker.js +28 -0
- package/dist/dolly-build-id.mjs +1 -1
- package/dist/dolly-dso-0.wasm +0 -0
- package/dist/dolly-image-build-id.mjs +1 -1
- package/dist/dolly-process-0.wasm +0 -0
- package/dist/dolly-process-abi.mjs +1 -1
- package/dist/dolly-process-dso.mjs +382 -53
- package/dist/dolly-process-gate-0.wasm +0 -0
- package/dist/dolly-process-worker.mjs +164 -35
- package/dist/dolly.wasm +0 -0
- package/host/dso/abi.mjs +3 -1
- package/host/dso/dso.mjs +1 -2
- package/host/http/abi.mjs +2 -2
- package/host/http/broker.mjs +18 -8
- package/host/http/http.mjs +0 -1
- package/host/http/policy.mjs +8 -4
- package/host/input/abi.mjs +47 -0
- package/host/modules.mjs +4 -3
- package/host/nena/abi.mjs +1 -1
- package/host/nena/nena.mjs +16 -18
- package/host/packages/service.mjs +35 -15
- package/host/packages/worker.mjs +21 -0
- package/host/runtime/abi.mjs +1 -0
- package/host/runtime/boot-files.mjs +8 -2
- package/host/runtime/grower.mjs +11 -8
- package/host/runtime/lease-bridge.mjs +8 -5
- package/host/runtime/module.json +1 -1
- package/host/runtime/runtime.mjs +37 -18
- package/host/snapshot/abi.mjs +7 -9
- package/host/snapshot/snapshot.mjs +14 -12
- package/host/snapshot/transport.mjs +4 -12
- package/host/snapshot/ui.mjs +17 -7
- package/host/stack/stack.mjs +1 -2
- package/host/threads/abi.mjs +1 -1
- package/host/threads/threads.mjs +2 -1
- package/package.json +1 -1
- package/src/browser.mjs +34 -37
- package/src/build-log.mjs +13 -2
- package/src/build-worker.mjs +182 -0
- package/src/custom-image.mjs +59 -0
- package/src/dollyfile-graph.mjs +127 -0
- package/src/dollyfile-view.mjs +335 -0
- package/src/image-artifact.mjs +248 -0
- package/src/image-build.mjs +89 -0
- package/src/image-builder.mjs +63 -0
- package/src/images.mjs +13 -2
- package/src/pack-store.mjs +141 -0
- package/src/packaged-snapshot.mjs +263 -91
- package/src/page-chords.mjs +4 -2
- package/src/process-constants.mjs +4 -2
- package/src/process-supervisor.mjs +209 -122
- package/src/runtime-worker.mjs +9 -10
- package/src/session-store.mjs +5 -7
- package/src/sha256.mjs +65 -0
- package/src/snapshot-records.mjs +135 -23
- package/src/static-asset.mjs +29 -2
- package/src/terminal-text.mjs +8 -5
- package/src/version.mjs +1 -1
- package/src/worker-image.mjs +3 -5
|
@@ -103,7 +103,7 @@ var DOLLY_PROCESS_SIZEOF = Object.freeze({
|
|
|
103
103
|
dolly_process_directory_request: 24,
|
|
104
104
|
dolly_process_stat_response: 88,
|
|
105
105
|
dolly_process_filesystem_stat_response: 96,
|
|
106
|
-
dolly_process_spawn_request:
|
|
106
|
+
dolly_process_spawn_request: 72,
|
|
107
107
|
dolly_process_fd_mapping: 8,
|
|
108
108
|
dolly_process_exec_state: 16,
|
|
109
109
|
dolly_process_exec_request: 48,
|
|
@@ -118,18 +118,24 @@ var DOLLY_PROCESS_SIZEOF = Object.freeze({
|
|
|
118
118
|
dolly_process_wait_response: 16,
|
|
119
119
|
dolly_process_signal_request: 8,
|
|
120
120
|
dolly_process_alarm: 16,
|
|
121
|
+
dolly_process_signal_dispositions: 8,
|
|
121
122
|
dolly_process_fd_seek_request: 16,
|
|
122
123
|
dolly_process_clock_request: 16,
|
|
123
124
|
dolly_process_path_request: 16
|
|
124
125
|
});
|
|
125
126
|
|
|
126
127
|
// host/threads/abi.mjs
|
|
128
|
+
var DOLLY_THREAD_SPAWN = 144;
|
|
127
129
|
var DOLLY_THREAD_EXIT = 146;
|
|
128
130
|
var DOLLY_ERRNO2 = Object.freeze({ E2BIG: 1, EACCES: 2, EADDRINUSE: 3, EADDRNOTAVAIL: 4, EAFNOSUPPORT: 5, EAGAIN: 6, EALREADY: 7, EBADF: 8, EBADMSG: 9, EBUSY: 10, ECANCELED: 11, ECHILD: 12, ECONNABORTED: 13, ECONNREFUSED: 14, ECONNRESET: 15, EDEADLK: 16, EDESTADDRREQ: 17, EDOM: 18, EDQUOT: 19, EEXIST: 20, EFAULT: 21, EFBIG: 22, EHOSTUNREACH: 23, EIDRM: 24, EILSEQ: 25, EINPROGRESS: 26, EINTR: 27, EINVAL: 28, EIO: 29, EISCONN: 30, EISDIR: 31, ELOOP: 32, EMFILE: 33, EMLINK: 34, EMSGSIZE: 35, EMULTIHOP: 36, ENAMETOOLONG: 37, ENETDOWN: 38, ENETRESET: 39, ENETUNREACH: 40, ENFILE: 41, ENOBUFS: 42, ENODEV: 43, ENOENT: 44, ENOEXEC: 45, ENOLCK: 46, ENOLINK: 47, ENOMEM: 48, ENOMSG: 49, ENOPROTOOPT: 50, ENOSPC: 51, ENOSYS: 52, ENOTCONN: 53, ENOTDIR: 54, ENOTEMPTY: 55, ENOTRECOVERABLE: 56, ENOTSOCK: 57, ENOTTY: 59, ENXIO: 60, EOVERFLOW: 61, EOWNERDEAD: 62, EPERM: 63, EPIPE: 64, EPROTO: 65, EPROTONOSUPPORT: 66, EPROTOTYPE: 67, ERANGE: 68, EROFS: 69, ESPIPE: 70, ESRCH: 71, ESTALE: 72, ETIMEDOUT: 73, ETXTBSY: 74, EXDEV: 75, ENODATA: 116, ENOTSUP: 138 });
|
|
129
131
|
|
|
130
132
|
// src/process-worker.mjs
|
|
131
133
|
var PROCESS_EXIT = Symbol("Dolly process exit");
|
|
132
134
|
var THREAD_EXIT = Symbol("Dolly thread exit");
|
|
135
|
+
var UNSTARTED = Symbol("Dolly thread ended before it started");
|
|
136
|
+
var deferredResult = -(1n << 63n);
|
|
137
|
+
var reservationRetryMilliseconds = 50;
|
|
138
|
+
var reservationPatienceMilliseconds = 5e3;
|
|
133
139
|
var configuration = await new Promise((resolve, reject) => {
|
|
134
140
|
const timeout = setTimeout(
|
|
135
141
|
() => reject(new Error("Dolly process configuration was not provided")),
|
|
@@ -142,19 +148,92 @@ var configuration = await new Promise((resolve, reject) => {
|
|
|
142
148
|
resolve(event.data);
|
|
143
149
|
});
|
|
144
150
|
});
|
|
145
|
-
if (!(configuration.module instanceof WebAssembly.Module) || !(configuration.
|
|
151
|
+
if (!(configuration.module instanceof WebAssembly.Module || configuration.module instanceof MessagePort) || !(configuration.gateModule instanceof WebAssembly.Module) || !(configuration.kernelMemory instanceof WebAssembly.Memory) || !(configuration.control instanceof SharedArrayBuffer) || !Array.isArray(configuration.local) || configuration.control.byteLength !== 32 || !Number.isFinite(configuration.clockOrigin) || !Number.isSafeInteger(configuration.mailbox) || configuration.mailbox <= 0 || typeof configuration.memory?.initial !== "bigint" || typeof configuration.memory?.maximum !== "bigint" || configuration.port !== void 0 && !(configuration.port instanceof MessagePort) || !Number.isInteger(configuration.pid) || configuration.pid <= 0) {
|
|
146
152
|
throw new Error("Dolly process received an invalid configuration");
|
|
147
153
|
}
|
|
148
154
|
var control = new Int32Array(configuration.control);
|
|
155
|
+
var mailbox = BigInt(configuration.mailbox);
|
|
156
|
+
var maximumBytes = configuration.memory.maximum * 65536n;
|
|
149
157
|
var clockOffset = performance.timeOrigin - configuration.clockOrigin;
|
|
150
158
|
var lastSignalCheck = -Infinity;
|
|
151
159
|
var threadResult;
|
|
160
|
+
var ending = null;
|
|
161
|
+
var ended = (how) => {
|
|
162
|
+
ending = how;
|
|
163
|
+
return deferredResult;
|
|
164
|
+
};
|
|
152
165
|
var instance;
|
|
166
|
+
var memory;
|
|
167
|
+
var gate;
|
|
153
168
|
var localModules = [];
|
|
169
|
+
var awaitHost = (wait) => wait();
|
|
170
|
+
var openChannels = [];
|
|
171
|
+
var receiverPatienceMilliseconds = 1e4;
|
|
172
|
+
function closeReceivedChannels() {
|
|
173
|
+
for (let index = openChannels.length; index--; ) {
|
|
174
|
+
const { port, received } = openChannels[index];
|
|
175
|
+
if (Atomics.load(received, 0)) {
|
|
176
|
+
port.close();
|
|
177
|
+
openChannels.splice(index, 1);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function reportThreadFinished() {
|
|
182
|
+
for (const { received } of openChannels) {
|
|
183
|
+
while (Atomics.load(received, 0) === 0 && Atomics.wait(received, 0, 0, receiverPatienceMilliseconds) !== "timed-out") ;
|
|
184
|
+
}
|
|
185
|
+
self.postMessage({ type: "thread-finished", pid: configuration.pid, tid: configuration.tid, result: threadResult });
|
|
186
|
+
}
|
|
187
|
+
async function processMemory() {
|
|
188
|
+
if (configuration.port) {
|
|
189
|
+
const ended2 = Atomics.waitAsync(control, 5, 0);
|
|
190
|
+
const { memory: memory2, received } = await new Promise((resolve, reject) => {
|
|
191
|
+
const timeout = setTimeout(() => reject(new Error("the process memory was not provided")), 1e4);
|
|
192
|
+
configuration.port.onmessage = ({ data }) => {
|
|
193
|
+
clearTimeout(timeout);
|
|
194
|
+
resolve(data ?? {});
|
|
195
|
+
};
|
|
196
|
+
Promise.resolve(ended2.value).then(() => {
|
|
197
|
+
clearTimeout(timeout);
|
|
198
|
+
reject(UNSTARTED);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
configuration.port.close();
|
|
202
|
+
if (!(memory2 instanceof WebAssembly.Memory) || !(received instanceof Int32Array)) {
|
|
203
|
+
throw new TypeError("Dolly thread received an invalid memory");
|
|
204
|
+
}
|
|
205
|
+
Atomics.store(received, 0, 1);
|
|
206
|
+
Atomics.notify(received, 0);
|
|
207
|
+
return memory2;
|
|
208
|
+
}
|
|
209
|
+
const { initial, maximum } = configuration.memory;
|
|
210
|
+
for (let waited = 0; ; waited += reservationRetryMilliseconds) {
|
|
211
|
+
try {
|
|
212
|
+
return new WebAssembly.Memory({ initial, maximum, shared: true, address: "i64" });
|
|
213
|
+
} catch (error) {
|
|
214
|
+
if (!(error instanceof RangeError) || waited >= reservationPatienceMilliseconds) throw error;
|
|
215
|
+
}
|
|
216
|
+
await new Promise((resolve) => setTimeout(resolve, reservationRetryMilliseconds));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async function programModule() {
|
|
220
|
+
const { module } = configuration;
|
|
221
|
+
if (module instanceof WebAssembly.Module) return module;
|
|
222
|
+
const received = await new Promise((resolve, reject) => {
|
|
223
|
+
const timeout = setTimeout(() => reject(new Error("the program's module was not provided")), 1e4);
|
|
224
|
+
module.onmessage = ({ data }) => {
|
|
225
|
+
clearTimeout(timeout);
|
|
226
|
+
resolve(data);
|
|
227
|
+
};
|
|
228
|
+
});
|
|
229
|
+
module.close();
|
|
230
|
+
if (!(received instanceof WebAssembly.Module)) throw new TypeError("Dolly process received an invalid module");
|
|
231
|
+
return received;
|
|
232
|
+
}
|
|
154
233
|
function packetRange(addressValue, sizeValue) {
|
|
155
234
|
const address = Number(addressValue);
|
|
156
235
|
const size = Number(sizeValue);
|
|
157
|
-
const length =
|
|
236
|
+
const length = memory.buffer.byteLength;
|
|
158
237
|
return !Number.isSafeInteger(address) || !Number.isSafeInteger(size) || address < 0 || size < 0 || address > length - size || size !== 0 && address === 0 ? null : { address, size };
|
|
159
238
|
}
|
|
160
239
|
function decodeResult() {
|
|
@@ -166,9 +245,29 @@ function clockResponse(clock, response, now) {
|
|
|
166
245
|
const nanoseconds = Math.round(
|
|
167
246
|
(clock === DOLLY_PROCESS_CLOCK_REALTIME ? Date.now() : now + clockOffset) * 1e6
|
|
168
247
|
);
|
|
169
|
-
new DataView(
|
|
248
|
+
new DataView(memory.buffer, response.address, 8).setBigUint64(0, BigInt(nanoseconds), true);
|
|
170
249
|
return 8n;
|
|
171
250
|
}
|
|
251
|
+
function copyRequest(request) {
|
|
252
|
+
if (Atomics.compareExchange(control, 4, 0, 1) !== 0) return false;
|
|
253
|
+
try {
|
|
254
|
+
gate.exports.request(BigInt(request.address), mailbox, BigInt(request.size));
|
|
255
|
+
} finally {
|
|
256
|
+
Atomics.store(control, 4, 0);
|
|
257
|
+
Atomics.notify(control, 4);
|
|
258
|
+
}
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
function reserveMaximum() {
|
|
262
|
+
const size = BigInt(memory.buffer.byteLength);
|
|
263
|
+
if (size >= maximumBytes) return true;
|
|
264
|
+
try {
|
|
265
|
+
memory.grow(configuration.memory.maximum - size / 65536n);
|
|
266
|
+
} catch {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
172
271
|
function call(operation, requestAddressValue, requestSizeValue, responseAddressValue, responseCapacityValue) {
|
|
173
272
|
operation >>>= 0;
|
|
174
273
|
const request = packetRange(requestAddressValue, requestSizeValue);
|
|
@@ -177,55 +276,83 @@ function call(operation, requestAddressValue, requestSizeValue, responseAddressV
|
|
|
177
276
|
for (const module of localModules) {
|
|
178
277
|
if (module.handles(operation)) return module.call(operation, request, response);
|
|
179
278
|
}
|
|
180
|
-
const exitingResult = operation === DOLLY_THREAD_EXIT && request.size === 8 ? new DataView(
|
|
279
|
+
const exitingResult = operation === DOLLY_THREAD_EXIT && request.size === 8 ? new DataView(memory.buffer, request.address, 8).getBigUint64(0, true) : void 0;
|
|
181
280
|
let clock;
|
|
182
281
|
if (operation === DOLLY_PROCESS_CLOCK_TIME && request.size === 16 && response.size >= 8 && response.size <= DOLLY_PROCESS_PACKET_LIMIT) {
|
|
183
|
-
const packet = new DataView(
|
|
282
|
+
const packet = new DataView(memory.buffer, request.address, 16);
|
|
184
283
|
const id = packet.getUint32(0, true);
|
|
185
284
|
if ((id === DOLLY_PROCESS_CLOCK_REALTIME || id === DOLLY_PROCESS_CLOCK_MONOTONIC) && packet.getUint32(4, true) === 0) clock = id;
|
|
186
285
|
const now = performance.now();
|
|
187
286
|
if (clock !== void 0 && now - lastSignalCheck < 1) return clockResponse(clock, response, now);
|
|
188
287
|
}
|
|
189
288
|
if (operation === DOLLY_PROCESS_INTERRUPT_POLL && request.size === 0 && response.size >= 4 && performance.now() - lastSignalCheck < 1) {
|
|
190
|
-
new DataView(
|
|
289
|
+
new DataView(memory.buffer, response.address, 4).setInt32(0, 0, true);
|
|
191
290
|
return 4n;
|
|
192
291
|
}
|
|
193
292
|
if (request.size > DOLLY_PROCESS_PACKET_LIMIT || response.size > DOLLY_PROCESS_PACKET_LIMIT) {
|
|
194
293
|
return -BigInt(DOLLY_ERRNO.E2BIG);
|
|
195
294
|
}
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
295
|
+
const memoryBytes = memory.buffer.byteLength;
|
|
296
|
+
let channel;
|
|
297
|
+
if (openChannels.length) closeReceivedChannels();
|
|
298
|
+
if (operation === DOLLY_THREAD_SPAWN && configuration.threaded) {
|
|
299
|
+
if (!reserveMaximum()) return -BigInt(DOLLY_ERRNO.EAGAIN);
|
|
300
|
+
channel = new MessageChannel();
|
|
301
|
+
}
|
|
302
|
+
const result = awaitHost(() => {
|
|
303
|
+
const sequence = Atomics.load(control, 0) % 2147483647 + 1;
|
|
304
|
+
Atomics.store(control, 0, sequence);
|
|
305
|
+
if (!copyRequest(request)) return deferredResult;
|
|
306
|
+
self.postMessage({
|
|
307
|
+
type: "syscall",
|
|
308
|
+
pid: configuration.pid,
|
|
309
|
+
tid: configuration.tid,
|
|
310
|
+
sequence,
|
|
311
|
+
operation,
|
|
312
|
+
requestAddress: request.address,
|
|
313
|
+
requestSize: request.size,
|
|
314
|
+
responseAddress: response.address,
|
|
315
|
+
responseCapacity: response.size,
|
|
316
|
+
memoryBytes,
|
|
317
|
+
port: channel?.port1
|
|
318
|
+
}, channel ? [channel.port1] : []);
|
|
319
|
+
for (; ; ) {
|
|
320
|
+
const observed = Atomics.load(control, 1);
|
|
321
|
+
if (observed === sequence) break;
|
|
322
|
+
Atomics.wait(control, 1, observed);
|
|
323
|
+
}
|
|
324
|
+
return decodeResult();
|
|
208
325
|
});
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
326
|
+
if (channel) {
|
|
327
|
+
if (result < 0n) channel.port2.close();
|
|
328
|
+
else {
|
|
329
|
+
const received = new Int32Array(new SharedArrayBuffer(4));
|
|
330
|
+
channel.port2.postMessage({ memory, received });
|
|
331
|
+
openChannels.push({ port: channel.port2, received });
|
|
332
|
+
}
|
|
213
333
|
}
|
|
214
|
-
|
|
215
|
-
if (result
|
|
334
|
+
if (result === deferredResult) return ended(PROCESS_EXIT);
|
|
335
|
+
if (result > 0n) gate.exports.response(mailbox, BigInt(response.address), result);
|
|
216
336
|
lastSignalCheck = result === -BigInt(DOLLY_ERRNO.EINTR) ? -Infinity : performance.now();
|
|
217
337
|
if (operation === DOLLY_THREAD_EXIT && result >= 0n) {
|
|
218
338
|
threadResult = exitingResult;
|
|
219
|
-
|
|
339
|
+
for (const module of localModules) module.leaving?.();
|
|
340
|
+
return ended(THREAD_EXIT);
|
|
220
341
|
}
|
|
221
342
|
if (clock !== void 0 && result === 8n) return clockResponse(clock, response, lastSignalCheck);
|
|
222
|
-
if (operation === DOLLY_PROCESS_EXIT && result >= 0n)
|
|
343
|
+
if (operation === DOLLY_PROCESS_EXIT && result >= 0n) return ended(PROCESS_EXIT);
|
|
223
344
|
return result;
|
|
224
345
|
}
|
|
225
346
|
try {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
347
|
+
memory = await processMemory();
|
|
348
|
+
if (configuration.port && Atomics.compareExchange(control, 5, 0, 1) !== 0) throw UNSTARTED;
|
|
349
|
+
gate = new WebAssembly.Instance(configuration.gateModule, {
|
|
350
|
+
process: { memory, call },
|
|
351
|
+
kernel: { memory: configuration.kernelMemory }
|
|
352
|
+
});
|
|
353
|
+
instance = await WebAssembly.instantiate(await programModule(), {
|
|
354
|
+
env: { memory },
|
|
355
|
+
dolly_process_0: { call: gate.exports.call }
|
|
229
356
|
});
|
|
230
357
|
if (typeof instance.exports._start !== "function") {
|
|
231
358
|
throw new TypeError("Dolly process does not export _start");
|
|
@@ -236,7 +363,7 @@ try {
|
|
|
236
363
|
return (await import(url)).serve({
|
|
237
364
|
...options,
|
|
238
365
|
instance,
|
|
239
|
-
memory
|
|
366
|
+
memory,
|
|
240
367
|
processInterface: configuration.processInterface,
|
|
241
368
|
threaded: configuration.threaded,
|
|
242
369
|
stack: { promisingEntry: configuration.promisingEntry, depths: configuration.stackDepths }
|
|
@@ -245,19 +372,21 @@ try {
|
|
|
245
372
|
URL.revokeObjectURL(url);
|
|
246
373
|
}
|
|
247
374
|
}));
|
|
375
|
+
awaitHost = localModules.find((module) => module.awaitHost)?.awaitHost ?? awaitHost;
|
|
248
376
|
self.postMessage({ type: "started", pid: configuration.pid, tid: configuration.tid });
|
|
249
377
|
const enter = configuration.promisingEntry ? WebAssembly.promising : (entry) => entry;
|
|
250
378
|
if (configuration.argument !== void 0) {
|
|
251
379
|
threadResult = await enter(instance.exports.dolly_thread_start)(configuration.tid, configuration.argument);
|
|
252
|
-
|
|
380
|
+
reportThreadFinished();
|
|
253
381
|
} else {
|
|
254
382
|
await enter(instance.exports._start)();
|
|
255
383
|
self.postMessage({ type: "finished", pid: configuration.pid, tid: configuration.tid });
|
|
256
384
|
}
|
|
257
385
|
} catch (caught) {
|
|
258
|
-
if (caught ===
|
|
259
|
-
|
|
260
|
-
|
|
386
|
+
if (caught === UNSTARTED) {
|
|
387
|
+
} else if (ending === THREAD_EXIT && configuration.threaded && typeof threadResult === "bigint") {
|
|
388
|
+
reportThreadFinished();
|
|
389
|
+
} else if (ending === PROCESS_EXIT) {
|
|
261
390
|
self.postMessage({ type: "finished", pid: configuration.pid, tid: configuration.tid });
|
|
262
391
|
} else {
|
|
263
392
|
await null;
|
package/dist/dolly.wasm
CHANGED
|
Binary file
|
package/host/dso/abi.mjs
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
export const DOLLY_DSO_OPEN = 112;
|
|
3
3
|
export const DOLLY_DSO_SYMBOL = 113;
|
|
4
4
|
export const DOLLY_DSO_CLOSE = 114;
|
|
5
|
+
export const DOLLY_DSO_WAIT = 115;
|
|
5
6
|
export const DOLLY_DSO_GLOBAL = 1;
|
|
6
7
|
export const DOLLY_DSO_LIMIT = 536870912;
|
|
7
8
|
export const DOLLY_DSO_ERROR_CAPACITY = 240;
|
|
9
|
+
export const DOLLY_DSO_THREADS_SIZE = 272;
|
|
8
10
|
export const DOLLY_FFI_CALL = 120;
|
|
9
11
|
export const DOLLY_FFI_CLOSURE_ALLOC = 121;
|
|
10
12
|
export const DOLLY_FFI_CLOSURE_FREE = 122;
|
|
11
13
|
export const DOLLY_FFI_CLOSURE_PREP = 123;
|
|
12
14
|
export const DOLLY_ERRNO = Object.freeze({ E2BIG: 1, EACCES: 2, EADDRINUSE: 3, EADDRNOTAVAIL: 4, EAFNOSUPPORT: 5, EAGAIN: 6, EALREADY: 7, EBADF: 8, EBADMSG: 9, EBUSY: 10, ECANCELED: 11, ECHILD: 12, ECONNABORTED: 13, ECONNREFUSED: 14, ECONNRESET: 15, EDEADLK: 16, EDESTADDRREQ: 17, EDOM: 18, EDQUOT: 19, EEXIST: 20, EFAULT: 21, EFBIG: 22, EHOSTUNREACH: 23, EIDRM: 24, EILSEQ: 25, EINPROGRESS: 26, EINTR: 27, EINVAL: 28, EIO: 29, EISCONN: 30, EISDIR: 31, ELOOP: 32, EMFILE: 33, EMLINK: 34, EMSGSIZE: 35, EMULTIHOP: 36, ENAMETOOLONG: 37, ENETDOWN: 38, ENETRESET: 39, ENETUNREACH: 40, ENFILE: 41, ENOBUFS: 42, ENODEV: 43, ENOENT: 44, ENOEXEC: 45, ENOLCK: 46, ENOLINK: 47, ENOMEM: 48, ENOMSG: 49, ENOPROTOOPT: 50, ENOSPC: 51, ENOSYS: 52, ENOTCONN: 53, ENOTDIR: 54, ENOTEMPTY: 55, ENOTRECOVERABLE: 56, ENOTSOCK: 57, ENOTTY: 59, ENXIO: 60, EOVERFLOW: 61, EOWNERDEAD: 62, EPERM: 63, EPIPE: 64, EPROTO: 65, EPROTONOSUPPORT: 66, EPROTOTYPE: 67, ERANGE: 68, EROFS: 69, ESPIPE: 70, ESRCH: 71, ESTALE: 72, ETIMEDOUT: 73, ETXTBSY: 74, EXDEV: 75, ENODATA: 116, ENOTSUP: 138 });
|
|
13
|
-
export const DOLLY_DSO_ABI_DIGEST = "
|
|
15
|
+
export const DOLLY_DSO_ABI_DIGEST = "6758333327df36a58f1dea5e0741eca482bb92cbede2b525a67a5e5f85bb35fd";
|
package/host/dso/dso.mjs
CHANGED
|
@@ -17,9 +17,8 @@ export function worker({ get, abi, runtimeBase }) {
|
|
|
17
17
|
}));
|
|
18
18
|
// abi is the live set of modules the image admits; a loaded library's
|
|
19
19
|
// own records are checked against it.
|
|
20
|
-
// threads: it serves each thread's Worker of a program using threads@0.
|
|
21
20
|
get("runtime").serveInProcess("dso@0", { bundle: new Blob([bundle], { type: "text/javascript" }),
|
|
22
|
-
configuration: { contract: parseWasmInterface(contract, "dolly-dso-0"), hostAbi: abi }
|
|
21
|
+
configuration: { contract: parseWasmInterface(contract, "dolly-dso-0"), hostAbi: abi } });
|
|
23
22
|
},
|
|
24
23
|
disabled() { get("runtime").serveInProcess("dso@0"); },
|
|
25
24
|
};
|
package/host/http/abi.mjs
CHANGED
|
@@ -4,7 +4,7 @@ export const DOLLY_HTTP_CHUNK_CAPACITY = 65536;
|
|
|
4
4
|
export const DOLLY_HTTP_MAX_METHOD = 32;
|
|
5
5
|
export const DOLLY_HTTP_MAX_URL = 8192;
|
|
6
6
|
export const DOLLY_HTTP_MAX_HEADERS = 65536;
|
|
7
|
-
export const DOLLY_HTTP_MAX_BODY =
|
|
7
|
+
export const DOLLY_HTTP_MAX_BODY = 67108864;
|
|
8
8
|
export const DOLLY_HTTP_HEADER_SIZE = 64;
|
|
9
9
|
export const DOLLY_HTTP_WORD_STATE = 0;
|
|
10
10
|
export const DOLLY_HTTP_WORD_SEQUENCE = 1;
|
|
@@ -25,4 +25,4 @@ export const DOLLY_HTTP_POLL = 81;
|
|
|
25
25
|
export const DOLLY_HTTP_CANCEL = 82;
|
|
26
26
|
export const DOLLY_HTTP_BODY_WRITE = 83;
|
|
27
27
|
export const DOLLY_ERRNO = Object.freeze({ E2BIG: 1, EACCES: 2, EADDRINUSE: 3, EADDRNOTAVAIL: 4, EAFNOSUPPORT: 5, EAGAIN: 6, EALREADY: 7, EBADF: 8, EBADMSG: 9, EBUSY: 10, ECANCELED: 11, ECHILD: 12, ECONNABORTED: 13, ECONNREFUSED: 14, ECONNRESET: 15, EDEADLK: 16, EDESTADDRREQ: 17, EDOM: 18, EDQUOT: 19, EEXIST: 20, EFAULT: 21, EFBIG: 22, EHOSTUNREACH: 23, EIDRM: 24, EILSEQ: 25, EINPROGRESS: 26, EINTR: 27, EINVAL: 28, EIO: 29, EISCONN: 30, EISDIR: 31, ELOOP: 32, EMFILE: 33, EMLINK: 34, EMSGSIZE: 35, EMULTIHOP: 36, ENAMETOOLONG: 37, ENETDOWN: 38, ENETRESET: 39, ENETUNREACH: 40, ENFILE: 41, ENOBUFS: 42, ENODEV: 43, ENOENT: 44, ENOEXEC: 45, ENOLCK: 46, ENOLINK: 47, ENOMEM: 48, ENOMSG: 49, ENOPROTOOPT: 50, ENOSPC: 51, ENOSYS: 52, ENOTCONN: 53, ENOTDIR: 54, ENOTEMPTY: 55, ENOTRECOVERABLE: 56, ENOTSOCK: 57, ENOTTY: 59, ENXIO: 60, EOVERFLOW: 61, EOWNERDEAD: 62, EPERM: 63, EPIPE: 64, EPROTO: 65, EPROTONOSUPPORT: 66, EPROTOTYPE: 67, ERANGE: 68, EROFS: 69, ESPIPE: 70, ESRCH: 71, ESTALE: 72, ETIMEDOUT: 73, ETXTBSY: 74, EXDEV: 75, ENODATA: 116, ENOTSUP: 138 });
|
|
28
|
-
export const DOLLY_HTTP_ABI_DIGEST = "
|
|
28
|
+
export const DOLLY_HTTP_ABI_DIGEST = "9e3dd4f36f656a23f477c67ce6fe723130caa9b4bf69f6af8670859ea653470f";
|
package/host/http/broker.mjs
CHANGED
|
@@ -218,7 +218,19 @@ class HttpTransfer {
|
|
|
218
218
|
check() {
|
|
219
219
|
if (this.terminalError) throw new HttpError(this.terminalError, "HTTP transfer ended");
|
|
220
220
|
if (this.controller.signal.aborted || performance.now() >= this.deadline)
|
|
221
|
-
throw new HttpError(errno.ETIMEDOUT, "HTTP
|
|
221
|
+
throw new HttpError(errno.ETIMEDOUT, "HTTP request timed out");
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// A transfer lasts as long as it moves. It times out when it has waited the
|
|
225
|
+
// rule's timeout for its next record: the response's headers, its next bytes
|
|
226
|
+
// or the guest's reading of the record before.
|
|
227
|
+
moved() {
|
|
228
|
+
clearTimeout(this.timer);
|
|
229
|
+
this.deadline = performance.now() + this.timeout;
|
|
230
|
+
this.timer = setTimeout(() => {
|
|
231
|
+
this.fail(errno.ETIMEDOUT);
|
|
232
|
+
this.controller.abort();
|
|
233
|
+
}, this.timeout);
|
|
222
234
|
}
|
|
223
235
|
|
|
224
236
|
// The mailbox ends the slot's transfer once, and only while the guest's
|
|
@@ -242,10 +254,11 @@ class HttpTransfer {
|
|
|
242
254
|
const error = await this.broker.mailbox.publish({ index: this.index, transfer: this.id, sequence: this.sequence,
|
|
243
255
|
bytes: bytes.slice(), status, eof, kind, deadline: now() + this.deadline - performance.now() });
|
|
244
256
|
if (error) throw new HttpError(error, "HTTP transfer ended");
|
|
257
|
+
this.moved();
|
|
245
258
|
}
|
|
246
259
|
|
|
247
260
|
async run({ method, url, headers: headerBlock, body, flags }) {
|
|
248
|
-
let
|
|
261
|
+
let failure = errno.EINVAL;
|
|
249
262
|
try {
|
|
250
263
|
this.check();
|
|
251
264
|
const target = this.broker.target(url);
|
|
@@ -266,11 +279,8 @@ class HttpTransfer {
|
|
|
266
279
|
stripDollyBrowserOwnedHeaders(headers);
|
|
267
280
|
const upperMethod = method.toUpperCase();
|
|
268
281
|
const rule = this.broker.policy.authorize(target, upperMethod, headers, body?.byteLength ?? 0);
|
|
269
|
-
this.
|
|
270
|
-
|
|
271
|
-
this.fail(errno.ETIMEDOUT);
|
|
272
|
-
this.controller.abort();
|
|
273
|
-
}, rule.timeoutMilliseconds);
|
|
282
|
+
this.timeout = rule.timeoutMilliseconds;
|
|
283
|
+
this.moved();
|
|
274
284
|
failure = errno.EIO;
|
|
275
285
|
const init = {
|
|
276
286
|
method: upperMethod, headers,
|
|
@@ -318,7 +328,7 @@ class HttpTransfer {
|
|
|
318
328
|
this.fail(error instanceof HttpError ? error.errno :
|
|
319
329
|
this.controller.signal.aborted ? errno.ETIMEDOUT : failure);
|
|
320
330
|
} finally {
|
|
321
|
-
clearTimeout(
|
|
331
|
+
clearTimeout(this.timer);
|
|
322
332
|
this.controller.abort();
|
|
323
333
|
}
|
|
324
334
|
}
|
package/host/http/http.mjs
CHANGED
|
@@ -12,7 +12,6 @@ export function browser({ applicationBase, configuration, send, ask }) {
|
|
|
12
12
|
const services = {};
|
|
13
13
|
const instance = {
|
|
14
14
|
services,
|
|
15
|
-
get transport() { return transport; },
|
|
16
15
|
page: {
|
|
17
16
|
get httpActive() { return transport?.active ?? false; },
|
|
18
17
|
get httpRequestCount() { return transport?.requestCount ?? 0; },
|
package/host/http/policy.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DOLLY_ERRNO } from "./abi.mjs";
|
|
1
|
+
import { DOLLY_ERRNO, DOLLY_HTTP_MAX_BODY } from "./abi.mjs";
|
|
2
2
|
|
|
3
3
|
export class HttpError extends Error {
|
|
4
4
|
constructor(errno, message) { super(message); this.errno = errno; }
|
|
@@ -38,14 +38,17 @@ export function stripDollyBrowserOwnedHeaders(headers) {
|
|
|
38
38
|
|
|
39
39
|
const defaultLimits = Object.freeze({
|
|
40
40
|
maxRequests: 256,
|
|
41
|
-
maxRequestBytes:
|
|
41
|
+
maxRequestBytes: DOLLY_HTTP_MAX_BODY,
|
|
42
42
|
maxResponseBytes: Infinity,
|
|
43
43
|
timeoutMilliseconds: 600_000,
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
// Timers wait at most this long; a longer timeout would fire early.
|
|
47
|
+
const longestTimeout = 2 ** 31 - 1;
|
|
48
|
+
|
|
49
|
+
function positiveInteger(value, fallback, name, maximum = Number.MAX_SAFE_INTEGER) {
|
|
47
50
|
const result = value ?? fallback;
|
|
48
|
-
if (!Number.isSafeInteger(result) || result <= 0) {
|
|
51
|
+
if (!Number.isSafeInteger(result) || result <= 0 || result > maximum) {
|
|
49
52
|
throw new TypeError(`invalid Dolly HTTP policy ${name}`);
|
|
50
53
|
}
|
|
51
54
|
return result;
|
|
@@ -105,6 +108,7 @@ function normalizeRule(rule) {
|
|
|
105
108
|
rule.timeoutMilliseconds,
|
|
106
109
|
defaultLimits.timeoutMilliseconds,
|
|
107
110
|
"timeoutMilliseconds",
|
|
111
|
+
longestTimeout,
|
|
108
112
|
),
|
|
109
113
|
});
|
|
110
114
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Generated from host/input/dolly-input-0.wat, host/input/input.h, include/dolly/process.h.
|
|
2
|
+
export const DOLLY_INPUT_HEADER_SIZE = 28;
|
|
3
|
+
export const DOLLY_INPUT_EVENT_SIZE = 128;
|
|
4
|
+
export const DOLLY_INPUT_EVENT_DATA_SIZE = 88;
|
|
5
|
+
export const DOLLY_INPUT_EVENT_CAPACITY = 256;
|
|
6
|
+
export const DOLLY_INPUT_WORD_EVENT_READ = 0;
|
|
7
|
+
export const DOLLY_INPUT_WORD_EVENT_WRITE = 1;
|
|
8
|
+
export const DOLLY_INPUT_WORD_FLAGS = 2;
|
|
9
|
+
export const DOLLY_INPUT_WORD_PASTE_SEQUENCE = 3;
|
|
10
|
+
export const DOLLY_INPUT_WORD_PASTE_CONSUMED_SEQUENCE = 4;
|
|
11
|
+
export const DOLLY_INPUT_WORD_PASTE_LENGTH = 5;
|
|
12
|
+
export const DOLLY_INPUT_WORD_ENABLED = 6;
|
|
13
|
+
export const DOLLY_INPUT_LEASED = 1;
|
|
14
|
+
export const DOLLY_INPUT_POINTER_RELATIVE = 2;
|
|
15
|
+
export const DOLLY_INPUT_EVENT_KEY = 1;
|
|
16
|
+
export const DOLLY_INPUT_EVENT_TEXT = 2;
|
|
17
|
+
export const DOLLY_INPUT_EVENT_FOCUS = 4;
|
|
18
|
+
export const DOLLY_INPUT_EVENT_PASTE = 5;
|
|
19
|
+
export const DOLLY_INPUT_EVENT_POINTER = 6;
|
|
20
|
+
export const DOLLY_INPUT_EVENT_SCROLL = 7;
|
|
21
|
+
export const DOLLY_INPUT_EVENT_POINTER_MOTION = 8;
|
|
22
|
+
export const DOLLY_INPUT_EVENT_POINTER_CAPTURE = 9;
|
|
23
|
+
export const DOLLY_INPUT_EVENT_POINTER_PRESENCE = 10;
|
|
24
|
+
export const DOLLY_INPUT_EVENT_DROPPED = 11;
|
|
25
|
+
export const DOLLY_KEY_ACTION_RELEASE = 0;
|
|
26
|
+
export const DOLLY_KEY_ACTION_PRESS = 1;
|
|
27
|
+
export const DOLLY_KEY_ACTION_REPEAT = 2;
|
|
28
|
+
export const DOLLY_POINTER_ACTION_RELEASE = 0;
|
|
29
|
+
export const DOLLY_POINTER_ACTION_PRESS = 1;
|
|
30
|
+
export const DOLLY_POINTER_ACTION_DRAG = 2;
|
|
31
|
+
export const DOLLY_SCROLL_UNIT_PIXEL = 0;
|
|
32
|
+
export const DOLLY_SCROLL_UNIT_LINE = 1;
|
|
33
|
+
export const DOLLY_SCROLL_UNIT_PAGE = 2;
|
|
34
|
+
export const DOLLY_INPUT_MOD_SHIFT = 1;
|
|
35
|
+
export const DOLLY_INPUT_MOD_CONTROL = 2;
|
|
36
|
+
export const DOLLY_INPUT_MOD_ALT = 4;
|
|
37
|
+
export const DOLLY_INPUT_MOD_META = 8;
|
|
38
|
+
export const DOLLY_INPUT_MOD_CAPS_LOCK = 16;
|
|
39
|
+
export const DOLLY_INPUT_MOD_NUM_LOCK = 32;
|
|
40
|
+
export const DOLLY_INPUT_FLAG_COMPOSING = 1;
|
|
41
|
+
export const DOLLY_INPUT_PASTE_CAPACITY = 262144;
|
|
42
|
+
export const DOLLY_INPUT_ACQUIRE = 88;
|
|
43
|
+
export const DOLLY_INPUT_NEXT_EVENT = 89;
|
|
44
|
+
export const DOLLY_INPUT_SET_POINTER = 90;
|
|
45
|
+
export const DOLLY_INPUT_RELEASE = 91;
|
|
46
|
+
export const DOLLY_ERRNO = Object.freeze({ E2BIG: 1, EACCES: 2, EADDRINUSE: 3, EADDRNOTAVAIL: 4, EAFNOSUPPORT: 5, EAGAIN: 6, EALREADY: 7, EBADF: 8, EBADMSG: 9, EBUSY: 10, ECANCELED: 11, ECHILD: 12, ECONNABORTED: 13, ECONNREFUSED: 14, ECONNRESET: 15, EDEADLK: 16, EDESTADDRREQ: 17, EDOM: 18, EDQUOT: 19, EEXIST: 20, EFAULT: 21, EFBIG: 22, EHOSTUNREACH: 23, EIDRM: 24, EILSEQ: 25, EINPROGRESS: 26, EINTR: 27, EINVAL: 28, EIO: 29, EISCONN: 30, EISDIR: 31, ELOOP: 32, EMFILE: 33, EMLINK: 34, EMSGSIZE: 35, EMULTIHOP: 36, ENAMETOOLONG: 37, ENETDOWN: 38, ENETRESET: 39, ENETUNREACH: 40, ENFILE: 41, ENOBUFS: 42, ENODEV: 43, ENOENT: 44, ENOEXEC: 45, ENOLCK: 46, ENOLINK: 47, ENOMEM: 48, ENOMSG: 49, ENOPROTOOPT: 50, ENOSPC: 51, ENOSYS: 52, ENOTCONN: 53, ENOTDIR: 54, ENOTEMPTY: 55, ENOTRECOVERABLE: 56, ENOTSOCK: 57, ENOTTY: 59, ENXIO: 60, EOVERFLOW: 61, EOWNERDEAD: 62, EPERM: 63, EPIPE: 64, EPROTO: 65, EPROTONOSUPPORT: 66, EPROTOTYPE: 67, ERANGE: 68, EROFS: 69, ESPIPE: 70, ESRCH: 71, ESTALE: 72, ETIMEDOUT: 73, ETXTBSY: 74, EXDEV: 75, ENODATA: 116, ENOTSUP: 138 });
|
|
47
|
+
export const DOLLY_INPUT_ABI_DIGEST = "862edb42d7dbb04c1c49411d740a97503fe387678a1b660f19b3b2dc4f18b9c6";
|
package/host/modules.mjs
CHANGED
|
@@ -2,9 +2,10 @@ import { hostRequirement, hostRequirements } from "./requirements.mjs";
|
|
|
2
2
|
import { DOLLY_ERRNO as E } from "../src/process-constants.mjs";
|
|
3
3
|
|
|
4
4
|
// What a build enables beside the runtime its image declares: the broker the
|
|
5
|
-
// engine fetches sources through, the threads its toolchain uses
|
|
6
|
-
// modules its compilers and interpreters load
|
|
7
|
-
|
|
5
|
+
// engine fetches sources through, the threads its toolchain uses, the
|
|
6
|
+
// modules its compilers and interpreters load and the fresh stacks of the
|
|
7
|
+
// tools stack-wrap rewrote.
|
|
8
|
+
export const buildHost = Object.freeze(["http@0", "threads@0", "dso@0", "stack@0"]);
|
|
8
9
|
|
|
9
10
|
// A page's host modules, from the URLs of their manifests. The embedding, trusted
|
|
10
11
|
// code, names them; images select no JS or Worker URLs, and each module's provider
|
package/host/nena/abi.mjs
CHANGED
|
@@ -16,4 +16,4 @@ export const DOLLY_NENA_ENABLED = 1;
|
|
|
16
16
|
export const DOLLY_NENA_DECLARED = 2;
|
|
17
17
|
export const DOLLY_NENA_FIXED = 4;
|
|
18
18
|
export const DOLLY_ERRNO = Object.freeze({ E2BIG: 1, EACCES: 2, EADDRINUSE: 3, EADDRNOTAVAIL: 4, EAFNOSUPPORT: 5, EAGAIN: 6, EALREADY: 7, EBADF: 8, EBADMSG: 9, EBUSY: 10, ECANCELED: 11, ECHILD: 12, ECONNABORTED: 13, ECONNREFUSED: 14, ECONNRESET: 15, EDEADLK: 16, EDESTADDRREQ: 17, EDOM: 18, EDQUOT: 19, EEXIST: 20, EFAULT: 21, EFBIG: 22, EHOSTUNREACH: 23, EIDRM: 24, EILSEQ: 25, EINPROGRESS: 26, EINTR: 27, EINVAL: 28, EIO: 29, EISCONN: 30, EISDIR: 31, ELOOP: 32, EMFILE: 33, EMLINK: 34, EMSGSIZE: 35, EMULTIHOP: 36, ENAMETOOLONG: 37, ENETDOWN: 38, ENETRESET: 39, ENETUNREACH: 40, ENFILE: 41, ENOBUFS: 42, ENODEV: 43, ENOENT: 44, ENOEXEC: 45, ENOLCK: 46, ENOLINK: 47, ENOMEM: 48, ENOMSG: 49, ENOPROTOOPT: 50, ENOSPC: 51, ENOSYS: 52, ENOTCONN: 53, ENOTDIR: 54, ENOTEMPTY: 55, ENOTRECOVERABLE: 56, ENOTSOCK: 57, ENOTTY: 59, ENXIO: 60, EOVERFLOW: 61, EOWNERDEAD: 62, EPERM: 63, EPIPE: 64, EPROTO: 65, EPROTONOSUPPORT: 66, EPROTOTYPE: 67, ERANGE: 68, EROFS: 69, ESPIPE: 70, ESRCH: 71, ESTALE: 72, ETIMEDOUT: 73, ETXTBSY: 74, EXDEV: 75, ENODATA: 116, ENOTSUP: 138 });
|
|
19
|
-
export const DOLLY_NENA_ABI_DIGEST = "
|
|
19
|
+
export const DOLLY_NENA_ABI_DIGEST = "cc920703d81c6e25656402683fc32180bd232f9206d5fbffabdca4c9cb24649a";
|
package/host/nena/nena.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { DOLLY_ERRNO as E } from "./abi.mjs";
|
|
|
5
5
|
export { DOLLY_NENA_ABI_DIGEST as digest } from "./abi.mjs";
|
|
6
6
|
|
|
7
7
|
// The device as the lease bridge sees it.
|
|
8
|
-
export const lease = { type: "nena", slots: N.DOLLY_NENA_SLOTS, packetBytes: N.DOLLY_NENA_PACKET_BYTES,
|
|
8
|
+
export const lease = { type: "nena", version: N.DOLLY_NENA_VERSION, slots: N.DOLLY_NENA_SLOTS, packetBytes: N.DOLLY_NENA_PACKET_BYTES,
|
|
9
9
|
replyBytes: N.DOLLY_NENA_REPLY_BYTES, open: N.DOLLY_NENA_OPEN, close: N.DOLLY_NENA_CLOSE };
|
|
10
10
|
// The session's own record of the modules it enabled, one name@version a line.
|
|
11
11
|
// The Worker writes it after every switch and the page enables what it lists
|
|
@@ -36,32 +36,30 @@ export function browser({ send, modules, showStatus }) {
|
|
|
36
36
|
}
|
|
37
37
|
// What the session enabled beyond what the page started with: its record.
|
|
38
38
|
const enabledSince = () => modules.list().filter(module => module.enabled && !module.declared).map(requirement);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
!scope || view.getUint32(24, true) !== packet.length - 32 || view.getUint32(28, true)) fail(E.EINVAL);
|
|
45
|
-
const sized = bytes => { if (packet.length !== bytes) fail(E.EINVAL); };
|
|
39
|
+
// A request the lease bridge checked: the packet's operation and scope
|
|
40
|
+
// (dolly_device_header) and its body.
|
|
41
|
+
async function dispatch({ operation, scope, body }) {
|
|
42
|
+
const view = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
43
|
+
const sized = bytes => { if (body.length !== bytes) fail(E.EINVAL); };
|
|
46
44
|
if (operation === N.DOLLY_NENA_OPEN) {
|
|
47
|
-
sized(
|
|
45
|
+
sized(0);
|
|
48
46
|
const reply = new Uint8Array(8);
|
|
49
47
|
new DataView(reply.buffer).setBigUint64(0, BigInt(scope), true);
|
|
50
48
|
return { bytes: reply };
|
|
51
49
|
}
|
|
52
|
-
if (operation === N.DOLLY_NENA_CLOSE) { sized(
|
|
50
|
+
if (operation === N.DOLLY_NENA_CLOSE) { sized(0); return { bytes: new Uint8Array() }; }
|
|
53
51
|
if (operation === N.DOLLY_NENA_MODULE) {
|
|
54
|
-
sized(
|
|
55
|
-
if (view.getUint32(
|
|
56
|
-
return { bytes: moduleRecord(modules.list()[view.getUint32(
|
|
52
|
+
sized(8);
|
|
53
|
+
if (view.getUint32(4, true)) fail(E.EINVAL);
|
|
54
|
+
return { bytes: moduleRecord(modules.list()[view.getUint32(0, true)] ?? fail(E.ENOENT)) };
|
|
57
55
|
}
|
|
58
56
|
if (operation !== N.DOLLY_NENA_ENABLE && operation !== N.DOLLY_NENA_DISABLE) fail(E.EINVAL);
|
|
59
|
-
sized(
|
|
60
|
-
const field =
|
|
61
|
-
if (end < 1 || field.subarray(end).some(byte => byte) || view.getUint32(
|
|
57
|
+
sized(40);
|
|
58
|
+
const field = body.subarray(0, N.DOLLY_NENA_NAME_BYTES), end = field.indexOf(0);
|
|
59
|
+
if (end < 1 || field.subarray(end).some(byte => byte) || view.getUint32(36, true)) fail(E.EINVAL);
|
|
62
60
|
let name;
|
|
63
61
|
try { name = decoder.decode(field.subarray(0, end)); } catch { fail(E.EINVAL); }
|
|
64
|
-
const version = view.getUint32(
|
|
62
|
+
const version = view.getUint32(32, true), enabling = operation === N.DOLLY_NENA_ENABLE;
|
|
65
63
|
const found = () => modules.list().find(module => module.name === name && module.version === version) ?? fail(E.ENOENT);
|
|
66
64
|
const value = requirement(found());
|
|
67
65
|
// The session gives the privilege up: answered first, since the answer
|
|
@@ -74,7 +72,7 @@ export function browser({ send, modules, showStatus }) {
|
|
|
74
72
|
messages: {
|
|
75
73
|
async "nena-request"(message) {
|
|
76
74
|
let result;
|
|
77
|
-
try { result = await dispatch(message
|
|
75
|
+
try { result = await dispatch(message); }
|
|
78
76
|
catch (error) { result = { error: error.errno ?? E.EIO, bytes: new Uint8Array() }; }
|
|
79
77
|
if (result.enabled) send({ type: "nena-record", enabled: result.enabled });
|
|
80
78
|
send({ type: "nena-complete", scope: message.scope, sequence: message.sequence, error: result.error ?? 0,
|