Skip to content

Commit

Permalink
Merge pull request bytecodealliance#977 from peterhuene/wasi-c-api
Browse files Browse the repository at this point in the history
Implement a WASI instantiation C API.
  • Loading branch information
peterhuene committed Feb 26, 2020
2 parents 150a3e5 + c92bf4d commit 78b32e2
Show file tree
Hide file tree
Showing 39 changed files with 1,979 additions and 631 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ rusty-tags.*
*~
\#*\#
docs/book
.vscode/
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/api/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ fn from_wasmtime_abiparam(param: &ir::AbiParam) -> Option<ValType> {
/// A descriptor for a function in a WebAssembly module.
///
/// WebAssembly functions can have 0 or more parameters and results.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub struct FuncType {
params: Box<[ValType]>,
results: Box<[ValType]>,
Expand Down
2 changes: 2 additions & 0 deletions crates/c-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ doctest = false

[dependencies]
wasmtime = { path = "../api" }
wasi-common = { path = "../wasi-common" }
wasmtime-wasi = { path = "../wasi" }
70 changes: 70 additions & 0 deletions crates/c-api/include/wasi.h
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
12 changes: 7 additions & 5 deletions crates/c-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ use wasmtime::{
Table, TableType, Trap, Val, ValType,
};

mod ext;
mod wasi;

pub use crate::ext::*;
pub use crate::wasi::*;

macro_rules! declare_vec {
($name:ident, $elem_ty:path) => {
#[repr(C)]
Expand Down Expand Up @@ -1025,7 +1031,7 @@ pub unsafe extern "C" fn wasm_trap_new(
if message[message.len() - 1] != 0 {
panic!("wasm_trap_new message stringz expected");
}
let message = String::from_utf8_lossy(message);
let message = String::from_utf8_lossy(&message[..message.len() - 1]);
let trap = Box::new(wasm_trap_t {
trap: HostRef::new(Trap::new(message)),
});
Expand Down Expand Up @@ -1777,7 +1783,3 @@ pub unsafe extern "C" fn wasm_valtype_vec_copy(
let slice = slice::from_raw_parts((*src).data, (*src).size);
(*out).set_from_slice(slice);
}

mod ext;

pub use crate::ext::*;
239 changes: 239 additions & 0 deletions crates/c-api/src/wasi.rs
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(),
}
}
Loading

0 comments on commit 78b32e2

Please sign in to comment.