nstdlib-nightly
Version:
Node.js standard library converted to runtime-agnostic ES modules.
170 lines (141 loc) • 4.85 kB
JavaScript
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/wasi.js
import { codes as __codes__ } from "nstdlib/lib/internal/errors";
import {
emitExperimentalWarning,
kEmptyObject,
} from "nstdlib/lib/internal/util";
import {
validateArray,
validateBoolean,
validateFunction,
validateInt32,
validateObject,
validateString,
validateUndefined,
} from "nstdlib/lib/internal/validators";
const { ERR_INVALID_ARG_VALUE, ERR_WASI_ALREADY_STARTED } = __codes__;
const kExitCode = Symbol("kExitCode");
const kSetMemory = Symbol("kSetMemory");
const kStarted = Symbol("kStarted");
const kInstance = Symbol("kInstance");
const kBindingName = Symbol("kBindingName");
emitExperimentalWarning("WASI");
function setupInstance(self, instance) {
validateObject(instance, "instance");
validateObject(instance.exports, "instance.exports");
self[kInstance] = instance;
self[kSetMemory](instance.exports.memory);
}
class WASI {
constructor(options = kEmptyObject) {
validateObject(options, "options");
let _WASI;
validateString(options.version, "options.version");
switch (options.version) {
case "unstable":
({ WASI: _WASI } = require("binding/wasi"));
this[kBindingName] = "wasi_unstable";
break;
case "preview1":
({ WASI: _WASI } = require("binding/wasi"));
this[kBindingName] = "wasi_snapshot_preview1";
break;
// When adding support for additional wasi versions add case here
default:
throw new ERR_INVALID_ARG_VALUE(
"options.version",
options.version,
"unsupported WASI version",
);
}
if (options.args !== undefined) validateArray(options.args, "options.args");
const args = Array.prototype.map.call(options.args || [], String);
const env = [];
if (options.env !== undefined) {
validateObject(options.env, "options.env");
Array.prototype.forEach.call(
Object.entries(options.env),
({ 0: key, 1: value }) => {
if (value !== undefined)
Array.prototype.push.call(env, `${key}=${value}`);
},
);
}
const preopens = [];
if (options.preopens !== undefined) {
validateObject(options.preopens, "options.preopens");
Array.prototype.forEach.call(
Object.entries(options.preopens),
({ 0: key, 1: value }) =>
Array.prototype.push.call(preopens, String(key), String(value)),
);
}
const { stdin = 0, stdout = 1, stderr = 2 } = options;
validateInt32(stdin, "options.stdin", 0);
validateInt32(stdout, "options.stdout", 0);
validateInt32(stderr, "options.stderr", 0);
const stdio = [stdin, stdout, stderr];
const wrap = new _WASI(args, env, preopens, stdio);
for (const prop in wrap) {
wrap[prop] = Function.prototype.bind.call(wrap[prop], wrap);
}
let returnOnExit = true;
if (options.returnOnExit !== undefined) {
validateBoolean(options.returnOnExit, "options.returnOnExit");
returnOnExit = options.returnOnExit;
}
if (returnOnExit)
wrap.proc_exit = Function.prototype.bind.call(wasiReturnOnProcExit, this);
this[kSetMemory] = wrap._setMemory;
delete wrap._setMemory;
this.wasiImport = wrap;
this[kStarted] = false;
this[kExitCode] = 0;
this[kInstance] = undefined;
}
// Must not export _initialize, must export _start
start(instance) {
if (this[kStarted]) {
throw new ERR_WASI_ALREADY_STARTED();
}
this[kStarted] = true;
setupInstance(this, instance);
const { _start, _initialize } = this[kInstance].exports;
validateFunction(_start, "instance.exports._start");
validateUndefined(_initialize, "instance.exports._initialize");
try {
_start();
} catch (err) {
if (err !== kExitCode) {
throw err;
}
}
return this[kExitCode];
}
// Must not export _start, may optionally export _initialize
initialize(instance) {
if (this[kStarted]) {
throw new ERR_WASI_ALREADY_STARTED();
}
this[kStarted] = true;
setupInstance(this, instance);
const { _start, _initialize } = this[kInstance].exports;
validateUndefined(_start, "instance.exports._start");
if (_initialize !== undefined) {
validateFunction(_initialize, "instance.exports._initialize");
_initialize();
}
}
getImportObject() {
return { [this[kBindingName]]: this.wasiImport };
}
}
export { WASI };
function wasiReturnOnProcExit(rval) {
// If __wasi_proc_exit() does not terminate the process, an assertion is
// triggered in the wasm runtime. Node can sidestep the assertion and return
// an exit code by recording the exit code, and throwing a JavaScript
// exception that WebAssembly cannot catch.
this[kExitCode] = rval;
throw kExitCode;
}