-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Implement a WASI instantiation C API. #977
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ae0b409
11dec4d
8dff0fc
78ee043
a6ec8f8
4e1d2a2
104919e
0706683
fa65a14
c92bf4d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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.
| 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 |
| 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 { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW for new APIs we may want to explore updating how we write bindings. For example we could make all functions safe ideally, and this function could, for example, return Another example would be that
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would be interesting to do. I don't like the unsafety of this layer either and while it needs to exist at some level to interact with C, perhaps we could make it much more difficult for us to introduce leaks and use-after-frees by generating the unsafe boilerplate. |
||
| 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) | ||
|
peterhuene marked this conversation as resolved.
|
||
| .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(), | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.