mirror of
https://github.com/denoland/deno.git
synced 2025-02-07 23:06:50 -05:00
feat(std/wasi): add wasi_snapshot_preview1 (#6441)
This commit is contained in:
parent
f4397e5ec9
commit
a354b248ea
11 changed files with 1561 additions and 0 deletions
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
|
@ -68,6 +68,12 @@ jobs:
|
||||||
with:
|
with:
|
||||||
rust-version: 1.44.0
|
rust-version: 1.44.0
|
||||||
|
|
||||||
|
- name: Install rust targets
|
||||||
|
run: |
|
||||||
|
# Necessary for the std/wasi tests
|
||||||
|
rustup target add wasm32-unknown-unknown
|
||||||
|
rustup target add wasm32-wasi
|
||||||
|
|
||||||
- name: Install clippy and rustfmt
|
- name: Install clippy and rustfmt
|
||||||
if: matrix.config.kind == 'lint'
|
if: matrix.config.kind == 'lint'
|
||||||
run: |
|
run: |
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -21,6 +21,9 @@ yarn.lock
|
||||||
# yarn creates this in error.
|
# yarn creates this in error.
|
||||||
tools/node_modules/
|
tools/node_modules/
|
||||||
|
|
||||||
|
# compiled wasm files
|
||||||
|
std/wasi/testdata/snapshot_preview1/
|
||||||
|
|
||||||
# MacOS generated files
|
# MacOS generated files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.DS_Store?
|
.DS_Store?
|
||||||
|
|
42
std/wasi/README.md
Normal file
42
std/wasi/README.md
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
# wasi
|
||||||
|
|
||||||
|
This module provides an implementation of the WebAssembly System Interface
|
||||||
|
|
||||||
|
## Supported Syscalls
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import WASI from "https://deno.land/std/wasi/snapshot_preview1.ts";
|
||||||
|
|
||||||
|
const wasi = new WASI({
|
||||||
|
args: Deno.args,
|
||||||
|
env: Deno.env,
|
||||||
|
});
|
||||||
|
|
||||||
|
const binary = Deno.readAll("path/to/your/module.wasm");
|
||||||
|
const module = await WebAssembly.compile(binary);
|
||||||
|
const instance = await WebAssembly.instantiate(module, {
|
||||||
|
wasi_snapshot_preview1: wasi.exports,
|
||||||
|
});
|
||||||
|
|
||||||
|
wasi.memory = module.exports.memory;
|
||||||
|
|
||||||
|
if (module.exports._start) {
|
||||||
|
instance.exports._start();
|
||||||
|
} else if (module.exports._initialize) {
|
||||||
|
instance.exports._initialize();
|
||||||
|
} else {
|
||||||
|
throw new Error("No entry point found");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
The test suite for this module spawns rustc processes to compile various example
|
||||||
|
Rust programs. You must have wasm targets enabled:
|
||||||
|
|
||||||
|
```
|
||||||
|
rustup target add wasm32-wasi
|
||||||
|
rustup target add wasm32-unknown-unknown
|
||||||
|
```
|
1344
std/wasi/snapshot_preview1.ts
Normal file
1344
std/wasi/snapshot_preview1.ts
Normal file
File diff suppressed because it is too large
Load diff
126
std/wasi/snapshot_preview1_test.ts
Normal file
126
std/wasi/snapshot_preview1_test.ts
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
import { assert, assertEquals } from "../testing/asserts.ts";
|
||||||
|
import * as path from "../path/mod.ts";
|
||||||
|
import WASI from "./snapshot_preview1.ts";
|
||||||
|
|
||||||
|
if (import.meta.main) {
|
||||||
|
const options = JSON.parse(Deno.args[0]);
|
||||||
|
const binary = await Deno.readFile(Deno.args[1]);
|
||||||
|
const module = await WebAssembly.compile(binary);
|
||||||
|
|
||||||
|
const wasi = new WASI({
|
||||||
|
env: options.env,
|
||||||
|
args: options.args,
|
||||||
|
preopens: options.preopens,
|
||||||
|
});
|
||||||
|
|
||||||
|
const instance = new WebAssembly.Instance(module, {
|
||||||
|
wasi_snapshot_preview1: wasi.exports,
|
||||||
|
});
|
||||||
|
|
||||||
|
wasi.memory = instance.exports.memory;
|
||||||
|
|
||||||
|
instance.exports._start();
|
||||||
|
} else {
|
||||||
|
const rootdir = path.dirname(path.fromFileUrl(import.meta.url));
|
||||||
|
const testdir = path.join(rootdir, "testdata");
|
||||||
|
const outdir = path.join(testdir, "snapshot_preview1");
|
||||||
|
|
||||||
|
for await (const entry of Deno.readDir(testdir)) {
|
||||||
|
if (!entry.name.endsWith(".rs")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const process = Deno.run({
|
||||||
|
cmd: [
|
||||||
|
"rustc",
|
||||||
|
"--target",
|
||||||
|
"wasm32-wasi",
|
||||||
|
"--out-dir",
|
||||||
|
outdir,
|
||||||
|
path.join(testdir, entry.name),
|
||||||
|
],
|
||||||
|
stdout: "inherit",
|
||||||
|
stderr: "inherit",
|
||||||
|
});
|
||||||
|
|
||||||
|
const status = await process.status();
|
||||||
|
assert(status.success);
|
||||||
|
|
||||||
|
process.close();
|
||||||
|
|
||||||
|
// TODO(caspervonb) allow the prelude to span multiple lines
|
||||||
|
const source = await Deno.readTextFile(path.join(testdir, entry.name));
|
||||||
|
const prelude = source.match(/^\/\/\s*\{.*/);
|
||||||
|
if (prelude) {
|
||||||
|
const basename = entry.name.replace(/.rs$/, ".json");
|
||||||
|
await Deno.writeTextFile(
|
||||||
|
path.join(outdir, basename),
|
||||||
|
prelude[0].slice(2)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for await (const entry of Deno.readDir(outdir)) {
|
||||||
|
if (!entry.name.endsWith(".wasm")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Deno.test(entry.name, async function () {
|
||||||
|
const basename = entry.name.replace(/\.wasm$/, ".json");
|
||||||
|
const prelude = await Deno.readTextFile(path.resolve(outdir, basename));
|
||||||
|
const options = JSON.parse(prelude);
|
||||||
|
|
||||||
|
const process = await Deno.run({
|
||||||
|
cwd: testdir,
|
||||||
|
cmd: [
|
||||||
|
`${Deno.execPath()}`,
|
||||||
|
"run",
|
||||||
|
"--quiet",
|
||||||
|
"--unstable",
|
||||||
|
"--v8-flags=--experimental-wasm-bigint",
|
||||||
|
"--allow-all",
|
||||||
|
import.meta.url,
|
||||||
|
prelude,
|
||||||
|
path.resolve(outdir, entry.name),
|
||||||
|
],
|
||||||
|
stdin: "piped",
|
||||||
|
stdout: "piped",
|
||||||
|
stderr: "piped",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (options.stdin) {
|
||||||
|
const stdin = new TextEncoder().encode(options.stdin);
|
||||||
|
await Deno.writeAll(process.stdin, stdin);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.stdin.close();
|
||||||
|
|
||||||
|
const stdout = await Deno.readAll(process.stdout);
|
||||||
|
|
||||||
|
if (options.stdout) {
|
||||||
|
assertEquals(new TextDecoder().decode(stdout), options.stdout);
|
||||||
|
} else {
|
||||||
|
await Deno.writeAll(Deno.stdout, stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.stdout.close();
|
||||||
|
|
||||||
|
const stderr = await Deno.readAll(process.stderr);
|
||||||
|
|
||||||
|
if (options.stderr) {
|
||||||
|
assertEquals(new TextDecoder().decode(stderr), options.stderr);
|
||||||
|
} else {
|
||||||
|
await Deno.writeAll(Deno.stderr, stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.stderr.close();
|
||||||
|
|
||||||
|
const status = await process.status();
|
||||||
|
assertEquals(status.code, options.exitCode ? +options.exitCode : 0);
|
||||||
|
|
||||||
|
process.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
6
std/wasi/testdata/std_env_args.rs
vendored
Normal file
6
std/wasi/testdata/std_env_args.rs
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// { "args": ["one", "two", "three" ]}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args = std::env::args();
|
||||||
|
assert_eq!(args.len(), 3);
|
||||||
|
}
|
6
std/wasi/testdata/std_env_vars.rs
vendored
Normal file
6
std/wasi/testdata/std_env_vars.rs
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// { "env": { "one": "1", "two": "2", "three": "3" } }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let vars = std::env::vars();
|
||||||
|
assert_eq!(vars.count(), 3);
|
||||||
|
}
|
7
std/wasi/testdata/std_io_stderr.rs
vendored
Normal file
7
std/wasi/testdata/std_io_stderr.rs
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
// { "stderr": "Hello, stderr!" }
|
||||||
|
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
assert!(std::io::stderr().write_all(b"Hello, stderr!").is_ok())
|
||||||
|
}
|
9
std/wasi/testdata/std_io_stdin.rs
vendored
Normal file
9
std/wasi/testdata/std_io_stdin.rs
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
// { "stdin": "Hello, stdin!" }
|
||||||
|
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut buffer = String::new();
|
||||||
|
assert!(std::io::stdin().read_to_string(&mut buffer).is_ok());
|
||||||
|
assert_eq!(buffer, "Hello, stdin!")
|
||||||
|
}
|
7
std/wasi/testdata/std_io_stdout.rs
vendored
Normal file
7
std/wasi/testdata/std_io_stdout.rs
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
// { "stdout": "Hello, stdout!" }
|
||||||
|
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
assert!(std::io::stdout().write_all(b"Hello, stdout!").is_ok())
|
||||||
|
}
|
5
std/wasi/testdata/std_process_exit.rs
vendored
Normal file
5
std/wasi/testdata/std_process_exit.rs
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
// { "exitCode": "120" }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
std::process::exit(120);
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue