Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 61 additions & 12 deletions cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,54 @@ import { utf8 } from "../util/text.js";
import * as optionsUtil from "../util/options.js";
import * as generated from "./index.generated.js";

import binaryen from "../lib/binaryen.js";
import * as assemblyscriptJS from "assemblyscript";

// Use the TS->JS variant by default
let assemblyscript = assemblyscriptJS;

// Use the AS->Wasm variant as an option (experimental)
const wasmPos = process.argv.indexOf("--wasm");
if (~wasmPos) {
const wasmPath = String(process.argv[wasmPos + 1]);
process.argv.splice(wasmPos, 2);
assemblyscript = await import(new URL(wasmPath, url.pathToFileURL(process.cwd() + "/")));
let assemblyscript = null;
let binaryen = null;
let binaryenModule = "../lib/binaryen.js";

async function loadAssemblyScript(wasmPath) {
if (assemblyscript) return assemblyscript;
return assemblyscript = wasmPath == null
? await importWithInstantiateStreamingFallback("assemblyscript")
: await importWithInstantiateStreamingFallback(new URL(wasmPath, url.pathToFileURL(process.cwd() + "/")));
}

async function loadBinaryen() {
if (binaryen) return binaryen;
return binaryen = (await importWithInstantiateStreamingFallback(binaryenModule)).default;
}

async function importWithInstantiateStreamingFallback(specifier) {
const restoreInstantiateStreaming = installInstantiateStreamingFallback();
try {
return await import(specifier);
} finally {
if (restoreInstantiateStreaming) restoreInstantiateStreaming();
}
}

function installInstantiateStreamingFallback() {
const instantiateStreaming = WebAssembly.instantiateStreaming;
if (typeof instantiateStreaming !== "function") return null;
async function instantiateStreamingFallback(source, imports) {
const response = await source;
try {
const streamingSource = response && typeof response.clone === "function"
? response.clone()
: response;
return await instantiateStreaming.call(WebAssembly, streamingSource, imports);
} catch (error) {
if (response && typeof response.arrayBuffer === "function") {
return WebAssembly.instantiate(await response.arrayBuffer(), imports);
}
throw error;
}
}
WebAssembly.instantiateStreaming = instantiateStreamingFallback;
return () => {
if (WebAssembly.instantiateStreaming === instantiateStreamingFallback) {
WebAssembly.instantiateStreaming = instantiateStreaming;
}
};
}

const require = module.createRequire(import.meta.url);
Expand Down Expand Up @@ -134,6 +170,15 @@ export async function main(argv, options) {
if (!Array.isArray(argv)) argv = configToArguments(argv);
if (!options) options = {};

// Use the AS->Wasm compiler variant as an option (experimental)
let wasmPath = null;
const wasmPos = argv.indexOf("--wasm");
if (~wasmPos) {
wasmPath = String(argv[wasmPos + 1]);
argv = argv.slice();
argv.splice(wasmPos, 2);
}

const stats = options.stats || new Stats();
const statsBegin = stats.begin();

Expand Down Expand Up @@ -289,6 +334,8 @@ export async function main(argv, options) {
return prepareResult(null);
}

const assemblyscript = await loadAssemblyScript(wasmPath);

// create a unique set of values
function unique(values) {
return [...new Set(values)];
Expand Down Expand Up @@ -446,6 +493,7 @@ export async function main(argv, options) {

// Fix up the prototype of the transforms’ constructors and instantiate them.
try {
const binaryen = transforms.length ? await loadBinaryen() : null;
transforms = transforms.map(transform => {
if (typeof transform === "function") {
Object.assign(transform.prototype, {
Expand Down Expand Up @@ -736,6 +784,7 @@ export async function main(argv, options) {
stats.compileTime += stats.end(begin);
}
// From here on we are going to use Binaryen.js
const binaryen = await loadBinaryen();
binaryenModule = binaryen.wrapModule(
typeof module === "number" || module instanceof Number
? assemblyscript.getBinaryenModuleRef(module)
Expand Down
43 changes: 42 additions & 1 deletion tests/browser.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
import * as asc from "../dist/asc.js";
import fs from "node:fs";

const ascSource = fs.readFileSync(new URL("../dist/asc.js", import.meta.url), "utf8");
if (/\bfrom\s*["']binaryen["']/.test(ascSource)) {
throw Error("dist/asc.js must not statically import binaryen");
}

const wasmCompile = WebAssembly.compile;
const wasmCompileStreaming = WebAssembly.compileStreaming;

WebAssembly.compile = () => {
throw Error("unexpected WebAssembly.compile");
};
WebAssembly.compileStreaming = () => {
throw Error("unexpected WebAssembly.compileStreaming");
};

const asc = await import("../dist/asc.js");

if (typeof asc.definitionFiles.assembly !== "string") throw Error("missing bundled assembly.d.ts");
if (typeof asc.definitionFiles.portable !== "string") throw Error("missing bundled portable.d.ts");
Expand All @@ -15,6 +32,9 @@ console.log("# asc --version");
process.stdout.write(stderr.toString());
}

WebAssembly.compile = wasmCompile;
WebAssembly.compileStreaming = wasmCompileStreaming;

console.log("\n# asc --help");
{
const { stdout, stderr } = await asc.main([ "--help" ]);
Expand All @@ -25,6 +45,27 @@ console.log("\n# asc --help");
process.stdout.write(stderr.toString());
}

console.log("\n# asc.compileString with broken instantiateStreaming");
{
const wasmInstantiateStreaming = WebAssembly.instantiateStreaming;
WebAssembly.instantiateStreaming = () => {
throw Error("unexpected WebAssembly.instantiateStreaming");
};
try {
const { error, stdout, stderr, text, binary } = await asc.compileString(`export function test(): void {}`, { optimizeLevel: 3, exportTable: true, stats: true });
if (error) throw error;
console.log(">>> .stdout >>>");
process.stdout.write(stdout.toString());
console.log(">>> .stderr >>>");
process.stdout.write(stderr.toString());
console.log(">>> .text >>>");
process.stdout.write(text);
console.log(">>> .binary >>> " + binary.length + " bytes");
} finally {
WebAssembly.instantiateStreaming = wasmInstantiateStreaming;
}
}

console.log("\n# asc index.ts --textFile");
{
const { error, stdout, stderr } = await asc.main([ "index.ts", "--textFile" ], {
Expand Down
Loading