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(
|
||||
wasm_store_t* store,
|
||||
const char* name,
|
||||
own wasi_config_t* config,
|
||||
own wasm_trap_t** trap
|
||||
);
|
||||
|
||||
@@ -185,7 +185,7 @@ pub unsafe extern "C" fn wasmtime_linker_define_wasi(
|
||||
instance: *const wasi_instance_t,
|
||||
) -> bool {
|
||||
let linker = &mut (*linker).linker;
|
||||
(*instance).wasi.add_to_linker(linker).is_ok()
|
||||
(*instance).add_to_linker(linker).is_ok()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
//! The WASI embedding API definitions for Wasmtime.
|
||||
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::ffi::CStr;
|
||||
use std::fs::File;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::slice;
|
||||
use wasi_common::{preopen_dir, WasiCtxBuilder};
|
||||
use wasmtime::{HostRef, Trap};
|
||||
use wasmtime_wasi::Wasi;
|
||||
use wasi_common::{
|
||||
old::snapshot_0::WasiCtxBuilder as WasiSnapshot0CtxBuilder, preopen_dir,
|
||||
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> {
|
||||
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()
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct wasi_config_t {
|
||||
builder: WasiCtxBuilder,
|
||||
pub enum WasiModule {
|
||||
Snapshot0(WasiSnapshot0),
|
||||
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]
|
||||
pub unsafe extern "C" fn wasi_config_new() -> *mut wasi_config_t {
|
||||
Box::into_raw(Box::new(wasi_config_t {
|
||||
builder: WasiCtxBuilder::new(),
|
||||
}))
|
||||
Box::into_raw(Box::new(wasi_config_t::default()))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -47,16 +65,17 @@ pub unsafe extern "C" fn wasi_config_set_argv(
|
||||
argc: c_int,
|
||||
argv: *const *const c_char,
|
||||
) {
|
||||
(*config).builder.args(
|
||||
slice::from_raw_parts(argv, argc as usize)
|
||||
.iter()
|
||||
.map(|a| slice::from_raw_parts(*a as *const u8, CStr::from_ptr(*a).to_bytes().len())),
|
||||
);
|
||||
(*config).args = slice::from_raw_parts(argv, argc as usize)
|
||||
.iter()
|
||||
.map(|p| CStr::from_ptr(*p).to_bytes().to_owned())
|
||||
.collect();
|
||||
(*config).inherit_args = false;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
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]
|
||||
@@ -69,17 +88,22 @@ pub unsafe extern "C" fn wasi_config_set_env(
|
||||
let names = slice::from_raw_parts(names, envc as usize);
|
||||
let values = slice::from_raw_parts(values, envc as usize);
|
||||
|
||||
(*config).builder.envs(
|
||||
names
|
||||
.iter()
|
||||
.map(|p| CStr::from_ptr(*p).to_bytes())
|
||||
.zip(values.iter().map(|p| CStr::from_ptr(*p).to_bytes())),
|
||||
);
|
||||
(*config).env = names
|
||||
.iter()
|
||||
.map(|p| CStr::from_ptr(*p).to_bytes().to_owned())
|
||||
.zip(
|
||||
values
|
||||
.iter()
|
||||
.map(|p| CStr::from_ptr(*p).to_bytes().to_owned()),
|
||||
)
|
||||
.collect();
|
||||
(*config).inherit_env = false;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
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]
|
||||
@@ -92,14 +116,16 @@ pub unsafe extern "C" fn wasi_config_set_stdin_file(
|
||||
None => return false,
|
||||
};
|
||||
|
||||
(*config).builder.stdin(file);
|
||||
(*config).stdin = Some(file);
|
||||
(*config).inherit_stdin = false;
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
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]
|
||||
@@ -112,14 +138,16 @@ pub unsafe extern "C" fn wasi_config_set_stdout_file(
|
||||
None => return false,
|
||||
};
|
||||
|
||||
(*config).builder.stdout(file);
|
||||
(*config).stdout = Some(file);
|
||||
(*config).inherit_stdout = false;
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
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]
|
||||
@@ -132,14 +160,16 @@ pub unsafe extern "C" fn wasi_config_set_stderr_file(
|
||||
None => return false,
|
||||
};
|
||||
|
||||
(*config).builder.stderr(file);
|
||||
(*config).stderr = Some(file);
|
||||
(*config).inherit_stderr = false;
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
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]
|
||||
@@ -161,29 +191,110 @@ pub unsafe extern "C" fn wasi_config_preopen_dir(
|
||||
None => return false,
|
||||
};
|
||||
|
||||
(*config).builder.preopened_dir(dir, guest_path);
|
||||
(*config).preopens.push((dir, guest_path.to_owned()));
|
||||
|
||||
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)]
|
||||
pub struct wasi_instance_t {
|
||||
pub wasi: Wasi,
|
||||
wasi: WasiInstance,
|
||||
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]
|
||||
pub unsafe extern "C" fn wasi_instance_new(
|
||||
store: *mut wasm_store_t,
|
||||
name: *const c_char,
|
||||
config: *mut wasi_config_t,
|
||||
trap: *mut *mut wasm_trap_t,
|
||||
) -> *mut wasi_instance_t {
|
||||
let store = &(*store).store.borrow();
|
||||
let mut config = Box::from_raw(config);
|
||||
let config = Box::from_raw(config);
|
||||
|
||||
match config.builder.build() {
|
||||
Ok(ctx) => Box::into_raw(Box::new(wasi_instance_t {
|
||||
wasi: Wasi::new(store, ctx),
|
||||
let result = match CStr::from_ptr(name).to_str().unwrap_or("") {
|
||||
"wasi_snapshot_preview1" => create_preview1_instance(store, *config),
|
||||
"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(),
|
||||
})),
|
||||
Err(e) => {
|
||||
@@ -206,20 +317,32 @@ pub unsafe extern "C" fn wasi_instance_bind_import(
|
||||
instance: *mut wasi_instance_t,
|
||||
import: *const wasm_importtype_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)
|
||||
let func_type = match (*import).ty.ty() {
|
||||
ExternType::Func(f) => f,
|
||||
_ => return std::ptr::null_mut(),
|
||||
};
|
||||
|
||||
let module = (*import).ty.module();
|
||||
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) => {
|
||||
if export.ty() != func_type {
|
||||
return std::ptr::null_mut();
|
||||
|
||||
Reference in New Issue
Block a user