-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #977 from peterhuene/wasi-c-api
Implement a WASI instantiation C API.
- Loading branch information
Showing
39 changed files
with
1,979 additions
and
631 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,3 +10,4 @@ rusty-tags.* | |
*~ | ||
\#*\# | ||
docs/book | ||
.vscode/ |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// WASI C API | ||
|
||
#ifndef WASI_H | ||
#define WASI_H | ||
|
||
#include "wasm.h" | ||
|
||
#ifndef WASI_API_EXTERN | ||
#ifdef _WIN32 | ||
#define WASI_API_EXTERN __declspec(dllimport) | ||
#else | ||
#define WASI_API_EXTERN | ||
#endif | ||
#endif | ||
|
||
#ifdef __cplusplus | ||
extern "C" { | ||
#endif | ||
|
||
#define own | ||
|
||
#define WASI_DECLARE_OWN(name) \ | ||
typedef struct wasi_##name##_t wasi_##name##_t; \ | ||
WASI_API_EXTERN void wasi_##name##_delete(own wasi_##name##_t*); | ||
|
||
// WASI config | ||
|
||
WASI_DECLARE_OWN(config) | ||
|
||
WASI_API_EXTERN own wasi_config_t* wasi_config_new(); | ||
|
||
WASI_API_EXTERN void wasi_config_set_argv(wasi_config_t* config, int argc, const char* argv[]); | ||
WASI_API_EXTERN void wasi_config_inherit_argv(wasi_config_t* config); | ||
|
||
WASI_API_EXTERN void wasi_config_set_env(wasi_config_t* config, int envc, const char* names[], const char* values[]); | ||
WASI_API_EXTERN void wasi_config_inherit_env(wasi_config_t* config); | ||
|
||
WASI_API_EXTERN bool wasi_config_set_stdin_file(wasi_config_t* config, const char* path); | ||
WASI_API_EXTERN void wasi_config_inherit_stdin(wasi_config_t* config); | ||
|
||
WASI_API_EXTERN bool wasi_config_set_stdout_file(wasi_config_t* config, const char* path); | ||
WASI_API_EXTERN void wasi_config_inherit_stdout(wasi_config_t* config); | ||
|
||
WASI_API_EXTERN bool wasi_config_set_stderr_file(wasi_config_t* config, const char* path); | ||
WASI_API_EXTERN void wasi_config_inherit_stderr(wasi_config_t* config); | ||
|
||
WASI_API_EXTERN bool wasi_config_preopen_dir(wasi_config_t* config, const char* path, const char* guest_path); | ||
|
||
// WASI instance | ||
|
||
WASI_DECLARE_OWN(instance) | ||
|
||
WASI_API_EXTERN own wasi_instance_t* wasi_instance_new( | ||
wasm_store_t* store, | ||
own wasi_config_t* config, | ||
own wasm_trap_t** trap | ||
); | ||
|
||
WASI_API_EXTERN const wasm_extern_t* wasi_instance_bind_import( | ||
const wasi_instance_t* instance, | ||
const wasm_importtype_t* import | ||
); | ||
|
||
#undef own | ||
|
||
#ifdef __cplusplus | ||
} // extern "C" | ||
#endif | ||
|
||
#endif // #ifdef WASI_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
//! The WASI embedding API definitions for Wasmtime. | ||
use crate::{wasm_extern_t, wasm_importtype_t, wasm_store_t, wasm_trap_t, ExternHost, ExternType}; | ||
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::slice; | ||
use wasi_common::{preopen_dir, WasiCtxBuilder}; | ||
use wasmtime::{HostRef, Trap}; | ||
use wasmtime_wasi::Wasi; | ||
|
||
unsafe fn cstr_to_path<'a>(path: *const c_char) -> Option<&'a Path> { | ||
CStr::from_ptr(path).to_str().map(Path::new).ok() | ||
} | ||
|
||
unsafe fn open_file(path: *const c_char) -> Option<File> { | ||
File::open(cstr_to_path(path)?).ok() | ||
} | ||
|
||
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, | ||
} | ||
|
||
impl wasi_config_t {} | ||
|
||
#[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(), | ||
})) | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn wasi_config_delete(config: *mut wasi_config_t) { | ||
drop(Box::from_raw(config)); | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn wasi_config_set_argv( | ||
config: *mut wasi_config_t, | ||
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())), | ||
); | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn wasi_config_inherit_argv(config: *mut wasi_config_t) { | ||
(*config).builder.inherit_args(); | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn wasi_config_set_env( | ||
config: *mut wasi_config_t, | ||
envc: c_int, | ||
names: *const *const c_char, | ||
values: *const *const c_char, | ||
) { | ||
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())), | ||
); | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn wasi_config_inherit_env(config: *mut wasi_config_t) { | ||
(*config).builder.inherit_env(); | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn wasi_config_set_stdin_file( | ||
config: *mut wasi_config_t, | ||
path: *const c_char, | ||
) -> bool { | ||
let file = match open_file(path) { | ||
Some(f) => f, | ||
None => return false, | ||
}; | ||
|
||
(*config).builder.stdin(file); | ||
|
||
true | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn wasi_config_inherit_stdin(config: *mut wasi_config_t) { | ||
(*config).builder.inherit_stdin(); | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn wasi_config_set_stdout_file( | ||
config: *mut wasi_config_t, | ||
path: *const c_char, | ||
) -> bool { | ||
let file = match create_file(path) { | ||
Some(f) => f, | ||
None => return false, | ||
}; | ||
|
||
(*config).builder.stdout(file); | ||
|
||
true | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn wasi_config_inherit_stdout(config: *mut wasi_config_t) { | ||
(*config).builder.inherit_stdout(); | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn wasi_config_set_stderr_file( | ||
config: *mut wasi_config_t, | ||
path: *const c_char, | ||
) -> bool { | ||
let file = match create_file(path) { | ||
Some(f) => f, | ||
None => return false, | ||
}; | ||
|
||
(*config).builder.stderr(file); | ||
|
||
true | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn wasi_config_inherit_stderr(config: *mut wasi_config_t) { | ||
(*config).builder.inherit_stderr(); | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn wasi_config_preopen_dir( | ||
config: *mut wasi_config_t, | ||
path: *const c_char, | ||
guest_path: *const c_char, | ||
) -> bool { | ||
let guest_path = match cstr_to_path(guest_path) { | ||
Some(p) => p, | ||
None => return false, | ||
}; | ||
|
||
let dir = match cstr_to_path(path) { | ||
Some(p) => match preopen_dir(p) { | ||
Ok(d) => d, | ||
Err(_) => return false, | ||
}, | ||
None => return false, | ||
}; | ||
|
||
(*config).builder.preopened_dir(dir, guest_path); | ||
|
||
true | ||
} | ||
|
||
#[repr(C)] | ||
pub struct wasi_instance_t { | ||
wasi: Wasi, | ||
export_cache: HashMap<String, Box<wasm_extern_t>>, | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn wasi_instance_new( | ||
store: *mut wasm_store_t, | ||
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); | ||
|
||
match config.builder.build() { | ||
Ok(ctx) => Box::into_raw(Box::new(wasi_instance_t { | ||
wasi: Wasi::new(store, ctx), | ||
export_cache: HashMap::new(), | ||
})), | ||
Err(e) => { | ||
(*trap) = Box::into_raw(Box::new(wasm_trap_t { | ||
trap: HostRef::new(Trap::new(e.to_string())), | ||
})); | ||
|
||
std::ptr::null_mut() | ||
} | ||
} | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn wasi_instance_delete(instance: *mut wasi_instance_t) { | ||
drop(Box::from_raw(instance)); | ||
} | ||
|
||
#[no_mangle] | ||
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 name = (*import).ty.name(); | ||
|
||
match (*instance).wasi.get_export(name) { | ||
Some(export) => { | ||
if export.ty() != func_type { | ||
return std::ptr::null_mut(); | ||
} | ||
|
||
&**(*instance) | ||
.export_cache | ||
.entry(name.to_string()) | ||
.or_insert_with(|| { | ||
Box::new(wasm_extern_t { | ||
which: ExternHost::Func(HostRef::new(export.clone())), | ||
}) | ||
}) as *const wasm_extern_t | ||
} | ||
None => std::ptr::null_mut(), | ||
} | ||
} |
Oops, something went wrong.