Skip to content

Commit 60e5f45

Browse files
mhdawsontargos
authored andcommitted
wasi: add support for version when creating WASI
Refs: #46254 - add version to options when creating WASI object - add convenience function to return importObject Signed-off-by: Michael Dawson <mdawson@devrus.com> PR-URL: #46469 Reviewed-By: Guy Bedford <guybedford@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
1 parent 694659c commit 60e5f45

File tree

5 files changed

+120
-21
lines changed

5 files changed

+120
-21
lines changed

doc/api/wasi.md

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,18 @@ import { WASI } from 'wasi';
1616
import { argv, env } from 'node:process';
1717

1818
const wasi = new WASI({
19+
version: 'preview1',
1920
args: argv,
2021
env,
2122
preopens: {
2223
'/sandbox': '/some/real/path/that/wasm/can/access',
2324
},
2425
});
2526

26-
// Some WASI binaries require:
27-
// const importObject = { wasi_unstable: wasi.wasiImport };
28-
const importObject = { wasi_snapshot_preview1: wasi.wasiImport };
29-
3027
const wasm = await WebAssembly.compile(
3128
await readFile(new URL('./demo.wasm', import.meta.url)),
3229
);
33-
const instance = await WebAssembly.instantiate(wasm, importObject);
30+
const instance = await WebAssembly.instantiate(wasm, wasi.getImportObject());
3431

3532
wasi.start(instance);
3633
```
@@ -43,22 +40,19 @@ const { argv, env } = require('node:process');
4340
const { join } = require('node:path');
4441

4542
const wasi = new WASI({
43+
version: 'preview1',
4644
args: argv,
4745
env,
4846
preopens: {
4947
'/sandbox': '/some/real/path/that/wasm/can/access',
5048
},
5149
});
5250

53-
// Some WASI binaries require:
54-
// const importObject = { wasi_unstable: wasi.wasiImport };
55-
const importObject = { wasi_snapshot_preview1: wasi.wasiImport };
56-
5751
(async () => {
5852
const wasm = await WebAssembly.compile(
5953
await readFile(join(__dirname, 'demo.wasm')),
6054
);
61-
const instance = await WebAssembly.instantiate(wasm, importObject);
55+
const instance = await WebAssembly.instantiate(wasm, wasi.getImportObject());
6256

6357
wasi.start(instance);
6458
})();
@@ -126,6 +120,10 @@ sandbox directory structure configured explicitly.
126120
added:
127121
- v13.3.0
128122
- v12.16.0
123+
changes:
124+
- version: REPLACEME
125+
pr-url: https://github.com/nodejs/node/pull/46469
126+
description: version field added to options.
129127
-->
130128

131129
* `options` {Object}
@@ -148,6 +146,30 @@ added:
148146
WebAssembly application. **Default:** `1`.
149147
* `stderr` {integer} The file descriptor used as standard error in the
150148
WebAssembly application. **Default:** `2`.
149+
* `version` {string} The version of WASI requested. Currently the only
150+
supported versions are `unstable` and `preview1`. **Default:** `preview1`.
151+
152+
### `wasi.getImportObject()`
153+
154+
<!-- YAML
155+
added: REPLACEME
156+
-->
157+
158+
Return an import object that can be passed to `WebAssembly.instantiate()` if
159+
no other WASM imports are needed beyond those provided by WASI.
160+
161+
If version `unstable` was passed into the constructor it will return:
162+
163+
```json
164+
{ wasi_unstable: wasi.wasiImport }
165+
```
166+
167+
If version `preview1` was passed into the constructor or no version was
168+
specified it will return:
169+
170+
```json
171+
{ wasi_snapshot_preview1: wasi.wasiImport }
172+
```
151173
152174
### `wasi.start(instance)`
153175

lib/wasi.js

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const {
1010
} = primordials;
1111

1212
const {
13+
ERR_INVALID_ARG_VALUE,
1314
ERR_WASI_ALREADY_STARTED
1415
} = require('internal/errors').codes;
1516
const {
@@ -22,13 +23,14 @@ const {
2223
validateFunction,
2324
validateInt32,
2425
validateObject,
26+
validateString,
2527
validateUndefined,
2628
} = require('internal/validators');
27-
const { WASI: _WASI } = internalBinding('wasi');
2829
const kExitCode = Symbol('kExitCode');
2930
const kSetMemory = Symbol('kSetMemory');
3031
const kStarted = Symbol('kStarted');
3132
const kInstance = Symbol('kInstance');
33+
const kBindingName = Symbol('kBindingName');
3234

3335
emitExperimentalWarning('WASI');
3436

@@ -45,6 +47,31 @@ class WASI {
4547
constructor(options = kEmptyObject) {
4648
validateObject(options, 'options');
4749

50+
let _WASI;
51+
if (options.version !== undefined) {
52+
validateString(options.version, 'options.version');
53+
switch (options.version) {
54+
case 'unstable':
55+
({ WASI: _WASI } = internalBinding('wasi'));
56+
this[kBindingName] = 'wasi_unstable';
57+
break;
58+
// When adding support for additional wasi versions add case here
59+
case 'preview1':
60+
({ WASI: _WASI } = internalBinding('wasi'));
61+
this[kBindingName] = 'wasi_snapshot_preview1';
62+
break;
63+
// When adding support for additional wasi versions add case here
64+
default:
65+
throw new ERR_INVALID_ARG_VALUE('options.version',
66+
options.version,
67+
'unsupported WASI version');
68+
}
69+
} else {
70+
// TODO(mdawson): Remove this in a SemVer major PR before Node.js 20
71+
({ WASI: _WASI } = internalBinding('wasi'));
72+
this[kBindingName] = 'wasi_snapshot_preview1';
73+
}
74+
4875
if (options.args !== undefined)
4976
validateArray(options.args, 'options.args');
5077
const args = ArrayPrototypeMap(options.args || [], String);
@@ -138,8 +165,11 @@ class WASI {
138165
_initialize();
139166
}
140167
}
141-
}
142168

169+
getImportObject() {
170+
return { [this[kBindingName]]: this.wasiImport };
171+
}
172+
}
143173

144174
module.exports = { WASI };
145175

src/node_wasi.cc

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,10 +1247,10 @@ void WASI::_SetMemory(const FunctionCallbackInfo<Value>& args) {
12471247
wasi->memory_.Reset(wasi->env()->isolate(), args[0].As<WasmMemoryObject>());
12481248
}
12491249

1250-
static void Initialize(Local<Object> target,
1251-
Local<Value> unused,
1252-
Local<Context> context,
1253-
void* priv) {
1250+
static void InitializePreview1(Local<Object> target,
1251+
Local<Value> unused,
1252+
Local<Context> context,
1253+
void* priv) {
12541254
Environment* env = Environment::GetCurrent(context);
12551255
Isolate* isolate = env->isolate();
12561256

@@ -1313,8 +1313,7 @@ static void Initialize(Local<Object> target,
13131313
SetConstructorFunction(context, target, "WASI", tmpl);
13141314
}
13151315

1316-
13171316
} // namespace wasi
13181317
} // namespace node
13191318

1320-
NODE_BINDING_CONTEXT_AWARE_INTERNAL(wasi, node::wasi::Initialize)
1319+
NODE_BINDING_CONTEXT_AWARE_INTERNAL(wasi, node::wasi::InitializePreview1)

test/wasi/test-wasi-options-validation.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,12 @@ assert.throws(() => { new WASI({ stderr: 'fhqwhgads' }); },
4747
assert.throws(() => {
4848
new WASI({ preopens: { '/sandbox': '__/not/real/path' } });
4949
}, { code: 'UVWASI_ENOENT', message: /uvwasi_init/ });
50+
51+
// If version is not a string, it should throw
52+
assert.throws(() => { new WASI({ version: { x: 'y' } }); },
53+
{ code: 'ERR_INVALID_ARG_TYPE', message: /\bversion\b/ });
54+
55+
56+
// If version is an unsupported version, it should throw
57+
assert.throws(() => { new WASI({ version: 'not_a_version' }); },
58+
{ code: 'ERR_INVALID_ARG_VALUE', message: /\bversion\b/ });

test/wasi/test-wasi.js

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
'use strict';
22
const common = require('../common');
33

4-
if (process.argv[2] === 'wasi-child') {
4+
if (process.argv[2] === 'wasi-child-default') {
5+
// test default case
56
const fixtures = require('../common/fixtures');
67
const tmpdir = require('../common/tmpdir');
78
const fs = require('fs');
@@ -30,12 +31,49 @@ if (process.argv[2] === 'wasi-child') {
3031

3132
wasi.start(instance);
3233
})().then(common.mustCall());
34+
} else if (process.argv[2] === 'wasi-child-preview1') {
35+
// Test version set to preview1
36+
const assert = require('assert');
37+
const fixtures = require('../common/fixtures');
38+
const tmpdir = require('../common/tmpdir');
39+
const fs = require('fs');
40+
const path = require('path');
41+
42+
common.expectWarning('ExperimentalWarning',
43+
'WASI is an experimental feature and might change at any time');
44+
45+
const { WASI } = require('wasi');
46+
tmpdir.refresh();
47+
const wasmDir = path.join(__dirname, 'wasm');
48+
const wasiPreview1 = new WASI({
49+
version: 'preview1',
50+
args: ['foo', '-bar', '--baz=value'],
51+
env: process.env,
52+
preopens: {
53+
'/sandbox': fixtures.path('wasi'),
54+
'/tmp': tmpdir.path,
55+
},
56+
});
57+
58+
// Validate the getImportObject helper
59+
assert.strictEqual(wasiPreview1.wasiImport,
60+
wasiPreview1.getImportObject().wasi_snapshot_preview1);
61+
const modulePathPreview1 = path.join(wasmDir, `${process.argv[3]}.wasm`);
62+
const bufferPreview1 = fs.readFileSync(modulePathPreview1);
63+
64+
(async () => {
65+
const { instance: instancePreview1 } =
66+
await WebAssembly.instantiate(bufferPreview1,
67+
wasiPreview1.getImportObject());
68+
69+
wasiPreview1.start(instancePreview1);
70+
})().then(common.mustCall());
3371
} else {
3472
const assert = require('assert');
3573
const cp = require('child_process');
3674
const { checkoutEOL } = common;
3775

38-
function innerRunWASI(options, args) {
76+
function innerRunWASI(options, args, flavor = 'default') {
3977
console.log('executing', options.test);
4078
const opts = {
4179
env: {
@@ -52,7 +90,7 @@ if (process.argv[2] === 'wasi-child') {
5290
...args,
5391
'--experimental-wasi-unstable-preview1',
5492
__filename,
55-
'wasi-child',
93+
'wasi-child-' + flavor,
5694
options.test,
5795
], opts);
5896
console.log(child.stderr.toString());
@@ -64,6 +102,7 @@ if (process.argv[2] === 'wasi-child') {
64102
function runWASI(options) {
65103
innerRunWASI(options, ['--no-turbo-fast-api-calls']);
66104
innerRunWASI(options, ['--turbo-fast-api-calls']);
105+
innerRunWASI(options, ['--turbo-fast-api-calls'], 'preview1');
67106
}
68107

69108
runWASI({ test: 'cant_dotdot' });

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy