Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wasi: make version non-optional #47391

Closed
wants to merge 6 commits into from
Closed
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
6 changes: 5 additions & 1 deletion doc/api/wasi.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ added:
- v13.3.0
- v12.16.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/47391
description: The version option is now required and has no default value.
- version: v19.8.0
pr-url: https://github.com/nodejs/node/pull/46469
description: version field added to options.
Expand All @@ -144,7 +147,8 @@ changes:
* `stderr` {integer} The file descriptor used as standard error in the
WebAssembly application. **Default:** `2`.
* `version` {string} The version of WASI requested. Currently the only
supported versions are `unstable` and `preview1`. **Default:** `preview1`.
supported versions are `unstable` and `preview1`. This option is
mandatory.

### `wasi.getImportObject()`

Expand Down
37 changes: 15 additions & 22 deletions lib/wasi.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,28 +48,21 @@ class WASI {
validateObject(options, 'options');

let _WASI;
if (options.version !== undefined) {
validateString(options.version, 'options.version');
switch (options.version) {
case 'unstable':
({ WASI: _WASI } = internalBinding('wasi'));
this[kBindingName] = 'wasi_unstable';
break;
// When adding support for additional wasi versions add case here
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');
}
} else {
// TODO(mdawson): Remove this in a SemVer major PR before Node.js 20
({ WASI: _WASI } = internalBinding('wasi'));
this[kBindingName] = 'wasi_snapshot_preview1';
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');
}

if (options.args !== undefined)
Expand Down
4 changes: 2 additions & 2 deletions test/wasi/test-return-on-exit.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const modulePath = path.join(wasmDir, 'exitcode.wasm');
const buffer = fs.readFileSync(modulePath);

(async () => {
const wasi = new WASI({ returnOnExit: true });
const wasi = new WASI({ version: 'preview1', returnOnExit: true });
const importObject = { wasi_snapshot_preview1: wasi.wasiImport };
const { instance } = await WebAssembly.instantiate(buffer, importObject);

Expand All @@ -19,7 +19,7 @@ const buffer = fs.readFileSync(modulePath);
(async () => {
// Verify that if a WASI application throws an exception, Node rethrows it
// properly.
const wasi = new WASI({ returnOnExit: true });
const wasi = new WASI({ version: 'preview1', returnOnExit: true });
const patchedExit = () => { throw new Error('test error'); };
wasi.wasiImport.proc_exit = patchedExit.bind(wasi.wasiImport);
const importObject = { wasi_snapshot_preview1: wasi.wasiImport };
Expand Down
14 changes: 7 additions & 7 deletions test/wasi/test-wasi-initialize-validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const bufferSource = fixtures.readSync('simple.wasm');
(async () => {
{
// Verify that a WebAssembly.Instance is passed in.
const wasi = new WASI();
const wasi = new WASI({ version: 'preview1' });

assert.throws(
() => { wasi.initialize(); },
Expand All @@ -24,7 +24,7 @@ const bufferSource = fixtures.readSync('simple.wasm');

{
// Verify that the passed instance has an exports objects.
const wasi = new WASI({});
const wasi = new WASI({ version: 'preview1' });
const wasm = await WebAssembly.compile(bufferSource);
const instance = await WebAssembly.instantiate(wasm);

Expand All @@ -40,7 +40,7 @@ const bufferSource = fixtures.readSync('simple.wasm');

{
// Verify that a _initialize() export was passed.
const wasi = new WASI({});
const wasi = new WASI({ version: 'preview1' });
const wasm = await WebAssembly.compile(bufferSource);
const instance = await WebAssembly.instantiate(wasm);

Expand All @@ -63,7 +63,7 @@ const bufferSource = fixtures.readSync('simple.wasm');

{
// Verify that a _start export was not passed.
const wasi = new WASI({});
const wasi = new WASI({ version: 'preview1' });
const wasm = await WebAssembly.compile(bufferSource);
const instance = await WebAssembly.instantiate(wasm);

Expand All @@ -88,7 +88,7 @@ const bufferSource = fixtures.readSync('simple.wasm');

{
// Verify that a memory export was passed.
const wasi = new WASI({});
const wasi = new WASI({ version: 'preview1' });
const wasm = await WebAssembly.compile(bufferSource);
const instance = await WebAssembly.instantiate(wasm);

Expand All @@ -106,7 +106,7 @@ const bufferSource = fixtures.readSync('simple.wasm');

{
// Verify that a WebAssembly.Instance from another VM context is accepted.
const wasi = new WASI({});
const wasi = new WASI({ version: 'preview1' });
const instance = await vm.runInNewContext(`
(async () => {
const wasm = await WebAssembly.compile(bufferSource);
Expand All @@ -130,7 +130,7 @@ const bufferSource = fixtures.readSync('simple.wasm');

{
// Verify that initialize() can only be called once.
const wasi = new WASI({});
const wasi = new WASI({ version: 'preview1' });
const wasm = await WebAssembly.compile(bufferSource);
const instance = await WebAssembly.instantiate(wasm);

Expand Down
1 change: 1 addition & 0 deletions test/wasi/test-wasi-not-started.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ if (process.argv[2] === 'wasi-child') {

const { WASI } = require('wasi');
const wasi = new WASI({
version: 'preview1',
args: ['foo', '-bar', '--baz=value'],
});
const importObject = { wasi_snapshot_preview1: wasi.wasiImport };
Expand Down
21 changes: 12 additions & 9 deletions test/wasi/test-wasi-options-validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,34 @@ const assert = require('assert');
const { WASI } = require('wasi');

// If args is undefined, it should default to [] and should not throw.
new WASI({});
new WASI({ version: 'preview1' });

// If args is not an Array and not undefined, it should throw.
assert.throws(() => { new WASI({ args: 'fhqwhgads' }); },
assert.throws(() => { new WASI({ version: 'preview1', args: 'fhqwhgads' }); },
{ code: 'ERR_INVALID_ARG_TYPE', message: /\bargs\b/ });

// If env is not an Object and not undefined, it should throw.
assert.throws(() => { new WASI({ env: 'fhqwhgads' }); },
assert.throws(() => { new WASI({ version: 'preview1', env: 'fhqwhgads' }); },
{ code: 'ERR_INVALID_ARG_TYPE', message: /\benv\b/ });

// If preopens is not an Object and not undefined, it should throw.
assert.throws(() => { new WASI({ preopens: 'fhqwhgads' }); },
assert.throws(() => { new WASI({ version: 'preview1', preopens: 'fhqwhgads' }); },
{ code: 'ERR_INVALID_ARG_TYPE', message: /\bpreopens\b/ });

// If returnOnExit is not a boolean and not undefined, it should throw.
assert.throws(() => { new WASI({ returnOnExit: 'fhqwhgads' }); },
assert.throws(() => { new WASI({ version: 'preview1', returnOnExit: 'fhqwhgads' }); },
{ code: 'ERR_INVALID_ARG_TYPE', message: /\breturnOnExit\b/ });

// If stdin is not an int32 and not undefined, it should throw.
assert.throws(() => { new WASI({ stdin: 'fhqwhgads' }); },
assert.throws(() => { new WASI({ version: 'preview1', stdin: 'fhqwhgads' }); },
{ code: 'ERR_INVALID_ARG_TYPE', message: /\bstdin\b/ });

// If stdout is not an int32 and not undefined, it should throw.
assert.throws(() => { new WASI({ stdout: 'fhqwhgads' }); },
assert.throws(() => { new WASI({ version: 'preview1', stdout: 'fhqwhgads' }); },
{ code: 'ERR_INVALID_ARG_TYPE', message: /\bstdout\b/ });

// If stderr is not an int32 and not undefined, it should throw.
assert.throws(() => { new WASI({ stderr: 'fhqwhgads' }); },
assert.throws(() => { new WASI({ version: 'preview1', stderr: 'fhqwhgads' }); },
{ code: 'ERR_INVALID_ARG_TYPE', message: /\bstderr\b/ });

// If options is provided, but not an object, the constructor should throw.
Expand All @@ -43,13 +43,16 @@ assert.throws(() => { new WASI({ stderr: 'fhqwhgads' }); },

// Verify that exceptions thrown from the binding layer are handled.
assert.throws(() => {
new WASI({ preopens: { '/sandbox': '__/not/real/path' } });
new WASI({ version: 'preview1', preopens: { '/sandbox': '__/not/real/path' } });
}, { code: 'UVWASI_ENOENT', message: /uvwasi_init/ });

// If version is not a string, it should throw
assert.throws(() => { new WASI({ version: { x: 'y' } }); },
{ code: 'ERR_INVALID_ARG_TYPE', message: /\bversion\b/ });

// If version is not specified, it should throw.
assert.throws(() => { new WASI(); },
{ code: 'ERR_INVALID_ARG_TYPE', message: /\bversion\b/ });

// If version is an unsupported version, it should throw
assert.throws(() => { new WASI({ version: 'not_a_version' }); },
Expand Down
14 changes: 7 additions & 7 deletions test/wasi/test-wasi-start-validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const bufferSource = fixtures.readSync('simple.wasm');
(async () => {
{
// Verify that a WebAssembly.Instance is passed in.
const wasi = new WASI();
const wasi = new WASI({ version: 'preview1' });

assert.throws(
() => { wasi.start(); },
Expand All @@ -24,7 +24,7 @@ const bufferSource = fixtures.readSync('simple.wasm');

{
// Verify that the passed instance has an exports objects.
const wasi = new WASI({});
const wasi = new WASI({ version: 'preview1' });
const wasm = await WebAssembly.compile(bufferSource);
const instance = await WebAssembly.instantiate(wasm);

Expand All @@ -40,7 +40,7 @@ const bufferSource = fixtures.readSync('simple.wasm');

{
// Verify that a _start() export was passed.
const wasi = new WASI({});
const wasi = new WASI({ version: 'preview1' });
const wasm = await WebAssembly.compile(bufferSource);
const instance = await WebAssembly.instantiate(wasm);

Expand All @@ -60,7 +60,7 @@ const bufferSource = fixtures.readSync('simple.wasm');

{
// Verify that an _initialize export was not passed.
const wasi = new WASI({});
const wasi = new WASI({ version: 'preview1' });
const wasm = await WebAssembly.compile(bufferSource);
const instance = await WebAssembly.instantiate(wasm);

Expand All @@ -85,7 +85,7 @@ const bufferSource = fixtures.readSync('simple.wasm');

{
// Verify that a memory export was passed.
const wasi = new WASI({});
const wasi = new WASI({ version: 'preview1' });
const wasm = await WebAssembly.compile(bufferSource);
const instance = await WebAssembly.instantiate(wasm);

Expand All @@ -103,7 +103,7 @@ const bufferSource = fixtures.readSync('simple.wasm');

{
// Verify that a WebAssembly.Instance from another VM context is accepted.
const wasi = new WASI({});
const wasi = new WASI({ version: 'preview1' });
const instance = await vm.runInNewContext(`
(async () => {
const wasm = await WebAssembly.compile(bufferSource);
Expand All @@ -127,7 +127,7 @@ const bufferSource = fixtures.readSync('simple.wasm');

{
// Verify that start() can only be called once.
const wasi = new WASI({});
const wasi = new WASI({ version: 'preview1' });
const wasm = await WebAssembly.compile(bufferSource);
const instance = await WebAssembly.instantiate(wasm);

Expand Down
2 changes: 1 addition & 1 deletion test/wasi/test-wasi-stdio.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ writeFileSync(stdinFile, 'x'.repeat(33));
const stdin = openSync(stdinFile, 'r');
const stdout = openSync(stdoutFile, 'a');
const stderr = openSync(stderrFile, 'a');
const wasi = new WASI({ stdin, stdout, stderr, returnOnExit: true });
const wasi = new WASI({ version: 'preview1', stdin, stdout, stderr, returnOnExit: true });
const importObject = { wasi_snapshot_preview1: wasi.wasiImport };

(async () => {
Expand Down
1 change: 1 addition & 0 deletions test/wasi/test-wasi-symlinks.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ if (process.argv[2] === 'wasi-child') {
const { WASI } = require('wasi');
const wasmDir = path.join(__dirname, 'wasm');
const wasi = new WASI({
version: 'preview1',
args: [],
env: process.env,
preopens: {
Expand Down
2 changes: 1 addition & 1 deletion test/wasi/test-wasi-worker-terminate.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ if (!process.env.HAS_STARTED_WORKER) {
}

async function go() {
const wasi = new WASI({ returnOnExit: true });
const wasi = new WASI({ version: 'preview1', returnOnExit: true });
const imports = { wasi_snapshot_preview1: wasi.wasiImport };
const module = await WebAssembly.compile(bytecode);
const instance = await WebAssembly.instantiate(module, imports);
Expand Down
35 changes: 2 additions & 33 deletions test/wasi/test-wasi.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,7 @@
'use strict';
const common = require('../common');

if (process.argv[2] === 'wasi-child-default') {
// test default case
const fixtures = require('../common/fixtures');
const tmpdir = require('../common/tmpdir');
const fs = require('fs');
const path = require('path');

common.expectWarning('ExperimentalWarning',
'WASI is an experimental feature and might change at any time');

const { WASI } = require('wasi');
tmpdir.refresh();
const wasmDir = path.join(__dirname, 'wasm');
const wasi = new WASI({
args: ['foo', '-bar', '--baz=value'],
env: process.env,
preopens: {
'/sandbox': fixtures.path('wasi'),
'/tmp': tmpdir.path,
},
});
const importObject = { wasi_snapshot_preview1: wasi.wasiImport };
const modulePath = path.join(wasmDir, `${process.argv[3]}.wasm`);
const buffer = fs.readFileSync(modulePath);

(async () => {
const { instance } = await WebAssembly.instantiate(buffer, importObject);

wasi.start(instance);
})().then(common.mustCall());
} else if (process.argv[2] === 'wasi-child-preview1') {
if (process.argv[2] === 'wasi-child-preview1') {
// Test version set to preview1
const assert = require('assert');
const fixtures = require('../common/fixtures');
Expand Down Expand Up @@ -73,7 +43,7 @@ if (process.argv[2] === 'wasi-child-default') {
const cp = require('child_process');
const { checkoutEOL } = common;

function innerRunWASI(options, args, flavor = 'default') {
function innerRunWASI(options, args, flavor = 'preview1') {
console.log('executing', options.test);
const opts = {
env: {
Expand Down Expand Up @@ -101,7 +71,6 @@ if (process.argv[2] === 'wasi-child-default') {
function runWASI(options) {
innerRunWASI(options, ['--no-turbo-fast-api-calls']);
innerRunWASI(options, ['--turbo-fast-api-calls']);
innerRunWASI(options, ['--turbo-fast-api-calls'], 'preview1');
}

runWASI({ test: 'cant_dotdot' });
Expand Down