2019-09-04 21:56:51 +00:00
|
|
|
'use strict';
|
|
|
|
const {
|
2020-12-31 11:37:41 +00:00
|
|
|
ArrayPrototypeForEach,
|
2019-09-04 21:56:51 +00:00
|
|
|
ArrayPrototypeMap,
|
2020-01-06 05:23:50 +00:00
|
|
|
ArrayPrototypePush,
|
2019-09-04 21:56:51 +00:00
|
|
|
FunctionPrototypeBind,
|
2020-01-06 05:23:50 +00:00
|
|
|
ObjectEntries,
|
2020-01-06 02:55:31 +00:00
|
|
|
String,
|
2019-11-30 15:55:29 +00:00
|
|
|
Symbol,
|
2019-09-04 21:56:51 +00:00
|
|
|
} = primordials;
|
2019-11-30 15:55:29 +00:00
|
|
|
|
2019-09-04 21:56:51 +00:00
|
|
|
const {
|
2023-01-31 23:46:55 +00:00
|
|
|
ERR_INVALID_ARG_VALUE,
|
2023-02-12 18:26:21 +00:00
|
|
|
ERR_WASI_ALREADY_STARTED,
|
2019-09-04 21:56:51 +00:00
|
|
|
} = require('internal/errors').codes;
|
2022-05-21 09:57:20 +00:00
|
|
|
const {
|
|
|
|
emitExperimentalWarning,
|
|
|
|
kEmptyObject,
|
|
|
|
} = require('internal/util');
|
2020-02-14 17:36:12 +00:00
|
|
|
const {
|
|
|
|
validateArray,
|
|
|
|
validateBoolean,
|
2021-05-16 11:22:48 +00:00
|
|
|
validateFunction,
|
2020-03-26 04:22:13 +00:00
|
|
|
validateInt32,
|
2020-02-14 17:36:12 +00:00
|
|
|
validateObject,
|
2023-01-31 23:46:55 +00:00
|
|
|
validateString,
|
2021-06-18 02:09:40 +00:00
|
|
|
validateUndefined,
|
2020-02-14 17:36:12 +00:00
|
|
|
} = require('internal/validators');
|
2020-06-25 00:45:03 +00:00
|
|
|
const kExitCode = Symbol('kExitCode');
|
|
|
|
const kSetMemory = Symbol('kSetMemory');
|
|
|
|
const kStarted = Symbol('kStarted');
|
|
|
|
const kInstance = Symbol('kInstance');
|
2023-01-31 23:46:55 +00:00
|
|
|
const kBindingName = Symbol('kBindingName');
|
2019-09-04 21:56:51 +00:00
|
|
|
|
|
|
|
emitExperimentalWarning('WASI');
|
|
|
|
|
|
|
|
|
2020-06-25 00:45:03 +00:00
|
|
|
function setupInstance(self, instance) {
|
|
|
|
validateObject(instance, 'instance');
|
|
|
|
validateObject(instance.exports, 'instance.exports');
|
|
|
|
|
|
|
|
self[kInstance] = instance;
|
|
|
|
self[kSetMemory](instance.exports.memory);
|
|
|
|
}
|
|
|
|
|
2019-09-04 21:56:51 +00:00
|
|
|
class WASI {
|
2022-05-21 09:57:20 +00:00
|
|
|
constructor(options = kEmptyObject) {
|
2020-02-14 17:36:12 +00:00
|
|
|
validateObject(options, 'options');
|
2019-09-04 21:56:51 +00:00
|
|
|
|
2023-01-31 23:46:55 +00:00
|
|
|
let _WASI;
|
2023-04-03 21:50:18 +00:00
|
|
|
validateString(options.version, 'options.version');
|
|
|
|
switch (options.version) {
|
|
|
|
case 'unstable':
|
|
|
|
({ WASI: _WASI } = internalBinding('wasi'));
|
|
|
|
this[kBindingName] = 'wasi_unstable';
|
|
|
|
break;
|
|
|
|
case 'preview1':
|
|
|
|
({ WASI: _WASI } = internalBinding('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');
|
2023-01-31 23:46:55 +00:00
|
|
|
}
|
|
|
|
|
2020-02-14 17:36:12 +00:00
|
|
|
if (options.args !== undefined)
|
|
|
|
validateArray(options.args, 'options.args');
|
|
|
|
const args = ArrayPrototypeMap(options.args || [], String);
|
2019-09-04 21:56:51 +00:00
|
|
|
|
2020-02-14 17:36:12 +00:00
|
|
|
const env = [];
|
|
|
|
if (options.env !== undefined) {
|
|
|
|
validateObject(options.env, 'options.env');
|
2020-12-31 11:37:41 +00:00
|
|
|
ArrayPrototypeForEach(
|
|
|
|
ObjectEntries(options.env),
|
|
|
|
({ 0: key, 1: value }) => {
|
|
|
|
if (value !== undefined)
|
|
|
|
ArrayPrototypePush(env, `${key}=${value}`);
|
|
|
|
});
|
2019-09-04 21:56:51 +00:00
|
|
|
}
|
|
|
|
|
2020-02-14 17:36:12 +00:00
|
|
|
const preopens = [];
|
|
|
|
if (options.preopens !== undefined) {
|
|
|
|
validateObject(options.preopens, 'options.preopens');
|
2020-12-31 11:37:41 +00:00
|
|
|
ArrayPrototypeForEach(
|
|
|
|
ObjectEntries(options.preopens),
|
|
|
|
({ 0: key, 1: value }) =>
|
2023-02-14 17:45:16 +00:00
|
|
|
ArrayPrototypePush(preopens, String(key), String(value)),
|
2020-12-31 11:37:41 +00:00
|
|
|
);
|
2020-03-05 01:46:02 +00:00
|
|
|
}
|
|
|
|
|
2020-03-26 04:22:13 +00:00
|
|
|
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);
|
2019-09-04 21:56:51 +00:00
|
|
|
|
|
|
|
for (const prop in wrap) {
|
|
|
|
wrap[prop] = FunctionPrototypeBind(wrap[prop], wrap);
|
|
|
|
}
|
|
|
|
|
2023-04-03 19:35:46 +00:00
|
|
|
let returnOnExit = true;
|
2020-02-14 17:36:12 +00:00
|
|
|
if (options.returnOnExit !== undefined) {
|
|
|
|
validateBoolean(options.returnOnExit, 'options.returnOnExit');
|
2023-04-03 19:35:46 +00:00
|
|
|
returnOnExit = options.returnOnExit;
|
2020-03-05 01:46:02 +00:00
|
|
|
}
|
2023-04-03 19:35:46 +00:00
|
|
|
if (returnOnExit)
|
|
|
|
wrap.proc_exit = FunctionPrototypeBind(wasiReturnOnProcExit, this);
|
2020-03-05 01:46:02 +00:00
|
|
|
|
2019-09-04 21:56:51 +00:00
|
|
|
this[kSetMemory] = wrap._setMemory;
|
|
|
|
delete wrap._setMemory;
|
|
|
|
this.wasiImport = wrap;
|
|
|
|
this[kStarted] = false;
|
2020-03-05 01:46:02 +00:00
|
|
|
this[kExitCode] = 0;
|
2020-06-25 00:45:03 +00:00
|
|
|
this[kInstance] = undefined;
|
2019-09-04 21:56:51 +00:00
|
|
|
}
|
|
|
|
|
2020-06-25 00:45:03 +00:00
|
|
|
// Must not export _initialize, must export _start
|
2019-09-04 21:56:51 +00:00
|
|
|
start(instance) {
|
2020-06-25 00:45:03 +00:00
|
|
|
if (this[kStarted]) {
|
|
|
|
throw new ERR_WASI_ALREADY_STARTED();
|
|
|
|
}
|
|
|
|
this[kStarted] = true;
|
2019-09-04 21:56:51 +00:00
|
|
|
|
2020-06-25 00:45:03 +00:00
|
|
|
setupInstance(this, instance);
|
2019-09-04 21:56:51 +00:00
|
|
|
|
2020-06-25 00:45:03 +00:00
|
|
|
const { _start, _initialize } = this[kInstance].exports;
|
2020-04-26 05:20:10 +00:00
|
|
|
|
2021-05-16 11:22:48 +00:00
|
|
|
validateFunction(_start, 'instance.exports._start');
|
2021-06-18 02:09:40 +00:00
|
|
|
validateUndefined(_initialize, 'instance.exports._initialize');
|
2019-09-04 21:56:51 +00:00
|
|
|
|
2020-06-25 00:45:03 +00:00
|
|
|
try {
|
|
|
|
_start();
|
|
|
|
} catch (err) {
|
|
|
|
if (err !== kExitCode) {
|
|
|
|
throw err;
|
|
|
|
}
|
2019-09-04 21:56:51 +00:00
|
|
|
}
|
|
|
|
|
2020-06-25 00:45:03 +00:00
|
|
|
return this[kExitCode];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Must not export _start, may optionally export _initialize
|
|
|
|
initialize(instance) {
|
2019-09-04 21:56:51 +00:00
|
|
|
if (this[kStarted]) {
|
|
|
|
throw new ERR_WASI_ALREADY_STARTED();
|
|
|
|
}
|
|
|
|
this[kStarted] = true;
|
|
|
|
|
2020-06-25 00:45:03 +00:00
|
|
|
setupInstance(this, instance);
|
|
|
|
|
|
|
|
const { _start, _initialize } = this[kInstance].exports;
|
|
|
|
|
2021-06-18 02:09:40 +00:00
|
|
|
validateUndefined(_start, 'instance.exports._start');
|
2020-06-25 00:45:03 +00:00
|
|
|
if (_initialize !== undefined) {
|
2021-06-18 02:09:40 +00:00
|
|
|
validateFunction(_initialize, 'instance.exports._initialize');
|
2020-06-25 00:45:03 +00:00
|
|
|
_initialize();
|
|
|
|
}
|
2019-09-04 21:56:51 +00:00
|
|
|
}
|
|
|
|
|
2023-01-31 23:46:55 +00:00
|
|
|
getImportObject() {
|
|
|
|
return { [this[kBindingName]]: this.wasiImport };
|
|
|
|
}
|
|
|
|
}
|
2019-09-04 21:56:51 +00:00
|
|
|
|
|
|
|
module.exports = { WASI };
|
2020-03-05 01:46:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|