/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
tests/unit_node/wasi_test.ts
221 строка
8 KB
em
fix(ext/node): implement node:wasi preview1 compat (#34245)
20 май 2026, 16:12
Не верифицирован
20 май 2026, 16:12
ed12991
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. // deno-lint-ignore-file no-explicit-any camelcase import { WASI } from "node:wasi"; import { assert, assertEquals, assertThrows } from "@std/assert"; // Compiled from: // (module // (import "wasi_snapshot_preview1" "fd_write" // (func $fd_write (param i32 i32 i32 i32) (result i32))) // (memory 1) // (export "memory" (memory 0)) // (data (i32.const 8) "hello world\n") // (func $main (export "_start") // (i32.store (i32.const 0) (i32.const 8)) // (i32.store (i32.const 4) (i32.const 12)) // (call $fd_write (i32.const 1) (i32.const 0) (i32.const 1) (i32.const 20)) // drop)) // deno-fmt-ignore const HELLO_WASM = new Uint8Array([ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0c, 0x02, 0x60, 0x04, 0x7f, 0x7f, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x00, 0x00, 0x02, 0x23, 0x01, 0x16, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x08, 0x66, 0x64, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x00, 0x00, 0x03, 0x02, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x13, 0x02, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x06, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x01, 0x0a, 0x1d, 0x01, 0x1b, 0x00, 0x41, 0x00, 0x41, 0x08, 0x36, 0x02, 0x00, 0x41, 0x04, 0x41, 0x0c, 0x36, 0x02, 0x00, 0x41, 0x01, 0x41, 0x00, 0x41, 0x01, 0x41, 0x14, 0x10, 0x00, 0x1a, 0x0b, 0x0b, 0x12, 0x01, 0x00, 0x41, 0x08, 0x0b, 0x0c, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x0a, ]); // (module (memory 1) (export "memory" (memory 0))) // deno-fmt-ignore const MEMONLY_WASM = new Uint8Array([ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x0a, 0x01, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, ]); Deno.test("[node/wasi] - WASI constructor validates version", () => { assertThrows( () => new WASI({ version: "invalid" as any }), TypeError, ); }); Deno.test("[node/wasi] - WASI constructor accepts preview1", () => { const wasi = new WASI({ version: "preview1" }); assert(wasi); assert(wasi.wasiImport); }); Deno.test("[node/wasi] - WASI constructor accepts unstable", () => { const wasi = new WASI({ version: "unstable" }); assert(wasi); }); Deno.test("[node/wasi] - getImportObject returns correct key for preview1", () => { const wasi = new WASI({ version: "preview1" }); const importObj = wasi.getImportObject(); assert("wasi_snapshot_preview1" in importObj); }); Deno.test("[node/wasi] - getImportObject returns correct key for unstable", () => { const wasi = new WASI({ version: "unstable" }); const importObj = wasi.getImportObject(); assert("wasi_unstable" in importObj); }); Deno.test("[node/wasi] - start() runs hello world wasm", () => { const wasi = new WASI({ version: "preview1" }); const module = new WebAssembly.Module(HELLO_WASM); const instance = new WebAssembly.Instance( module, wasi.getImportObject() as WebAssembly.Imports, ); const exitCode = wasi.start(instance); assertEquals(exitCode, 0); }); Deno.test("[node/wasi] - start() throws if called twice", () => { const wasi = new WASI({ version: "preview1" }); const module = new WebAssembly.Module(HELLO_WASM); const instance = new WebAssembly.Instance( module, wasi.getImportObject() as WebAssembly.Imports, ); wasi.start(instance); assertThrows(() => wasi.start(instance), Error, "already started"); }); Deno.test("[node/wasi] - start() throws without _start export", () => { const wasi = new WASI({ version: "preview1" }); const module = new WebAssembly.Module(MEMONLY_WASM); const instance = new WebAssembly.Instance(module, {}); assertThrows(() => wasi.start(instance), Error, "_start"); }); Deno.test("[node/wasi] - WASI constructor with args and env", () => { const wasi = new WASI({ version: "preview1", args: ["test", "--flag"], env: { FOO: "bar" }, }); assert(wasi); }); Deno.test("[node/wasi] - WASI constructor coerces args, env, and preopens", () => { const wasi = new WASI({ version: "preview1", args: [123, true] as any, env: { FOO: 1, BAR: false } as any, preopens: { "/sandbox": { toString: () => Deno.cwd() }, } as any, }); const module = new WebAssembly.Module(MEMONLY_WASM); const instance = new WebAssembly.Instance(module, {}); wasi.initialize(instance); const memory = new Uint8Array( (instance.exports.memory as WebAssembly.Memory).buffer, ); const view = new DataView(memory.buffer); assertEquals(wasi.wasiImport.args_sizes_get(0, 4), 0); assertEquals(view.getUint32(0, true), 2); assertEquals(view.getUint32(4, true), "123\0true\0".length); assertEquals(wasi.wasiImport.environ_sizes_get(8, 12), 0); assertEquals(view.getUint32(8, true), 2); assertEquals(view.getUint32(12, true), "FOO=1\0BAR=false\0".length); }); Deno.test("[node/wasi] - WASI constructor with preopens", () => { const wasi = new WASI({ version: "preview1", preopens: { "/sandbox": Deno.cwd() }, }); assert(wasi); }); Deno.test("[node/wasi] - wasiImport has all expected functions", () => { const wasi = new WASI({ version: "preview1" }); const expectedFunctions = [ "args_get", "args_sizes_get", "environ_get", "environ_sizes_get", "clock_res_get", "clock_time_get", "random_get", "proc_exit", "fd_write", "fd_read", "fd_seek", "fd_close", "fd_fdstat_get", "fd_prestat_get", "fd_prestat_dir_name", "path_open", "path_link", "sched_yield", "poll_oneoff", ]; for (const fn_name of expectedFunctions) { assertEquals( typeof wasi.wasiImport[fn_name], "function", `wasiImport.${fn_name} should be a function`, ); } }); Deno.test("[node/wasi] - finalizeBindings binds wasm memory", () => { const wasi = new WASI({ version: "preview1" }); const module = new WebAssembly.Module(MEMONLY_WASM); const instance = new WebAssembly.Instance(module, {}); // finalizeBindings is a Node-only API used by `wasi:thread-spawn` // implementations in workers: it associates a memory with the WASI // instance without invoking _start. (wasi as any).finalizeBindings(instance, { memory: instance.exports.memory as WebAssembly.Memory, }); // After binding, the wasi imports should accept calls. environ_sizes_get // is read-only so we can exercise it without further setup. const memory = new Uint8Array( (instance.exports.memory as WebAssembly.Memory).buffer, ); const view = new DataView(memory.buffer); assertEquals(wasi.wasiImport.environ_sizes_get(0, 4), 0); // Default env is empty when not supplied. assertEquals(view.getUint32(0, true), 0); }); Deno.test("[node/wasi] - constructor emits ExperimentalWarning with the expected message", async () => { const warnings: { name?: string; message?: string }[] = []; const proc = (globalThis as any).process; const listener = (w: { name?: string; message?: string }) => { if (w?.name === "ExperimentalWarning") warnings.push(w); }; proc.on("warning", listener); try { new WASI({ version: "preview1" }); new WASI({ version: "preview1" }); // emitWarning uses process.nextTick, so wait one tick. await new Promise((r) => setTimeout(r, 0)); // We can't guarantee uniqueness across the whole test process (the // warning is module-scoped, so a prior test may have already triggered // it), but if any fire they must carry the WASI message. for (const w of warnings) { assert(typeof w.message === "string"); assert(w.message!.includes("WASI is an experimental feature")); } } finally { proc.off("warning", listener); } });