Skip to content

Commit 2311766

Browse files
committed
add support for version when creating WASI (nodejs/node#46469)
1 parent 7152924 commit 2311766

17 files changed

Lines changed: 127 additions & 135 deletions

File tree

src/wasi/index.ts

Lines changed: 77 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { WASI as _WASI } from './preview1'
1+
import { WASI as WASIPreview1 } from './preview1'
22
import type { Preopen } from './preview1'
33
import type { exitcode } from './types'
44

@@ -7,7 +7,8 @@ import {
77
validateArray,
88
validateBoolean,
99
validateFunction,
10-
validateUndefined
10+
validateUndefined,
11+
validateString
1112
} from './util'
1213
import type { IFs, IFsPromises } from './fs'
1314
import type { Asyncify } from '../asyncify'
@@ -18,6 +19,7 @@ const kExitCode = Symbol('kExitCode')
1819
const kSetMemory = Symbol('kSetMemory')
1920
const kStarted = Symbol('kStarted')
2021
const kInstance = Symbol('kInstance')
22+
const kBindingName = Symbol('kBindingName')
2123

2224
function setupInstance (self: WASI, instance: WebAssembly.Instance): void {
2325
validateObject(instance, 'instance')
@@ -29,6 +31,7 @@ function setupInstance (self: WASI, instance: WebAssembly.Instance): void {
2931

3032
/** @public */
3133
export interface WASIOptions {
34+
version?: 'unstable' | 'preview1'
3235
args?: string[] | undefined
3336
env?: Record<string, string> | undefined
3437
preopens?: Record<string, string> | undefined
@@ -68,17 +71,35 @@ export interface AsyncWASIOptions extends WASIOptions {
6871
asyncify?: Asyncify
6972
}
7073

71-
// /** @public */
72-
// export type WasiSnapshotPreview1 = Omit<_WASI, '_setMemory'>
73-
74-
function validateOptions (options: WASIOptions & { fs?: IFs | { promises: IFsPromises } }): {
74+
function validateOptions (this: WASI, options: WASIOptions & { fs?: IFs | { promises: IFsPromises } }): {
7575
args: string[]
7676
env: string[]
7777
preopens: Preopen[]
7878
stdio: readonly [0, 1, 2]
79+
_WASI: any
7980
} {
8081
validateObject(options, 'options')
8182

83+
let _WASI: any
84+
if (options.version !== undefined) {
85+
validateString(options.version, 'options.version')
86+
switch (options.version) {
87+
case 'unstable':
88+
_WASI = WASIPreview1
89+
this[kBindingName] = 'wasi_unstable'
90+
break
91+
case 'preview1':
92+
_WASI = WASIPreview1
93+
this[kBindingName] = 'wasi_snapshot_preview1'
94+
break
95+
default:
96+
throw new TypeError(`unsupported WASI version "${options.version as string}"`)
97+
}
98+
} else {
99+
_WASI = WASIPreview1
100+
this[kBindingName] = 'wasi_snapshot_preview1'
101+
}
102+
82103
if (options.args !== undefined) {
83104
validateArray(options.args, 'options.args')
84105
}
@@ -145,11 +166,12 @@ function validateOptions (options: WASIOptions & { fs?: IFs | { promises: IFsPro
145166
args,
146167
env,
147168
preopens,
148-
stdio
169+
stdio,
170+
_WASI
149171
}
150172
}
151173

152-
function initWASI (this: WASI, setMemory: (m: WebAssembly.Memory) => void, wrap: _WASI): void {
174+
function initWASI (this: WASI, setMemory: (m: WebAssembly.Memory) => void, wrap: any): void {
153175
this[kSetMemory] = setMemory;
154176
(this as any).wasiImport = wrap
155177
this[kStarted] = false
@@ -189,57 +211,18 @@ export class WASI {
189211
private [kStarted]!: boolean
190212
private [kExitCode]!: number
191213
private [kInstance]: WebAssembly.Instance | undefined
214+
private [kBindingName]!: string
192215

193216
public readonly wasiImport!: Record<string, any>
194217

195-
static createSync (options: SyncWASIOptions = kEmptyObject): WASI {
196-
return new WASI(options)
197-
}
198-
199-
static async createAsync (options: AsyncWASIOptions = kEmptyObject): Promise<WASI> {
200-
const {
201-
args,
202-
env,
203-
preopens,
204-
stdio
205-
} = validateOptions(options)
206-
207-
if (options.asyncify !== undefined) {
208-
validateObject(options.asyncify, 'options.asyncify')
209-
validateFunction(options.asyncify.wrapImportFunction, 'options.asyncify.wrapImportFunction')
210-
}
211-
212-
const wrap = await _WASI.createAsync(
213-
args,
214-
env,
215-
preopens,
216-
stdio,
217-
options.fs,
218-
options.print,
219-
options.printErr,
220-
options.asyncify
221-
)
222-
223-
const setMemory = wrap._setMemory!
224-
delete wrap._setMemory
225-
const _this = Object.create(WASI.prototype)
226-
initWASI.call(_this,
227-
setMemory,
228-
wrap
229-
)
230-
231-
if (options.returnOnExit) { wrap.proc_exit = wasiReturnOnProcExit.bind(_this) }
232-
233-
return _this
234-
}
235-
236218
public constructor (options: SyncWASIOptions = kEmptyObject) {
237219
const {
238220
args,
239221
env,
240222
preopens,
241-
stdio
242-
} = validateOptions(options)
223+
stdio,
224+
_WASI
225+
} = validateOptions.call(this, options)
243226

244227
const wrap = _WASI.createSync(
245228
args,
@@ -311,10 +294,53 @@ export class WASI {
311294
return (_initialize as () => any)()
312295
}
313296
}
297+
298+
getImportObject (): Record<string, Record<string, any>> {
299+
return { [this[kBindingName]]: this.wasiImport }
300+
}
314301
}
315302

316303
function wasiReturnOnProcExit (this: WASI, rval: exitcode): exitcode {
317304
this[kExitCode] = rval
318305
// eslint-disable-next-line @typescript-eslint/no-throw-literal
319306
throw kExitCode
320307
}
308+
309+
/** @public */
310+
export async function createAsyncWASI (options: AsyncWASIOptions = kEmptyObject): Promise<WASI> {
311+
const _this = Object.create(WASI.prototype)
312+
const {
313+
args,
314+
env,
315+
preopens,
316+
stdio,
317+
_WASI
318+
} = validateOptions.call(_this, options)
319+
320+
if (options.asyncify !== undefined) {
321+
validateObject(options.asyncify, 'options.asyncify')
322+
validateFunction(options.asyncify.wrapImportFunction, 'options.asyncify.wrapImportFunction')
323+
}
324+
325+
const wrap = await _WASI.createAsync(
326+
args,
327+
env,
328+
preopens,
329+
stdio,
330+
options.fs,
331+
options.print,
332+
options.printErr,
333+
options.asyncify
334+
)
335+
336+
const setMemory = wrap._setMemory!
337+
delete wrap._setMemory
338+
initWASI.call(_this,
339+
setMemory,
340+
wrap
341+
)
342+
343+
if (options.returnOnExit) { wrap.proc_exit = wasiReturnOnProcExit.bind(_this) }
344+
345+
return _this
346+
}

test/abort/index.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@
22

33
describe('abort', function () {
44
it('should throw RuntimeError', async function () {
5-
const wasi = wasmUtil.WASI.createSync({
5+
const wasi = new wasmUtil.WASI({
66
returnOnExit: true
77
})
8-
const { instance } = await wasmUtil.load('/test/abort/abort.wasm', {
9-
wasi_snapshot_preview1: wasi.wasiImport
10-
})
8+
const { instance } = await wasmUtil.load('/test/abort/abort.wasm', wasi.getImportObject())
119

1210
assertThrow(() => {
1311
wasi.start(instance)

test/assert/index.js

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,21 @@ describe('assert', function () {
44

55

66
it('assert false', async function () {
7-
const wasi = wasmUtil.WASI.createSync({
7+
const wasi = new wasmUtil.WASI({
88
returnOnExit: true
99
})
10-
const { instance } = await wasmUtil.load('/test/assert/assert_false.wasm', {
11-
wasi_snapshot_preview1: wasi.wasiImport
12-
})
10+
const { instance } = await wasmUtil.load('/test/assert/assert_false.wasm', wasi.getImportObject())
1311

1412
assertThrow(() => {
1513
wasi.start(instance)
1614
}, WebAssembly.RuntimeError, /unreachable/)
1715
})
1816

1917
it('assert true', async function () {
20-
const wasi = wasmUtil.WASI.createSync({
18+
const wasi = new wasmUtil.WASI({
2119
returnOnExit: true
2220
})
23-
const { instance } = await wasmUtil.load('/test/assert/assert_true.wasm', {
24-
wasi_snapshot_preview1: wasi.wasiImport
25-
})
21+
const { instance } = await wasmUtil.load('/test/assert/assert_true.wasm', wasi.getImportObject())
2622

2723
wasi.start(instance)
2824
})

test/asyncify/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
describe('asyncify', function () {
44
it('sleep 200ms', async function () {
5-
const wasi = wasmUtil.WASI.createSync({
5+
const wasi = new wasmUtil.WASI({
66
returnOnExit: true
77
})
88

@@ -16,7 +16,7 @@ describe('asyncify', function () {
1616
})
1717
})
1818
},
19-
wasi_snapshot_preview1: wasi.wasiImport
19+
...wasi.getImportObject()
2020
}
2121
const bytes = await (await fetch('/test/asyncify/asyncify.wasm')).arrayBuffer()
2222
const { instance } = await WebAssembly.instantiate(bytes, imports)

test/clock/index.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@
22

33
describe('clock', function () {
44
it('clock', async function () {
5-
const wasi = wasmUtil.WASI.createSync({
5+
const wasi = new wasmUtil.WASI({
66
returnOnExit: true
77
})
8-
const { instance } = await wasmUtil.load('/test/clock/clock.wasm', {
9-
wasi_snapshot_preview1: wasi.wasiImport
10-
})
8+
const { instance } = await wasmUtil.load('/test/clock/clock.wasm', wasi.getImportObject())
119

1210
wasi.start(instance)
1311
})

test/directory/index.js

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,14 @@ describe('directory', function () {
66
const vol = memfs.Volume.fromJSON({
77
'/fopen-directory-parent-directory.dir': null
88
})
9-
const wasi = wasmUtil.WASI.createSync({
9+
const wasi = new wasmUtil.WASI({
1010
returnOnExit: true,
1111
preopens: {
1212
'fopen-directory-parent-directory.dir': 'fopen-directory-parent-directory.dir'
1313
},
1414
fs: memfs.createFsFromVolume(vol)
1515
})
16-
const { instance } = await wasmUtil.load('/test/directory/directory.wasm', {
17-
wasi_snapshot_preview1: wasi.wasiImport
18-
})
16+
const { instance } = await wasmUtil.load('/test/directory/directory.wasm', wasi.getImportObject())
1917

2018
wasi.start(instance)
2119
})
@@ -25,17 +23,15 @@ describe('directory', function () {
2523
'/fopen-directory-parent-directory.dir': null
2624
})
2725
const asyncify = new wasmUtil.Asyncify()
28-
const wasi = await wasmUtil.WASI.createAsync({
26+
const wasi = await wasmUtil.createAsyncWASI({
2927
returnOnExit: true,
3028
preopens: {
3129
'fopen-directory-parent-directory.dir': 'fopen-directory-parent-directory.dir'
3230
},
3331
fs: memfs.createFsFromVolume(vol),
3432
asyncify: asyncify
3533
})
36-
const { instance } = await wasmUtil.load('/test/directory/directory_asyncify.wasm', {
37-
wasi_snapshot_preview1: wasi.wasiImport
38-
})
34+
const { instance } = await wasmUtil.load('/test/directory/directory_asyncify.wasm', wasi.getImportObject())
3935
const wrappedInstance = asyncify.init(instance.exports.memory, instance, {
4036
wrapExports: ['_start']
4137
})
@@ -47,16 +43,14 @@ describe('directory', function () {
4743
const vol = memfs.Volume.fromJSON({
4844
'/fopen-directory-parent-directory.dir': null
4945
})
50-
const wasi = await wasmUtil.WASI.createAsync({
46+
const wasi = await wasmUtil.createAsyncWASI({
5147
returnOnExit: true,
5248
preopens: {
5349
'fopen-directory-parent-directory.dir': 'fopen-directory-parent-directory.dir'
5450
},
5551
fs: memfs.createFsFromVolume(vol)
5652
})
57-
const { instance } = await wasmUtil.load('/test/directory/directory_jspi.wasm', {
58-
wasi_snapshot_preview1: wasi.wasiImport
59-
})
53+
const { instance } = await wasmUtil.load('/test/directory/directory_jspi.wasm', wasi.getImportObject())
6054
const wrappedInstance = { exports: wasmUtil.wrapExports(instance.exports, ['_start']) }
6155

6256
await wasi.start(wrappedInstance)

test/exit/index.js

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,20 @@ describe('exit', function () {
44

55

66
it('exit failure', async function () {
7-
const wasi = wasmUtil.WASI.createSync({
7+
const wasi = new wasmUtil.WASI({
88
returnOnExit: true
99
})
10-
const { instance } = await wasmUtil.load('/test/exit/exit_failure.wasm', {
11-
wasi_snapshot_preview1: wasi.wasiImport
12-
})
10+
const { instance } = await wasmUtil.load('/test/exit/exit_failure.wasm', wasi.getImportObject())
1311

1412
const code = wasi.start(instance)
1513
assert(code === 1)
1614
})
1715

1816
it('exit success', async function () {
19-
const wasi = wasmUtil.WASI.createSync({
17+
const wasi = new wasmUtil.WASI({
2018
returnOnExit: true
2119
})
22-
const { instance } = await wasmUtil.load('/test/exit/exit_success.wasm', {
23-
wasi_snapshot_preview1: wasi.wasiImport
24-
})
20+
const { instance } = await wasmUtil.load('/test/exit/exit_success.wasm', wasi.getImportObject())
2521

2622
const code = wasi.start(instance)
2723
assert(code === 0)

0 commit comments

Comments
 (0)