| 'use strict'; |
| const { |
| ArrayPrototypeForEach, |
| ArrayPrototypeMap, |
| ArrayPrototypePush, |
| FunctionPrototypeBind, |
| ObjectEntries, |
| String, |
| Symbol, |
| } = primordials; |
| |
| const { |
| ERR_WASI_ALREADY_STARTED |
| } = require('internal/errors').codes; |
| const { |
| emitExperimentalWarning, |
| kEmptyObject, |
| } = require('internal/util'); |
| const { |
| validateArray, |
| validateBoolean, |
| validateFunction, |
| validateInt32, |
| validateObject, |
| validateUndefined, |
| } = require('internal/validators'); |
| const { WASI: _WASI } = internalBinding('wasi'); |
| const kExitCode = Symbol('kExitCode'); |
| const kSetMemory = Symbol('kSetMemory'); |
| const kStarted = Symbol('kStarted'); |
| const kInstance = Symbol('kInstance'); |
| |
| 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'); |
| |
| if (options.args !== undefined) |
| validateArray(options.args, 'options.args'); |
| const args = ArrayPrototypeMap(options.args || [], String); |
| |
| const env = []; |
| if (options.env !== undefined) { |
| validateObject(options.env, 'options.env'); |
| ArrayPrototypeForEach( |
| ObjectEntries(options.env), |
| ({ 0: key, 1: value }) => { |
| if (value !== undefined) |
| ArrayPrototypePush(env, `${key}=${value}`); |
| }); |
| } |
| |
| const preopens = []; |
| if (options.preopens !== undefined) { |
| validateObject(options.preopens, 'options.preopens'); |
| ArrayPrototypeForEach( |
| ObjectEntries(options.preopens), |
| ({ 0: key, 1: value }) => |
| ArrayPrototypePush(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] = FunctionPrototypeBind(wrap[prop], wrap); |
| } |
| |
| if (options.returnOnExit !== undefined) { |
| validateBoolean(options.returnOnExit, 'options.returnOnExit'); |
| if (options.returnOnExit) |
| wrap.proc_exit = FunctionPrototypeBind(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(); |
| } |
| } |
| } |
| |
| |
| module.exports = { 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; |
| } |