| 'use strict'; |
| /* global WebAssembly */ |
| const { |
| ArrayPrototypeMap, |
| ArrayPrototypePush, |
| FunctionPrototypeBind, |
| ObjectEntries, |
| Symbol, |
| } = primordials; |
| |
| const { |
| ERR_INVALID_ARG_TYPE, |
| ERR_WASI_ALREADY_STARTED |
| } = require('internal/errors').codes; |
| const { emitExperimentalWarning } = require('internal/util'); |
| const { |
| validateArray, |
| validateBoolean, |
| validateObject, |
| } = require('internal/validators'); |
| const { WASI: _WASI } = internalBinding('wasi'); |
| const kExitCode = Symbol('exitCode'); |
| const kSetMemory = Symbol('setMemory'); |
| const kStarted = Symbol('started'); |
| |
| emitExperimentalWarning('WASI'); |
| |
| |
| class WASI { |
| constructor(options = {}) { |
| 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'); |
| for (const [key, value] of ObjectEntries(options.env)) { |
| if (value !== undefined) |
| ArrayPrototypePush(env, `${key}=${value}`); |
| } |
| } |
| |
| const preopens = []; |
| if (options.preopens !== undefined) { |
| validateObject(options.preopens, 'options.preopens'); |
| for (const [key, value] of ObjectEntries(options.preopens)) { |
| ArrayPrototypePush(preopens, String(key), String(value)); |
| } |
| } |
| |
| const wrap = new _WASI(args, env, preopens); |
| |
| 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; |
| } |
| |
| start(instance) { |
| if (!(instance instanceof WebAssembly.Instance)) { |
| throw new ERR_INVALID_ARG_TYPE( |
| 'instance', 'WebAssembly.Instance', instance); |
| } |
| |
| const exports = instance.exports; |
| |
| validateObject(exports, 'instance.exports'); |
| |
| const { _initialize, _start, memory } = exports; |
| |
| if (typeof _start !== 'function') { |
| throw new ERR_INVALID_ARG_TYPE( |
| 'instance.exports._start', 'function', _start); |
| } |
| |
| if (_initialize !== undefined) { |
| throw new ERR_INVALID_ARG_TYPE( |
| 'instance.exports._initialize', 'undefined', _initialize); |
| } |
| |
| if (!(memory instanceof WebAssembly.Memory)) { |
| throw new ERR_INVALID_ARG_TYPE( |
| 'instance.exports.memory', 'WebAssembly.Memory', memory); |
| } |
| |
| if (this[kStarted]) { |
| throw new ERR_WASI_ALREADY_STARTED(); |
| } |
| |
| this[kStarted] = true; |
| this[kSetMemory](memory); |
| |
| try { |
| exports._start(); |
| } catch (err) { |
| if (err !== kExitCode) { |
| throw err; |
| } |
| } |
| |
| return this[kExitCode]; |
| } |
| } |
| |
| |
| 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; |
| } |