Support WASI snapshot0 in the C API.
This commit adds support for snapshot0 in the WASI C API. A name parameter was added to `wasi_instance_new` to accept which WASI module is being instantiated. Additionally, the C# API now supports constructing a WASI instance based on the WASI module name. Fixes #1221.
This commit is contained in:
@@ -52,6 +52,7 @@ WASI_DECLARE_OWN(instance)
|
|||||||
|
|
||||||
WASI_API_EXTERN own wasi_instance_t* wasi_instance_new(
|
WASI_API_EXTERN own wasi_instance_t* wasi_instance_new(
|
||||||
wasm_store_t* store,
|
wasm_store_t* store,
|
||||||
|
const char* name,
|
||||||
own wasi_config_t* config,
|
own wasi_config_t* config,
|
||||||
own wasm_trap_t** trap
|
own wasm_trap_t** trap
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ pub unsafe extern "C" fn wasmtime_linker_define_wasi(
|
|||||||
instance: *const wasi_instance_t,
|
instance: *const wasi_instance_t,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let linker = &mut (*linker).linker;
|
let linker = &mut (*linker).linker;
|
||||||
(*instance).wasi.add_to_linker(linker).is_ok()
|
(*instance).add_to_linker(linker).is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
//! The WASI embedding API definitions for Wasmtime.
|
//! The WASI embedding API definitions for Wasmtime.
|
||||||
use crate::{wasm_extern_t, wasm_importtype_t, wasm_store_t, wasm_trap_t, ExternHost, ExternType};
|
use crate::{wasm_extern_t, wasm_importtype_t, wasm_store_t, wasm_trap_t, ExternHost, ExternType};
|
||||||
|
use anyhow::Result;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::os::raw::{c_char, c_int};
|
use std::os::raw::{c_char, c_int};
|
||||||
use std::path::Path;
|
use std::path::{Path, PathBuf};
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use wasi_common::{preopen_dir, WasiCtxBuilder};
|
use wasi_common::{
|
||||||
use wasmtime::{HostRef, Trap};
|
old::snapshot_0::WasiCtxBuilder as WasiSnapshot0CtxBuilder, preopen_dir,
|
||||||
use wasmtime_wasi::Wasi;
|
WasiCtxBuilder as WasiPreview1CtxBuilder,
|
||||||
|
};
|
||||||
|
use wasmtime::{HostRef, Linker, Store, Trap};
|
||||||
|
use wasmtime_wasi::{old::snapshot_0::Wasi as WasiSnapshot0, Wasi as WasiPreview1};
|
||||||
|
|
||||||
unsafe fn cstr_to_path<'a>(path: *const c_char) -> Option<&'a Path> {
|
unsafe fn cstr_to_path<'a>(path: *const c_char) -> Option<&'a Path> {
|
||||||
CStr::from_ptr(path).to_str().map(Path::new).ok()
|
CStr::from_ptr(path).to_str().map(Path::new).ok()
|
||||||
@@ -22,18 +26,32 @@ unsafe fn create_file(path: *const c_char) -> Option<File> {
|
|||||||
File::create(cstr_to_path(path)?).ok()
|
File::create(cstr_to_path(path)?).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
pub enum WasiModule {
|
||||||
pub struct wasi_config_t {
|
Snapshot0(WasiSnapshot0),
|
||||||
builder: WasiCtxBuilder,
|
Preview1(WasiPreview1),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl wasi_config_t {}
|
impl WasiModule {}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct wasi_config_t {
|
||||||
|
args: Vec<Vec<u8>>,
|
||||||
|
env: Vec<(Vec<u8>, Vec<u8>)>,
|
||||||
|
stdin: Option<File>,
|
||||||
|
stdout: Option<File>,
|
||||||
|
stderr: Option<File>,
|
||||||
|
preopens: Vec<(File, PathBuf)>,
|
||||||
|
inherit_args: bool,
|
||||||
|
inherit_env: bool,
|
||||||
|
inherit_stdin: bool,
|
||||||
|
inherit_stdout: bool,
|
||||||
|
inherit_stderr: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn wasi_config_new() -> *mut wasi_config_t {
|
pub unsafe extern "C" fn wasi_config_new() -> *mut wasi_config_t {
|
||||||
Box::into_raw(Box::new(wasi_config_t {
|
Box::into_raw(Box::new(wasi_config_t::default()))
|
||||||
builder: WasiCtxBuilder::new(),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@@ -47,16 +65,17 @@ pub unsafe extern "C" fn wasi_config_set_argv(
|
|||||||
argc: c_int,
|
argc: c_int,
|
||||||
argv: *const *const c_char,
|
argv: *const *const c_char,
|
||||||
) {
|
) {
|
||||||
(*config).builder.args(
|
(*config).args = slice::from_raw_parts(argv, argc as usize)
|
||||||
slice::from_raw_parts(argv, argc as usize)
|
.iter()
|
||||||
.iter()
|
.map(|p| CStr::from_ptr(*p).to_bytes().to_owned())
|
||||||
.map(|a| slice::from_raw_parts(*a as *const u8, CStr::from_ptr(*a).to_bytes().len())),
|
.collect();
|
||||||
);
|
(*config).inherit_args = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn wasi_config_inherit_argv(config: *mut wasi_config_t) {
|
pub unsafe extern "C" fn wasi_config_inherit_argv(config: *mut wasi_config_t) {
|
||||||
(*config).builder.inherit_args();
|
(*config).args.clear();
|
||||||
|
(*config).inherit_args = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@@ -69,17 +88,22 @@ pub unsafe extern "C" fn wasi_config_set_env(
|
|||||||
let names = slice::from_raw_parts(names, envc as usize);
|
let names = slice::from_raw_parts(names, envc as usize);
|
||||||
let values = slice::from_raw_parts(values, envc as usize);
|
let values = slice::from_raw_parts(values, envc as usize);
|
||||||
|
|
||||||
(*config).builder.envs(
|
(*config).env = names
|
||||||
names
|
.iter()
|
||||||
.iter()
|
.map(|p| CStr::from_ptr(*p).to_bytes().to_owned())
|
||||||
.map(|p| CStr::from_ptr(*p).to_bytes())
|
.zip(
|
||||||
.zip(values.iter().map(|p| CStr::from_ptr(*p).to_bytes())),
|
values
|
||||||
);
|
.iter()
|
||||||
|
.map(|p| CStr::from_ptr(*p).to_bytes().to_owned()),
|
||||||
|
)
|
||||||
|
.collect();
|
||||||
|
(*config).inherit_env = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn wasi_config_inherit_env(config: *mut wasi_config_t) {
|
pub unsafe extern "C" fn wasi_config_inherit_env(config: *mut wasi_config_t) {
|
||||||
(*config).builder.inherit_env();
|
(*config).env.clear();
|
||||||
|
(*config).inherit_env = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@@ -92,14 +116,16 @@ pub unsafe extern "C" fn wasi_config_set_stdin_file(
|
|||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
(*config).builder.stdin(file);
|
(*config).stdin = Some(file);
|
||||||
|
(*config).inherit_stdin = false;
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn wasi_config_inherit_stdin(config: *mut wasi_config_t) {
|
pub unsafe extern "C" fn wasi_config_inherit_stdin(config: *mut wasi_config_t) {
|
||||||
(*config).builder.inherit_stdin();
|
(*config).stdin = None;
|
||||||
|
(*config).inherit_stdin = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@@ -112,14 +138,16 @@ pub unsafe extern "C" fn wasi_config_set_stdout_file(
|
|||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
(*config).builder.stdout(file);
|
(*config).stdout = Some(file);
|
||||||
|
(*config).inherit_stdout = false;
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn wasi_config_inherit_stdout(config: *mut wasi_config_t) {
|
pub unsafe extern "C" fn wasi_config_inherit_stdout(config: *mut wasi_config_t) {
|
||||||
(*config).builder.inherit_stdout();
|
(*config).stdout = None;
|
||||||
|
(*config).inherit_stdout = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@@ -132,14 +160,16 @@ pub unsafe extern "C" fn wasi_config_set_stderr_file(
|
|||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
(*config).builder.stderr(file);
|
(*config).stderr = Some(file);
|
||||||
|
(*config).inherit_stderr = false;
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn wasi_config_inherit_stderr(config: *mut wasi_config_t) {
|
pub unsafe extern "C" fn wasi_config_inherit_stderr(config: *mut wasi_config_t) {
|
||||||
(*config).builder.inherit_stderr();
|
(*config).stderr = None;
|
||||||
|
(*config).inherit_stderr = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@@ -161,29 +191,110 @@ pub unsafe extern "C" fn wasi_config_preopen_dir(
|
|||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
(*config).builder.preopened_dir(dir, guest_path);
|
(*config).preopens.push((dir, guest_path.to_owned()));
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum WasiInstance {
|
||||||
|
Preview1(WasiPreview1),
|
||||||
|
Snapshot0(WasiSnapshot0),
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! config_to_builder {
|
||||||
|
($builder:ident, $config:ident) => {{
|
||||||
|
let mut builder = $builder::new();
|
||||||
|
|
||||||
|
if $config.inherit_args {
|
||||||
|
builder.inherit_args();
|
||||||
|
} else if !$config.args.is_empty() {
|
||||||
|
builder.args($config.args);
|
||||||
|
}
|
||||||
|
|
||||||
|
if $config.inherit_env {
|
||||||
|
builder.inherit_env();
|
||||||
|
} else if !$config.env.is_empty() {
|
||||||
|
builder.envs($config.env);
|
||||||
|
}
|
||||||
|
|
||||||
|
if $config.inherit_stdin {
|
||||||
|
builder.inherit_stdin();
|
||||||
|
} else if let Some(file) = $config.stdin {
|
||||||
|
builder.stdin(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
if $config.inherit_stdout {
|
||||||
|
builder.inherit_stdout();
|
||||||
|
} else if let Some(file) = $config.stdout {
|
||||||
|
builder.stdout(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
if $config.inherit_stderr {
|
||||||
|
builder.inherit_stderr();
|
||||||
|
} else if let Some(file) = $config.stderr {
|
||||||
|
builder.stderr(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
for preopen in $config.preopens {
|
||||||
|
builder.preopened_dir(preopen.0, preopen.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_snapshot0_instance(store: &Store, config: wasi_config_t) -> Result<WasiInstance, String> {
|
||||||
|
Ok(WasiInstance::Snapshot0(WasiSnapshot0::new(
|
||||||
|
store,
|
||||||
|
config_to_builder!(WasiSnapshot0CtxBuilder, config)
|
||||||
|
.build()
|
||||||
|
.map_err(|e| e.to_string())?,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_preview1_instance(store: &Store, config: wasi_config_t) -> Result<WasiInstance, String> {
|
||||||
|
Ok(WasiInstance::Preview1(WasiPreview1::new(
|
||||||
|
store,
|
||||||
|
config_to_builder!(WasiPreview1CtxBuilder, config)
|
||||||
|
.build()
|
||||||
|
.map_err(|e| e.to_string())?,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct wasi_instance_t {
|
pub struct wasi_instance_t {
|
||||||
pub wasi: Wasi,
|
wasi: WasiInstance,
|
||||||
export_cache: HashMap<String, Box<wasm_extern_t>>,
|
export_cache: HashMap<String, Box<wasm_extern_t>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl wasi_instance_t {
|
||||||
|
pub fn add_to_linker(&self, linker: &mut Linker) -> Result<()> {
|
||||||
|
match &self.wasi {
|
||||||
|
WasiInstance::Snapshot0(w) => w.add_to_linker(linker),
|
||||||
|
WasiInstance::Preview1(w) => w.add_to_linker(linker),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn wasi_instance_new(
|
pub unsafe extern "C" fn wasi_instance_new(
|
||||||
store: *mut wasm_store_t,
|
store: *mut wasm_store_t,
|
||||||
|
name: *const c_char,
|
||||||
config: *mut wasi_config_t,
|
config: *mut wasi_config_t,
|
||||||
trap: *mut *mut wasm_trap_t,
|
trap: *mut *mut wasm_trap_t,
|
||||||
) -> *mut wasi_instance_t {
|
) -> *mut wasi_instance_t {
|
||||||
let store = &(*store).store.borrow();
|
let store = &(*store).store.borrow();
|
||||||
let mut config = Box::from_raw(config);
|
let config = Box::from_raw(config);
|
||||||
|
|
||||||
match config.builder.build() {
|
let result = match CStr::from_ptr(name).to_str().unwrap_or("") {
|
||||||
Ok(ctx) => Box::into_raw(Box::new(wasi_instance_t {
|
"wasi_snapshot_preview1" => create_preview1_instance(store, *config),
|
||||||
wasi: Wasi::new(store, ctx),
|
"wasi_unstable" => create_snapshot0_instance(store, *config),
|
||||||
|
_ => Err("unsupported WASI version".into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(wasi) => Box::into_raw(Box::new(wasi_instance_t {
|
||||||
|
wasi,
|
||||||
export_cache: HashMap::new(),
|
export_cache: HashMap::new(),
|
||||||
})),
|
})),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -206,20 +317,32 @@ pub unsafe extern "C" fn wasi_instance_bind_import(
|
|||||||
instance: *mut wasi_instance_t,
|
instance: *mut wasi_instance_t,
|
||||||
import: *const wasm_importtype_t,
|
import: *const wasm_importtype_t,
|
||||||
) -> *const wasm_extern_t {
|
) -> *const wasm_extern_t {
|
||||||
// TODO: support previous versions?
|
|
||||||
if (*import).ty.module() != "wasi_snapshot_preview1" {
|
|
||||||
return std::ptr::null_mut();
|
|
||||||
}
|
|
||||||
|
|
||||||
// The import should be a function (WASI only exports functions)
|
// The import should be a function (WASI only exports functions)
|
||||||
let func_type = match (*import).ty.ty() {
|
let func_type = match (*import).ty.ty() {
|
||||||
ExternType::Func(f) => f,
|
ExternType::Func(f) => f,
|
||||||
_ => return std::ptr::null_mut(),
|
_ => return std::ptr::null_mut(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let module = (*import).ty.module();
|
||||||
let name = (*import).ty.name();
|
let name = (*import).ty.name();
|
||||||
|
|
||||||
match (*instance).wasi.get_export(name) {
|
let import = match &(*instance).wasi {
|
||||||
|
WasiInstance::Preview1(wasi) => {
|
||||||
|
if module != "wasi_snapshot_preview1" {
|
||||||
|
return std::ptr::null();
|
||||||
|
}
|
||||||
|
wasi.get_export(name)
|
||||||
|
}
|
||||||
|
WasiInstance::Snapshot0(wasi) => {
|
||||||
|
if module != "wasi_unstable" {
|
||||||
|
return std::ptr::null();
|
||||||
|
}
|
||||||
|
|
||||||
|
wasi.get_export(name)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match import {
|
||||||
Some(export) => {
|
Some(export) => {
|
||||||
if export.ty() != func_type {
|
if export.ty() != func_type {
|
||||||
return std::ptr::null_mut();
|
return std::ptr::null_mut();
|
||||||
|
|||||||
@@ -936,6 +936,7 @@ namespace Wasmtime
|
|||||||
[DllImport(LibraryName)]
|
[DllImport(LibraryName)]
|
||||||
public static extern WasiInstanceHandle wasi_instance_new(
|
public static extern WasiInstanceHandle wasi_instance_new(
|
||||||
StoreHandle store,
|
StoreHandle store,
|
||||||
|
[MarshalAs(UnmanagedType.LPUTF8Str)] string name,
|
||||||
WasiConfigHandle config,
|
WasiConfigHandle config,
|
||||||
out IntPtr trap
|
out IntPtr trap
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,18 +9,26 @@ namespace Wasmtime
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a default <see cref="Wasi"/> instance.
|
/// Creates a default <see cref="Wasi"/> instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Wasi(Store store) :
|
/// <param name="store">The store to use for the new WASI instance.</param>
|
||||||
|
/// <param name="name">The name of the WASI module to create.</param>
|
||||||
|
public Wasi(Store store, string name) :
|
||||||
this(
|
this(
|
||||||
(store ?? throw new ArgumentNullException(nameof(store))).Handle,
|
(store ?? throw new ArgumentNullException(nameof(store))).Handle,
|
||||||
Interop.wasi_config_new()
|
Interop.wasi_config_new(),
|
||||||
|
name
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Wasi(Interop.StoreHandle store, Interop.WasiConfigHandle config)
|
internal Wasi(Interop.StoreHandle store, Interop.WasiConfigHandle config, string name)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrEmpty(name))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Name cannot be null or empty.", nameof(name));
|
||||||
|
}
|
||||||
|
|
||||||
IntPtr trap;
|
IntPtr trap;
|
||||||
Handle = Interop.wasi_instance_new(store, config, out trap);
|
Handle = Interop.wasi_instance_new(store, name, config, out trap);
|
||||||
config.SetHandleAsInvalid();
|
config.SetHandleAsInvalid();
|
||||||
|
|
||||||
if (trap != IntPtr.Zero)
|
if (trap != IntPtr.Zero)
|
||||||
|
|||||||
@@ -12,10 +12,22 @@ namespace Wasmtime
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructs a new <see cref="WasiBuilder" />.
|
/// Constructs a new <see cref="WasiBuilder" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public WasiBuilder()
|
public WasiBuilder(string name)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrEmpty(name))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Name cannot be null or empty.", nameof(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the name of the WASI module being built.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The name of the WASI module.</value>
|
||||||
|
public string Name { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a command line argument to the builder.
|
/// Adds a command line argument to the builder.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -271,7 +283,7 @@ namespace Wasmtime
|
|||||||
SetStandardError(config);
|
SetStandardError(config);
|
||||||
SetPreopenDirectories(config);
|
SetPreopenDirectories(config);
|
||||||
|
|
||||||
return new Wasi(store.Handle, config);
|
return new Wasi(store.Handle, config, Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe void SetConfigArgs(Interop.WasiConfigHandle config)
|
private unsafe void SetConfigArgs(Interop.WasiConfigHandle config)
|
||||||
|
|||||||
66
crates/misc/dotnet/tests/Modules/WasiSnapshot0.wat
Normal file
66
crates/misc/dotnet/tests/Modules/WasiSnapshot0.wat
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
(module
|
||||||
|
(type $t0 (func (param i32 i32) (result i32)))
|
||||||
|
(type $t1 (func (param i32 i32 i32 i32) (result i32)))
|
||||||
|
(type $t2 (func (param i32) (result i32)))
|
||||||
|
(type $t3 (func (param i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i32)))
|
||||||
|
(import "wasi_unstable" "environ_sizes_get" (func $wasi_unstable.environ_sizes_get (type $t0)))
|
||||||
|
(import "wasi_unstable" "environ_get" (func $wasi_unstable.environ_get (type $t0)))
|
||||||
|
(import "wasi_unstable" "args_sizes_get" (func $wasi_unstable.args_sizes_get (type $t0)))
|
||||||
|
(import "wasi_unstable" "args_get" (func $wasi_unstable.args_get (type $t0)))
|
||||||
|
(import "wasi_unstable" "fd_write" (func $wasi_unstable.fd_write (type $t1)))
|
||||||
|
(import "wasi_unstable" "fd_read" (func $wasi_unstable.fd_read (type $t1)))
|
||||||
|
(import "wasi_unstable" "fd_close" (func $wasi_unstable.fd_close (type $t2)))
|
||||||
|
(import "wasi_unstable" "path_open" (func $wasi_unstable.path_open (type $t3)))
|
||||||
|
(memory $memory 1)
|
||||||
|
(export "memory" (memory 0))
|
||||||
|
(func $call_environ_sizes_get (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
|
||||||
|
local.get $p0
|
||||||
|
local.get $p1
|
||||||
|
call $wasi_unstable.environ_sizes_get)
|
||||||
|
(func $call_environ_get (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
|
||||||
|
local.get $p0
|
||||||
|
local.get $p1
|
||||||
|
call $wasi_unstable.environ_get)
|
||||||
|
(func $call_args_sizes_get (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
|
||||||
|
local.get $p0
|
||||||
|
local.get $p1
|
||||||
|
call $wasi_unstable.args_sizes_get)
|
||||||
|
(func $call_args_get (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
|
||||||
|
local.get $p0
|
||||||
|
local.get $p1
|
||||||
|
call $wasi_unstable.args_get)
|
||||||
|
(func $call_fd_write (type $t1) (param $p0 i32) (param $p1 i32) (param $p2 i32) (param $p3 i32) (result i32)
|
||||||
|
local.get $p0
|
||||||
|
local.get $p1
|
||||||
|
local.get $p2
|
||||||
|
local.get $p3
|
||||||
|
call $wasi_unstable.fd_write)
|
||||||
|
(func $call_fd_read (type $t1) (param $p0 i32) (param $p1 i32) (param $p2 i32) (param $p3 i32) (result i32)
|
||||||
|
local.get $p0
|
||||||
|
local.get $p1
|
||||||
|
local.get $p2
|
||||||
|
local.get $p3
|
||||||
|
call $wasi_unstable.fd_read)
|
||||||
|
(func $call_fd_close (type $t2) (param $p0 i32) (result i32)
|
||||||
|
local.get $p0
|
||||||
|
call $wasi_unstable.fd_close)
|
||||||
|
(func $call_path_open (type $t3) (param $p0 i32) (param $p1 i32) (param $p2 i32) (param $p3 i32) (param $p4 i32) (param $p5 i64) (param $p6 i64) (param $p7 i32) (param $p8 i32) (result i32)
|
||||||
|
local.get $p0
|
||||||
|
local.get $p1
|
||||||
|
local.get $p2
|
||||||
|
local.get $p3
|
||||||
|
local.get $p4
|
||||||
|
local.get $p5
|
||||||
|
local.get $p6
|
||||||
|
local.get $p7
|
||||||
|
local.get $p8
|
||||||
|
call $wasi_unstable.path_open)
|
||||||
|
(export "call_environ_sizes_get" (func $call_environ_sizes_get))
|
||||||
|
(export "call_environ_get" (func $call_environ_get))
|
||||||
|
(export "call_args_sizes_get" (func $call_args_sizes_get))
|
||||||
|
(export "call_args_get" (func $call_args_get))
|
||||||
|
(export "call_fd_write" (func $call_fd_write))
|
||||||
|
(export "call_fd_read" (func $call_fd_read))
|
||||||
|
(export "call_fd_close" (func $call_fd_close))
|
||||||
|
(export "call_path_open" (func $call_path_open))
|
||||||
|
)
|
||||||
246
crates/misc/dotnet/tests/WasiSnapshot0Tests.cs
Normal file
246
crates/misc/dotnet/tests/WasiSnapshot0Tests.cs
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.IO;
|
||||||
|
using FluentAssertions;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Wasmtime.Tests
|
||||||
|
{
|
||||||
|
public class WasiSnapshot0Fixture : ModuleFixture
|
||||||
|
{
|
||||||
|
protected override string ModuleFileName => "WasiSnapshot0.wat";
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WasiSnapshot0Tests : IClassFixture<WasiSnapshot0Fixture>
|
||||||
|
{
|
||||||
|
public WasiSnapshot0Tests(WasiSnapshot0Fixture fixture)
|
||||||
|
{
|
||||||
|
Fixture = fixture;
|
||||||
|
}
|
||||||
|
|
||||||
|
private WasiSnapshot0Fixture Fixture { get; set; }
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ItHasNoEnvironmentByDefault()
|
||||||
|
{
|
||||||
|
using var instance = Fixture.Module.Instantiate(new Wasi(Fixture.Module.Store, "wasi_unstable"));
|
||||||
|
dynamic inst = instance;
|
||||||
|
|
||||||
|
var memory = instance.Externs.Memories[0];
|
||||||
|
|
||||||
|
Assert.Equal(0, inst.call_environ_sizes_get(0, 4));
|
||||||
|
Assert.Equal(0, memory.ReadInt32(0));
|
||||||
|
Assert.Equal(0, memory.ReadInt32(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ItHasSpecifiedEnvironment()
|
||||||
|
{
|
||||||
|
var env = new Dictionary<string, string>() {
|
||||||
|
{"FOO", "BAR"},
|
||||||
|
{"WASM", "IS"},
|
||||||
|
{"VERY", "COOL"},
|
||||||
|
};
|
||||||
|
|
||||||
|
var wasi = new WasiBuilder("wasi_unstable")
|
||||||
|
.WithEnvironmentVariables(env.Select(kvp => (kvp.Key, kvp.Value)))
|
||||||
|
.Build(Fixture.Module.Store);
|
||||||
|
|
||||||
|
using var instance = Fixture.Module.Instantiate(wasi);
|
||||||
|
dynamic inst = instance;
|
||||||
|
|
||||||
|
var memory = instance.Externs.Memories[0];
|
||||||
|
|
||||||
|
Assert.Equal(0, inst.call_environ_sizes_get(0, 4));
|
||||||
|
Assert.Equal(env.Count, memory.ReadInt32(0));
|
||||||
|
Assert.Equal(env.Sum(kvp => kvp.Key.Length + kvp.Value.Length + 2), memory.ReadInt32(4));
|
||||||
|
Assert.Equal(0, inst.call_environ_get(0, 4 * env.Count));
|
||||||
|
|
||||||
|
for (int i = 0; i < env.Count; ++i)
|
||||||
|
{
|
||||||
|
var kvp = memory.ReadNullTerminatedString(memory.ReadInt32(i * 4)).Split("=");
|
||||||
|
Assert.Equal(env[kvp[0]], kvp[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ItInheritsEnvironment()
|
||||||
|
{
|
||||||
|
var wasi = new WasiBuilder("wasi_unstable")
|
||||||
|
.WithInheritedEnvironment()
|
||||||
|
.Build(Fixture.Module.Store);
|
||||||
|
|
||||||
|
using var instance = Fixture.Module.Instantiate(wasi);
|
||||||
|
dynamic inst = instance;
|
||||||
|
|
||||||
|
var memory = instance.Externs.Memories[0];
|
||||||
|
|
||||||
|
Assert.Equal(0, inst.call_environ_sizes_get(0, 4));
|
||||||
|
Assert.Equal(Environment.GetEnvironmentVariables().Keys.Count, memory.ReadInt32(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ItHasNoArgumentsByDefault()
|
||||||
|
{
|
||||||
|
using var instance = Fixture.Module.Instantiate(new Wasi(Fixture.Module.Store, "wasi_unstable"));
|
||||||
|
dynamic inst = instance;
|
||||||
|
|
||||||
|
var memory = instance.Externs.Memories[0];
|
||||||
|
|
||||||
|
Assert.Equal(0, inst.call_args_sizes_get(0, 4));
|
||||||
|
Assert.Equal(0, memory.ReadInt32(0));
|
||||||
|
Assert.Equal(0, memory.ReadInt32(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ItHasSpecifiedArguments()
|
||||||
|
{
|
||||||
|
var args = new List<string>() {
|
||||||
|
"WASM",
|
||||||
|
"IS",
|
||||||
|
"VERY",
|
||||||
|
"COOL"
|
||||||
|
};
|
||||||
|
|
||||||
|
var wasi = new WasiBuilder("wasi_unstable")
|
||||||
|
.WithArgs(args)
|
||||||
|
.Build(Fixture.Module.Store);
|
||||||
|
|
||||||
|
using var instance = Fixture.Module.Instantiate(wasi);
|
||||||
|
dynamic inst = instance;
|
||||||
|
|
||||||
|
var memory = instance.Externs.Memories[0];
|
||||||
|
|
||||||
|
Assert.Equal(0, inst.call_args_sizes_get(0, 4));
|
||||||
|
Assert.Equal(args.Count, memory.ReadInt32(0));
|
||||||
|
Assert.Equal(args.Sum(a => a.Length + 1), memory.ReadInt32(4));
|
||||||
|
Assert.Equal(0, inst.call_args_get(0, 4 * args.Count));
|
||||||
|
|
||||||
|
for (int i = 0; i < args.Count; ++i)
|
||||||
|
{
|
||||||
|
var arg = memory.ReadNullTerminatedString(memory.ReadInt32(i * 4));
|
||||||
|
Assert.Equal(args[i], arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ItInheritsArguments()
|
||||||
|
{
|
||||||
|
var wasi = new WasiBuilder("wasi_unstable")
|
||||||
|
.WithInheritedArgs()
|
||||||
|
.Build(Fixture.Module.Store);
|
||||||
|
|
||||||
|
using var instance = Fixture.Module.Instantiate(wasi);
|
||||||
|
dynamic inst = instance;
|
||||||
|
|
||||||
|
var memory = instance.Externs.Memories[0];
|
||||||
|
|
||||||
|
Assert.Equal(0, inst.call_args_sizes_get(0, 4));
|
||||||
|
Assert.Equal(Environment.GetCommandLineArgs().Length, memory.ReadInt32(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ItSetsStdIn()
|
||||||
|
{
|
||||||
|
const string MESSAGE = "WASM IS VERY COOL";
|
||||||
|
|
||||||
|
using var file = new TempFile();
|
||||||
|
File.WriteAllText(file.Path, MESSAGE);
|
||||||
|
|
||||||
|
var wasi = new WasiBuilder("wasi_unstable")
|
||||||
|
.WithStandardInput(file.Path)
|
||||||
|
.Build(Fixture.Module.Store);
|
||||||
|
|
||||||
|
using var instance = Fixture.Module.Instantiate(wasi);
|
||||||
|
dynamic inst = instance;
|
||||||
|
|
||||||
|
var memory = instance.Externs.Memories[0];
|
||||||
|
memory.WriteInt32(0, 8);
|
||||||
|
memory.WriteInt32(4, MESSAGE.Length);
|
||||||
|
|
||||||
|
Assert.Equal(0, inst.call_fd_read(0, 0, 1, 32));
|
||||||
|
Assert.Equal(MESSAGE.Length, memory.ReadInt32(32));
|
||||||
|
Assert.Equal(MESSAGE, memory.ReadString(8, MESSAGE.Length));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(1)]
|
||||||
|
[InlineData(2)]
|
||||||
|
public void ItSetsStdOutAndStdErr(int fd)
|
||||||
|
{
|
||||||
|
const string MESSAGE = "WASM IS VERY COOL";
|
||||||
|
|
||||||
|
using var file = new TempFile();
|
||||||
|
|
||||||
|
var builder = new WasiBuilder("wasi_unstable");
|
||||||
|
if (fd == 1)
|
||||||
|
{
|
||||||
|
builder.WithStandardOutput(file.Path);
|
||||||
|
}
|
||||||
|
else if (fd == 2)
|
||||||
|
{
|
||||||
|
builder.WithStandardError(file.Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
var wasi = builder.Build(Fixture.Module.Store);
|
||||||
|
|
||||||
|
using var instance = Fixture.Module.Instantiate(wasi);
|
||||||
|
dynamic inst = instance;
|
||||||
|
|
||||||
|
var memory = instance.Externs.Memories[0];
|
||||||
|
memory.WriteInt32(0, 8);
|
||||||
|
memory.WriteInt32(4, MESSAGE.Length);
|
||||||
|
memory.WriteString(8, MESSAGE);
|
||||||
|
|
||||||
|
Assert.Equal(0, inst.call_fd_write(fd, 0, 1, 32));
|
||||||
|
Assert.Equal(MESSAGE.Length, memory.ReadInt32(32));
|
||||||
|
Assert.Equal(0, inst.call_fd_close(fd));
|
||||||
|
Assert.Equal(MESSAGE, File.ReadAllText(file.Path));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ItSetsPreopenDirectories()
|
||||||
|
{
|
||||||
|
const string MESSAGE = "WASM IS VERY COOL";
|
||||||
|
|
||||||
|
using var file = new TempFile();
|
||||||
|
|
||||||
|
var wasi = new WasiBuilder("wasi_unstable")
|
||||||
|
.WithPreopenedDirectory(Path.GetDirectoryName(file.Path), "/foo")
|
||||||
|
.Build(Fixture.Module.Store);
|
||||||
|
|
||||||
|
using var instance = Fixture.Module.Instantiate(wasi);
|
||||||
|
dynamic inst = instance;
|
||||||
|
|
||||||
|
var memory = instance.Externs.Memories[0];
|
||||||
|
var fileName = Path.GetFileName(file.Path);
|
||||||
|
memory.WriteString(0, fileName);
|
||||||
|
|
||||||
|
Assert.Equal(0, inst.call_path_open(
|
||||||
|
3,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
fileName.Length,
|
||||||
|
0,
|
||||||
|
0x40 /* RIGHTS_FD_WRITE */,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
64
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
var fileFd = (int) memory.ReadInt32(64);
|
||||||
|
Assert.True(fileFd > 3);
|
||||||
|
|
||||||
|
memory.WriteInt32(0, 8);
|
||||||
|
memory.WriteInt32(4, MESSAGE.Length);
|
||||||
|
memory.WriteString(8, MESSAGE);
|
||||||
|
|
||||||
|
Assert.Equal(0, inst.call_fd_write(fileFd, 0, 1, 64));
|
||||||
|
Assert.Equal(MESSAGE.Length, memory.ReadInt32(64));
|
||||||
|
Assert.Equal(0, inst.call_fd_close(fileFd));
|
||||||
|
Assert.Equal(MESSAGE, File.ReadAllText(file.Path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ namespace Wasmtime.Tests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void ItHasNoEnvironmentByDefault()
|
public void ItHasNoEnvironmentByDefault()
|
||||||
{
|
{
|
||||||
using var instance = Fixture.Module.Instantiate(new Wasi(Fixture.Module.Store));
|
using var instance = Fixture.Module.Instantiate(new Wasi(Fixture.Module.Store, "wasi_snapshot_preview1"));
|
||||||
dynamic inst = instance;
|
dynamic inst = instance;
|
||||||
|
|
||||||
var memory = instance.Externs.Memories[0];
|
var memory = instance.Externs.Memories[0];
|
||||||
@@ -43,7 +43,7 @@ namespace Wasmtime.Tests
|
|||||||
{"VERY", "COOL"},
|
{"VERY", "COOL"},
|
||||||
};
|
};
|
||||||
|
|
||||||
var wasi = new WasiBuilder()
|
var wasi = new WasiBuilder("wasi_snapshot_preview1")
|
||||||
.WithEnvironmentVariables(env.Select(kvp => (kvp.Key, kvp.Value)))
|
.WithEnvironmentVariables(env.Select(kvp => (kvp.Key, kvp.Value)))
|
||||||
.Build(Fixture.Module.Store);
|
.Build(Fixture.Module.Store);
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ namespace Wasmtime.Tests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void ItInheritsEnvironment()
|
public void ItInheritsEnvironment()
|
||||||
{
|
{
|
||||||
var wasi = new WasiBuilder()
|
var wasi = new WasiBuilder("wasi_snapshot_preview1")
|
||||||
.WithInheritedEnvironment()
|
.WithInheritedEnvironment()
|
||||||
.Build(Fixture.Module.Store);
|
.Build(Fixture.Module.Store);
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ namespace Wasmtime.Tests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void ItHasNoArgumentsByDefault()
|
public void ItHasNoArgumentsByDefault()
|
||||||
{
|
{
|
||||||
using var instance = Fixture.Module.Instantiate(new Wasi(Fixture.Module.Store));
|
using var instance = Fixture.Module.Instantiate(new Wasi(Fixture.Module.Store, "wasi_snapshot_preview1"));
|
||||||
dynamic inst = instance;
|
dynamic inst = instance;
|
||||||
|
|
||||||
var memory = instance.Externs.Memories[0];
|
var memory = instance.Externs.Memories[0];
|
||||||
@@ -103,7 +103,7 @@ namespace Wasmtime.Tests
|
|||||||
"COOL"
|
"COOL"
|
||||||
};
|
};
|
||||||
|
|
||||||
var wasi = new WasiBuilder()
|
var wasi = new WasiBuilder("wasi_snapshot_preview1")
|
||||||
.WithArgs(args)
|
.WithArgs(args)
|
||||||
.Build(Fixture.Module.Store);
|
.Build(Fixture.Module.Store);
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ namespace Wasmtime.Tests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void ItInheritsArguments()
|
public void ItInheritsArguments()
|
||||||
{
|
{
|
||||||
var wasi = new WasiBuilder()
|
var wasi = new WasiBuilder("wasi_snapshot_preview1")
|
||||||
.WithInheritedArgs()
|
.WithInheritedArgs()
|
||||||
.Build(Fixture.Module.Store);
|
.Build(Fixture.Module.Store);
|
||||||
|
|
||||||
@@ -148,7 +148,7 @@ namespace Wasmtime.Tests
|
|||||||
using var file = new TempFile();
|
using var file = new TempFile();
|
||||||
File.WriteAllText(file.Path, MESSAGE);
|
File.WriteAllText(file.Path, MESSAGE);
|
||||||
|
|
||||||
var wasi = new WasiBuilder()
|
var wasi = new WasiBuilder("wasi_snapshot_preview1")
|
||||||
.WithStandardInput(file.Path)
|
.WithStandardInput(file.Path)
|
||||||
.Build(Fixture.Module.Store);
|
.Build(Fixture.Module.Store);
|
||||||
|
|
||||||
@@ -173,7 +173,7 @@ namespace Wasmtime.Tests
|
|||||||
|
|
||||||
using var file = new TempFile();
|
using var file = new TempFile();
|
||||||
|
|
||||||
var builder = new WasiBuilder();
|
var builder = new WasiBuilder("wasi_snapshot_preview1");
|
||||||
if (fd == 1)
|
if (fd == 1)
|
||||||
{
|
{
|
||||||
builder.WithStandardOutput(file.Path);
|
builder.WithStandardOutput(file.Path);
|
||||||
@@ -206,7 +206,7 @@ namespace Wasmtime.Tests
|
|||||||
|
|
||||||
using var file = new TempFile();
|
using var file = new TempFile();
|
||||||
|
|
||||||
var wasi = new WasiBuilder()
|
var wasi = new WasiBuilder("wasi_snapshot_preview1")
|
||||||
.WithPreopenedDirectory(Path.GetDirectoryName(file.Path), "/foo")
|
.WithPreopenedDirectory(Path.GetDirectoryName(file.Path), "/foo")
|
||||||
.Build(Fixture.Module.Store);
|
.Build(Fixture.Module.Store);
|
||||||
|
|
||||||
|
|||||||
@@ -101,46 +101,46 @@ impl PendingCString {
|
|||||||
|
|
||||||
/// A builder allowing customizable construction of `WasiCtx` instances.
|
/// A builder allowing customizable construction of `WasiCtx` instances.
|
||||||
pub struct WasiCtxBuilder {
|
pub struct WasiCtxBuilder {
|
||||||
stdin: PendingEntry,
|
stdin: Option<PendingEntry>,
|
||||||
stdout: PendingEntry,
|
stdout: Option<PendingEntry>,
|
||||||
stderr: PendingEntry,
|
stderr: Option<PendingEntry>,
|
||||||
preopens: Vec<(PathBuf, File)>,
|
preopens: Option<Vec<(PathBuf, File)>>,
|
||||||
args: Vec<PendingCString>,
|
args: Option<Vec<PendingCString>>,
|
||||||
env: HashMap<PendingCString, PendingCString>,
|
env: Option<HashMap<PendingCString, PendingCString>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WasiCtxBuilder {
|
impl WasiCtxBuilder {
|
||||||
/// Builder for a new `WasiCtx`.
|
/// Builder for a new `WasiCtx`.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let stdin = PendingEntry::Thunk(Entry::null);
|
|
||||||
let stdout = PendingEntry::Thunk(Entry::null);
|
|
||||||
let stderr = PendingEntry::Thunk(Entry::null);
|
|
||||||
Self {
|
Self {
|
||||||
stdin,
|
stdin: Some(PendingEntry::Thunk(Entry::null)),
|
||||||
stdout,
|
stdout: Some(PendingEntry::Thunk(Entry::null)),
|
||||||
stderr,
|
stderr: Some(PendingEntry::Thunk(Entry::null)),
|
||||||
preopens: Vec::new(),
|
preopens: Some(Vec::new()),
|
||||||
args: vec![],
|
args: Some(Vec::new()),
|
||||||
env: HashMap::new(),
|
env: Some(HashMap::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add arguments to the command-line arguments list.
|
/// Add arguments to the command-line arguments list.
|
||||||
///
|
///
|
||||||
/// Arguments must be valid UTF-8 with no NUL bytes, or else `WasiCtxBuilder::build()` will fail.
|
/// Arguments must be valid UTF-8 with no NUL bytes, or else `WasiCtxBuilder::build()` will fail.
|
||||||
pub fn args<S: AsRef<[u8]>>(mut self, args: impl IntoIterator<Item = S>) -> Self {
|
pub fn args<S: AsRef<[u8]>>(&mut self, args: impl IntoIterator<Item = S>) -> &mut Self {
|
||||||
self.args = args
|
self.args
|
||||||
.into_iter()
|
.as_mut()
|
||||||
.map(|arg| arg.as_ref().to_vec().into())
|
.unwrap()
|
||||||
.collect();
|
.extend(args.into_iter().map(|a| a.as_ref().to_vec().into()));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add an argument to the command-line arguments list.
|
/// Add an argument to the command-line arguments list.
|
||||||
///
|
///
|
||||||
/// Arguments must be valid UTF-8 with no NUL bytes, or else `WasiCtxBuilder::build()` will fail.
|
/// Arguments must be valid UTF-8 with no NUL bytes, or else `WasiCtxBuilder::build()` will fail.
|
||||||
pub fn arg<S: AsRef<[u8]>>(mut self, arg: S) -> Self {
|
pub fn arg<S: AsRef<[u8]>>(&mut self, arg: S) -> &mut Self {
|
||||||
self.args.push(arg.as_ref().to_vec().into());
|
self.args
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.push(arg.as_ref().to_vec().into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,16 +148,36 @@ impl WasiCtxBuilder {
|
|||||||
///
|
///
|
||||||
/// If any arguments from the host process contain invalid UTF-8, `WasiCtxBuilder::build()` will
|
/// If any arguments from the host process contain invalid UTF-8, `WasiCtxBuilder::build()` will
|
||||||
/// fail.
|
/// fail.
|
||||||
pub fn inherit_args(mut self) -> Self {
|
pub fn inherit_args(&mut self) -> &mut Self {
|
||||||
self.args = env::args_os().map(PendingCString::OsString).collect();
|
let args = self.args.as_mut().unwrap();
|
||||||
|
args.clear();
|
||||||
|
args.extend(env::args_os().map(PendingCString::OsString));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inherit stdin from the host process.
|
||||||
|
pub fn inherit_stdin(&mut self) -> &mut Self {
|
||||||
|
self.stdin = Some(PendingEntry::Thunk(Entry::duplicate_stdin));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inherit stdout from the host process.
|
||||||
|
pub fn inherit_stdout(&mut self) -> &mut Self {
|
||||||
|
self.stdout = Some(PendingEntry::Thunk(Entry::duplicate_stdout));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inherit stdout from the host process.
|
||||||
|
pub fn inherit_stderr(&mut self) -> &mut Self {
|
||||||
|
self.stderr = Some(PendingEntry::Thunk(Entry::duplicate_stderr));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inherit the stdin, stdout, and stderr streams from the host process.
|
/// Inherit the stdin, stdout, and stderr streams from the host process.
|
||||||
pub fn inherit_stdio(mut self) -> Self {
|
pub fn inherit_stdio(&mut self) -> &mut Self {
|
||||||
self.stdin = PendingEntry::Thunk(Entry::duplicate_stdin);
|
self.stdin = Some(PendingEntry::Thunk(Entry::duplicate_stdin));
|
||||||
self.stdout = PendingEntry::Thunk(Entry::duplicate_stdout);
|
self.stdout = Some(PendingEntry::Thunk(Entry::duplicate_stdout));
|
||||||
self.stderr = PendingEntry::Thunk(Entry::duplicate_stderr);
|
self.stderr = Some(PendingEntry::Thunk(Entry::duplicate_stderr));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,10 +185,10 @@ impl WasiCtxBuilder {
|
|||||||
///
|
///
|
||||||
/// If any environment variables from the host process contain invalid Unicode (UTF-16 for
|
/// If any environment variables from the host process contain invalid Unicode (UTF-16 for
|
||||||
/// Windows, UTF-8 for other platforms), `WasiCtxBuilder::build()` will fail.
|
/// Windows, UTF-8 for other platforms), `WasiCtxBuilder::build()` will fail.
|
||||||
pub fn inherit_env(mut self) -> Self {
|
pub fn inherit_env(&mut self) -> &mut Self {
|
||||||
self.env = std::env::vars_os()
|
let env = self.env.as_mut().unwrap();
|
||||||
.map(|(k, v)| (k.into(), v.into()))
|
env.clear();
|
||||||
.collect();
|
env.extend(std::env::vars_os().map(|(k, v)| (k.into(), v.into())));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,8 +196,10 @@ impl WasiCtxBuilder {
|
|||||||
///
|
///
|
||||||
/// Environment variable keys and values must be valid UTF-8 with no NUL bytes, or else
|
/// Environment variable keys and values must be valid UTF-8 with no NUL bytes, or else
|
||||||
/// `WasiCtxBuilder::build()` will fail.
|
/// `WasiCtxBuilder::build()` will fail.
|
||||||
pub fn env<S: AsRef<[u8]>>(mut self, k: S, v: S) -> Self {
|
pub fn env<S: AsRef<[u8]>>(&mut self, k: S, v: S) -> &mut Self {
|
||||||
self.env
|
self.env
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
.insert(k.as_ref().to_vec().into(), v.as_ref().to_vec().into());
|
.insert(k.as_ref().to_vec().into(), v.as_ref().to_vec().into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -187,40 +209,40 @@ impl WasiCtxBuilder {
|
|||||||
/// Environment variable keys and values must be valid UTF-8 with no NUL bytes, or else
|
/// Environment variable keys and values must be valid UTF-8 with no NUL bytes, or else
|
||||||
/// `WasiCtxBuilder::build()` will fail.
|
/// `WasiCtxBuilder::build()` will fail.
|
||||||
pub fn envs<S: AsRef<[u8]>, T: Borrow<(S, S)>>(
|
pub fn envs<S: AsRef<[u8]>, T: Borrow<(S, S)>>(
|
||||||
mut self,
|
&mut self,
|
||||||
envs: impl IntoIterator<Item = T>,
|
envs: impl IntoIterator<Item = T>,
|
||||||
) -> Self {
|
) -> &mut Self {
|
||||||
self.env = envs
|
self.env.as_mut().unwrap().extend(envs.into_iter().map(|t| {
|
||||||
.into_iter()
|
let (k, v) = t.borrow();
|
||||||
.map(|t| {
|
(k.as_ref().to_vec().into(), v.as_ref().to_vec().into())
|
||||||
let (k, v) = t.borrow();
|
}));
|
||||||
(k.as_ref().to_vec().into(), v.as_ref().to_vec().into())
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provide a File to use as stdin
|
/// Provide a File to use as stdin
|
||||||
pub fn stdin(mut self, file: File) -> Self {
|
pub fn stdin(&mut self, file: File) -> &mut Self {
|
||||||
self.stdin = PendingEntry::File(file);
|
self.stdin = Some(PendingEntry::File(file));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provide a File to use as stdout
|
/// Provide a File to use as stdout
|
||||||
pub fn stdout(mut self, file: File) -> Self {
|
pub fn stdout(&mut self, file: File) -> &mut Self {
|
||||||
self.stdout = PendingEntry::File(file);
|
self.stdout = Some(PendingEntry::File(file));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provide a File to use as stderr
|
/// Provide a File to use as stderr
|
||||||
pub fn stderr(mut self, file: File) -> Self {
|
pub fn stderr(&mut self, file: File) -> &mut Self {
|
||||||
self.stderr = PendingEntry::File(file);
|
self.stderr = Some(PendingEntry::File(file));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a preopened directory.
|
/// Add a preopened directory.
|
||||||
pub fn preopened_dir<P: AsRef<Path>>(mut self, dir: File, guest_path: P) -> Self {
|
pub fn preopened_dir<P: AsRef<Path>>(&mut self, dir: File, guest_path: P) -> &mut Self {
|
||||||
self.preopens.push((guest_path.as_ref().to_owned(), dir));
|
self.preopens
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.push((guest_path.as_ref().to_owned(), dir));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,17 +250,21 @@ impl WasiCtxBuilder {
|
|||||||
///
|
///
|
||||||
/// If any of the arguments or environment variables in this builder cannot be converted into
|
/// If any of the arguments or environment variables in this builder cannot be converted into
|
||||||
/// `CString`s, either due to NUL bytes or Unicode conversions, this returns an error.
|
/// `CString`s, either due to NUL bytes or Unicode conversions, this returns an error.
|
||||||
pub fn build(self) -> WasiCtxBuilderResult<WasiCtx> {
|
pub fn build(&mut self) -> WasiCtxBuilderResult<WasiCtx> {
|
||||||
// Process arguments and environment variables into `CString`s, failing quickly if they
|
// Process arguments and environment variables into `CString`s, failing quickly if they
|
||||||
// contain any NUL bytes, or if conversion from `OsString` fails.
|
// contain any NUL bytes, or if conversion from `OsString` fails.
|
||||||
let args = self
|
let args = self
|
||||||
.args
|
.args
|
||||||
|
.take()
|
||||||
|
.unwrap()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|arg| arg.into_utf8_cstring())
|
.map(|arg| arg.into_utf8_cstring())
|
||||||
.collect::<WasiCtxBuilderResult<Vec<CString>>>()?;
|
.collect::<WasiCtxBuilderResult<Vec<CString>>>()?;
|
||||||
|
|
||||||
let env = self
|
let env = self
|
||||||
.env
|
.env
|
||||||
|
.take()
|
||||||
|
.unwrap()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(k, v)| {
|
.map(|(k, v)| {
|
||||||
k.into_string().and_then(|mut pair| {
|
k.into_string().and_then(|mut pair| {
|
||||||
@@ -257,12 +283,12 @@ impl WasiCtxBuilder {
|
|||||||
let mut fd_pool = FdPool::new();
|
let mut fd_pool = FdPool::new();
|
||||||
let mut entries: HashMap<wasi::__wasi_fd_t, Entry> = HashMap::new();
|
let mut entries: HashMap<wasi::__wasi_fd_t, Entry> = HashMap::new();
|
||||||
// Populate the non-preopen fds.
|
// Populate the non-preopen fds.
|
||||||
for pending in vec![self.stdin, self.stdout, self.stderr] {
|
for pending in &mut [&mut self.stdin, &mut self.stdout, &mut self.stderr] {
|
||||||
let fd = fd_pool
|
let fd = fd_pool
|
||||||
.allocate()
|
.allocate()
|
||||||
.ok_or(WasiCtxBuilderError::TooManyFilesOpen)?;
|
.ok_or(WasiCtxBuilderError::TooManyFilesOpen)?;
|
||||||
log::debug!("WasiCtx inserting ({:?}, {:?})", fd, pending);
|
log::debug!("WasiCtx inserting ({:?}, {:?})", fd, pending);
|
||||||
match pending {
|
match pending.take().unwrap() {
|
||||||
PendingEntry::Thunk(f) => {
|
PendingEntry::Thunk(f) => {
|
||||||
entries.insert(fd, f()?);
|
entries.insert(fd, f()?);
|
||||||
}
|
}
|
||||||
@@ -272,7 +298,7 @@ impl WasiCtxBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Then add the preopen fds.
|
// Then add the preopen fds.
|
||||||
for (guest_path, dir) in self.preopens {
|
for (guest_path, dir) in self.preopens.take().unwrap() {
|
||||||
// We do the increment at the beginning of the loop body, so that we don't overflow
|
// We do the increment at the beginning of the loop body, so that we don't overflow
|
||||||
// unnecessarily if we have exactly the maximum number of file descriptors.
|
// unnecessarily if we have exactly the maximum number of file descriptors.
|
||||||
let preopen_fd = fd_pool
|
let preopen_fd = fd_pool
|
||||||
|
|||||||
@@ -33,14 +33,19 @@ pub fn define_struct(args: TokenStream) -> TokenStream {
|
|||||||
let mut get_exports = Vec::new();
|
let mut get_exports = Vec::new();
|
||||||
let mut ctor_externs = Vec::new();
|
let mut ctor_externs = Vec::new();
|
||||||
let mut ctor_fields = Vec::new();
|
let mut ctor_fields = Vec::new();
|
||||||
|
let mut linker_add = Vec::new();
|
||||||
|
|
||||||
for module in doc.modules() {
|
for module in doc.modules() {
|
||||||
|
let module_name = module.name.as_str();
|
||||||
for func in module.funcs() {
|
for func in module.funcs() {
|
||||||
let name = func.name.as_str();
|
let name = func.name.as_str();
|
||||||
let name_ident = Ident::new(func.name.as_str(), Span::call_site());
|
let name_ident = Ident::new(name, Span::call_site());
|
||||||
fields.push(quote! { pub #name_ident: wasmtime::Func });
|
fields.push(quote! { pub #name_ident: wasmtime::Func });
|
||||||
get_exports.push(quote! { #name => Some(&self.#name_ident) });
|
get_exports.push(quote! { #name => Some(&self.#name_ident) });
|
||||||
ctor_fields.push(name_ident.clone());
|
ctor_fields.push(name_ident.clone());
|
||||||
|
linker_add.push(quote! {
|
||||||
|
linker.define(#module_name, #name, self.#name_ident.clone())?;
|
||||||
|
});
|
||||||
|
|
||||||
let mut shim_arg_decls = Vec::new();
|
let mut shim_arg_decls = Vec::new();
|
||||||
let mut params = Vec::new();
|
let mut params = Vec::new();
|
||||||
@@ -249,6 +254,12 @@ pub fn define_struct(args: TokenStream) -> TokenStream {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds all wasi items to the specified `Linker`.
|
||||||
|
pub fn add_to_linker(&self, linker: &mut wasmtime::Linker) -> anyhow::Result<()> {
|
||||||
|
#(#linker_add)*
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -270,10 +281,10 @@ pub fn define_struct_for_wiggle(args: TokenStream) -> TokenStream {
|
|||||||
|
|
||||||
for module in doc.modules() {
|
for module in doc.modules() {
|
||||||
let module_name = module.name.as_str();
|
let module_name = module.name.as_str();
|
||||||
let module_id = Ident::new(module.name.as_str(), Span::call_site());
|
let module_id = Ident::new(module_name, Span::call_site());
|
||||||
for func in module.funcs() {
|
for func in module.funcs() {
|
||||||
let name = func.name.as_str();
|
let name = func.name.as_str();
|
||||||
let name_ident = Ident::new(func.name.as_str(), Span::call_site());
|
let name_ident = Ident::new(name, Span::call_site());
|
||||||
fields.push(quote! { pub #name_ident: wasmtime::Func });
|
fields.push(quote! { pub #name_ident: wasmtime::Func });
|
||||||
get_exports.push(quote! { #name => Some(&self.#name_ident) });
|
get_exports.push(quote! { #name => Some(&self.#name_ident) });
|
||||||
ctor_fields.push(name_ident.clone());
|
ctor_fields.push(name_ident.clone());
|
||||||
|
|||||||
@@ -336,13 +336,12 @@ impl ModuleRegistry {
|
|||||||
|
|
||||||
let cx1 = cx1.build()?;
|
let cx1 = cx1.build()?;
|
||||||
|
|
||||||
let mut cx2 = wasi_common::old::snapshot_0::WasiCtxBuilder::new()
|
let mut cx2 = wasi_common::old::snapshot_0::WasiCtxBuilder::new();
|
||||||
.inherit_stdio()
|
|
||||||
.args(argv)
|
cx2.inherit_stdio().args(argv).envs(vars);
|
||||||
.envs(vars);
|
|
||||||
|
|
||||||
for (name, file) in preopen_dirs {
|
for (name, file) in preopen_dirs {
|
||||||
cx2 = cx2.preopened_dir(file.try_clone()?, name);
|
cx2.preopened_dir(file.try_clone()?, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
let cx2 = cx2.build()?;
|
let cx2 = cx2.build()?;
|
||||||
|
|||||||
Reference in New Issue
Block a user