Skip to content

Commit 96bf98f

Browse files
committed
Eliminate multiple API entry points; simplify Node compatibility code.
I found out that you can just create a `Response` object, meaning you can make any blob work with `WebAssembly.compileStreaming`, and not only an actual `fetch` response; and that Node doesn't have a working `fetch` for file URLs but has a working `new Response()`. This allowed me to simplify this library considerably. This commit also parallelizes fetching of code and data.
1 parent 1cd88f2 commit 96bf98f

File tree

9 files changed

+60
-71
lines changed

9 files changed

+60
-71
lines changed

.eslintrc.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
22
"env": {
3+
"es2021": true,
34
"browser": true,
4-
"es2021": true
5+
"node": true
56
},
67
"extends": "eslint:recommended",
78
"parserOptions": {

.github/workflows/package.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ jobs:
5050
release:
5151
needs: check
5252
runs-on: ubuntu-latest
53-
if: "contains(github.event.head_commit.message, 'autorelease') && github.event_name == 'push' && github.event.ref == 'refs/heads/develop'"
53+
if: contains(github.event.head_commit.message, 'autorelease') && github.event_name == 'push' && github.event.ref == 'refs/heads/develop'
5454
steps:
5555
- name: Check out source code
5656
uses: actions/checkout@v3

lib/api-browser.js

-13
This file was deleted.

lib/api-node.js

-14
This file was deleted.
+39-38
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,45 @@
1+
import fetch from './fetch.js';
12
import { Exit, Environment, directoryFromTree, directoryIntoTree } from './wasi-virt.js';
23
import { lineBuffered } from './util.js';
34

45
export { Exit } from './wasi-virt.js';
56

6-
export class BaseApplication {
7+
async function fetchObject(obj, fetchFn) {
8+
// Mutate the object being fetched, to avoid re-fetches within the same session.
9+
// Do this in parallel to avoid head-of-line blocking.
10+
const promises = [];
11+
for (const [key, value] of Object.entries(obj)) {
12+
if (typeof value === "string" || value instanceof Uint8Array) {
13+
promises.push(Promise.resolve([key, value]));
14+
} else if (value instanceof URL) {
15+
promises.push(fetchFn(value).then((fetched) => [key, fetched]));
16+
} else {
17+
promises.push(fetchObject(value, fetchFn).then((fetched) => [key, fetched]));
18+
}
19+
}
20+
for (const [key, value] of await Promise.all(promises))
21+
obj[key] = value;
22+
return obj;
23+
}
24+
25+
function fetchWebAssembly(url) {
26+
return fetch(url).then(WebAssembly.compileStreaming);
27+
}
28+
29+
function fetchUint8Array(url) {
30+
return fetch(url).then((resp) => resp.arrayBuffer()).then((buf) => new Uint8Array(buf));
31+
}
32+
33+
function fetchResources({ modules, filesystem }) {
34+
return Promise.all([
35+
fetchObject(modules, fetchWebAssembly),
36+
fetchObject(filesystem, fetchUint8Array)
37+
]).then(([modules, filesystem]) => {
38+
return { modules, filesystem };
39+
});
40+
}
41+
42+
export class Application {
743
constructor(resourceFileURL, instantiate, argv0) {
844
this.resourceFileURL = resourceFileURL;
945
this.resources = null;
@@ -14,10 +50,10 @@ export class BaseApplication {
1450
// The `printLine` option is deprecated and not documented but still accepted for compatibility.
1551
async run(args = null, files = {}, { stdout, stderr, decodeASCII = true, printLine } = {}) {
1652
if (this.resources === null)
17-
this.resources = await this._fetchResources();
53+
this.resources = await import(this.resourceFileURL).then(fetchResources);
1854

1955
if (args === null)
20-
return; // only fetch resources
56+
return; // prefetch resources, but do not run
2157

2258
const environment = new Environment();
2359
environment.args = [this.argv0].concat(args);
@@ -51,39 +87,4 @@ export class BaseApplication {
5187
return files;
5288
}
5389
}
54-
55-
async _fetchResources() {
56-
// Async import, to allow inlining some of the resources within resource file.
57-
const { modules, filesystem } = await import(this.resourceFileURL);
58-
return {
59-
modules: await this._fetchObject(modules, this._fetchWebAssembly),
60-
filesystem: await this._fetchObject(filesystem, this._fetchUint8Array),
61-
};
62-
}
63-
64-
async _fetchObject(obj, fetchFn) {
65-
// Mutate the object being fetched, to avoid re-fetches within the same session.
66-
// Do this in parallel to avoid head-of-line blocking.
67-
const promises = [];
68-
for (const [key, value] of Object.entries(obj)) {
69-
if (value instanceof URL) {
70-
promises.push(fetchFn(value).then((fetched) => [key, fetched]));
71-
} else if (typeof value === "string" || value instanceof Uint8Array) {
72-
promises.push(Promise.resolve([key, value]));
73-
} else {
74-
promises.push(this._fetchObject(value, fetchFn).then((fetched) => [key, fetched]));
75-
}
76-
}
77-
for (const [key, value] of await Promise.all(promises))
78-
obj[key] = value;
79-
return obj;
80-
}
81-
82-
async _fetchUint8Array(_url) {
83-
throw 'not implemented';
84-
}
85-
86-
async _fetchWebAssembly(_url) {
87-
throw 'not implemented';
88-
}
8990
}

lib/fetch.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Node v18.x doesn't have a usable `fetch()`.
2+
let fetch;
3+
if (typeof process === 'object' && typeof process.release === 'object' && process.release.name === 'node') {
4+
fetch = async function(url) {
5+
const { readFile } = await import('fs/promises');
6+
let contentType = 'application/octet-stream';
7+
if (url.pathname.endsWith('.wasm'))
8+
contentType = 'application/wasm';
9+
return new Response(await readFile(url), { headers: { "Content-Type": contentType } });
10+
};
11+
} else {
12+
fetch = globalThis.fetch;
13+
}
14+
15+
export default fetch;

lib/util.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export function lineBuffered(processLine: (line: string) => void): (bytes: Uint8Array) => void;
1+
export function lineBuffered(processLine: (line: string) => void): (bytes: Uint8Array) => void;

package-in.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@
1919
],
2020
"exports": {
2121
".": {
22-
"node": "./lib/api-node.js",
23-
"browser": "./lib/api-browser.js",
22+
"default": "./lib/api.js",
2423
"types": "./lib/api.d.ts"
2524
},
2625
"./util": {

test/yowasp_runtime_test/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"@yowasp/runtime": "file:../../"
88
},
99
"devDependencies": {
10-
"@bytecodealliance/jco": "^0.14.1"
10+
"@bytecodealliance/jco": "0.14.2"
1111
},
1212
"scripts": {
1313
"pack": "yowasp-pack-resources gen/resources.js gen share",

0 commit comments

Comments
 (0)